mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-29 12:34:21 +02:00
[matter] Map contact sensor channels correctly (#20788)
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
+3
@@ -235,6 +235,9 @@ public class MatterBindingConstants {
|
||||
public static final String CHANNEL_ID_BOOLEANSTATE_STATEVALUE = "booleanstate-statevalue";
|
||||
public static final ChannelTypeUID CHANNEL_BOOLEANSTATE_STATEVALUE = new ChannelTypeUID(BINDING_ID,
|
||||
CHANNEL_ID_BOOLEANSTATE_STATEVALUE);
|
||||
public static final String CHANNEL_ID_CONTACT_STATEVALUE = "contact-statevalue";
|
||||
public static final ChannelTypeUID CHANNEL_CONTACT_STATEVALUE = new ChannelTypeUID(BINDING_ID,
|
||||
CHANNEL_ID_CONTACT_STATEVALUE);
|
||||
public static final String CHANNEL_ID_OTASOFTWAREUPDATEREQUESTOR_UPDATEAVAILABLE = "otasoftwareupdaterequestor-updateavailable";
|
||||
public static final ChannelTypeUID CHANNEL_OTASOFTWAREUPDATEREQUESTOR_UPDATEAVAILABLE = new ChannelTypeUID(
|
||||
BINDING_ID, CHANNEL_ID_OTASOFTWAREUPDATEREQUESTOR_UPDATEAVAILABLE);
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.matter.internal.controller.devices.converter;
|
||||
|
||||
import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_CONTACT_STATEVALUE;
|
||||
import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_ID_CONTACT_STATEVALUE;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.matter.internal.client.dto.cluster.gen.BooleanStateCluster;
|
||||
import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage;
|
||||
import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler;
|
||||
import org.openhab.core.library.CoreItemFactory;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelGroupUID;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.types.StateDescription;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* A converter for mapping {@link BooleanStateCluster} to Contact semantics.
|
||||
*
|
||||
* Matter contact sensors define TRUE as closed/contact and FALSE as open/no contact.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ContactStateConverter extends GenericConverter<BooleanStateCluster> {
|
||||
|
||||
public ContactStateConverter(BooleanStateCluster cluster, MatterBaseThingHandler handler, int endpointNumber,
|
||||
String labelPrefix) {
|
||||
super(cluster, handler, endpointNumber, labelPrefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Channel, @Nullable StateDescription> createChannels(ChannelGroupUID channelGroupUID) {
|
||||
Channel contactStateChannel = ChannelBuilder
|
||||
.create(new ChannelUID(channelGroupUID, CHANNEL_ID_CONTACT_STATEVALUE), CoreItemFactory.CONTACT)
|
||||
.withType(CHANNEL_CONTACT_STATEVALUE).build();
|
||||
return Collections.singletonMap(contactStateChannel, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(AttributeChangedMessage message) {
|
||||
switch (message.path.attributeName) {
|
||||
case BooleanStateCluster.ATTRIBUTE_STATE_VALUE -> {
|
||||
if (message.value instanceof Boolean booleanValue) {
|
||||
updateState(CHANNEL_ID_CONTACT_STATEVALUE,
|
||||
booleanValue ? OpenClosedType.CLOSED : OpenClosedType.OPEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
super.onEvent(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initState() {
|
||||
Boolean stateValue = initializingCluster.stateValue;
|
||||
if (stateValue == null) {
|
||||
updateState(CHANNEL_ID_CONTACT_STATEVALUE, UnDefType.NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
updateState(CHANNEL_ID_CONTACT_STATEVALUE, stateValue ? OpenClosedType.CLOSED : OpenClosedType.OPEN);
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.matter.internal.controller.devices.types;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster;
|
||||
import org.openhab.binding.matter.internal.client.dto.cluster.gen.BooleanStateCluster;
|
||||
import org.openhab.binding.matter.internal.controller.devices.converter.ContactStateConverter;
|
||||
import org.openhab.binding.matter.internal.controller.devices.converter.GenericConverter;
|
||||
import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler;
|
||||
|
||||
/**
|
||||
* A DeviceType for contact sensors.
|
||||
*
|
||||
* Contact sensors use BooleanState where TRUE means CLOSED and FALSE means OPEN.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ContactSensorType extends DeviceType {
|
||||
|
||||
public ContactSensorType(Integer deviceType, MatterBaseThingHandler handler, Integer endpointNumber) {
|
||||
super(deviceType, handler, endpointNumber);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable GenericConverter<? extends BaseCluster> createConverter(BaseCluster cluster,
|
||||
Map<String, BaseCluster> allClusters, String labelPrefix) {
|
||||
if (cluster instanceof BooleanStateCluster booleanStateCluster) {
|
||||
return new ContactStateConverter(booleanStateCluster, handler, endpointNumber, labelPrefix);
|
||||
}
|
||||
|
||||
return super.createConverter(cluster, allClusters, labelPrefix);
|
||||
}
|
||||
}
|
||||
+1
@@ -36,6 +36,7 @@ public class DeviceTypeRegistry {
|
||||
DeviceTypes.DIMMABLE_LIGHT, DeviceTypes.DIMMABLE_PLUG_IN_UNIT, DeviceTypes.DIMMER_SWITCH,
|
||||
DeviceTypes.COLOR_DIMMER_SWITCH, DeviceTypes.EXTENDED_COLOR_LIGHT, DeviceTypes.COLOR_TEMPERATURE_LIGHT)
|
||||
.forEach(type -> DeviceTypeRegistry.registerDeviceType(type, LightingType.class));
|
||||
DeviceTypeRegistry.registerDeviceType(DeviceTypes.CONTACT_SENSOR, ContactSensorType.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -378,6 +378,17 @@
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="contact-statevalue">
|
||||
<item-type>Contact</item-type>
|
||||
<label>Contact State</label>
|
||||
<description>Indicates a contact state, open or closed.</description>
|
||||
<tags>
|
||||
<tag>Status</tag>
|
||||
<tag>OpenState</tag>
|
||||
</tags>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="wifinetworkdiagnostics-rssi">
|
||||
<item-type unitHint="dBm">Number:Power</item-type>
|
||||
<label>Signal</label>
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2026 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.matter.internal.controller.devices.converter;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.openhab.binding.matter.internal.client.dto.cluster.gen.BooleanStateCluster;
|
||||
import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage;
|
||||
import org.openhab.binding.matter.internal.client.dto.ws.Path;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelGroupUID;
|
||||
import org.openhab.core.types.StateDescription;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* Test class for ContactStateConverter.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class ContactStateConverterTest extends BaseMatterConverterTest {
|
||||
|
||||
@Mock
|
||||
@NonNullByDefault({})
|
||||
private BooleanStateCluster mockCluster;
|
||||
@NonNullByDefault({})
|
||||
private ContactStateConverter contactConverter;
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
super.setUp();
|
||||
contactConverter = new ContactStateConverter(mockCluster, mockHandler, 1, "TestLabel");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateChannels() {
|
||||
ChannelGroupUID thingUID = new ChannelGroupUID("matter:node:test:12345:1");
|
||||
Map<Channel, @Nullable StateDescription> channels = contactConverter.createChannels(thingUID);
|
||||
assertEquals(1, channels.size());
|
||||
|
||||
Channel contactChannel = channels.keySet().stream()
|
||||
.filter(channel -> channel.getUID().toString().endsWith("#contact-statevalue")).findFirst()
|
||||
.orElseThrow();
|
||||
assertEquals("Contact", contactChannel.getAcceptedItemType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOnEventWithBooleanValueTrue() throws Exception {
|
||||
AttributeChangedMessage message = new AttributeChangedMessage();
|
||||
message.path = new Path();
|
||||
message.path.attributeName = "stateValue";
|
||||
message.value = true;
|
||||
contactConverter.onEvent(message);
|
||||
verify(mockHandler, times(1)).updateState(eq(1), eq("contact-statevalue"), eq(OpenClosedType.CLOSED));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOnEventWithBooleanValueFalse() throws Exception {
|
||||
AttributeChangedMessage message = new AttributeChangedMessage();
|
||||
message.path = new Path();
|
||||
message.path.attributeName = "stateValue";
|
||||
message.value = false;
|
||||
contactConverter.onEvent(message);
|
||||
verify(mockHandler, times(1)).updateState(eq(1), eq("contact-statevalue"), eq(OpenClosedType.OPEN));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInitState() throws Exception {
|
||||
mockCluster.stateValue = true;
|
||||
contactConverter.initState();
|
||||
verify(mockHandler, times(1)).updateState(eq(1), eq("contact-statevalue"), eq(OpenClosedType.CLOSED));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInitStateNull() throws Exception {
|
||||
mockCluster.stateValue = null;
|
||||
contactConverter.initState();
|
||||
verify(mockHandler, times(1)).updateState(eq(1), eq("contact-statevalue"), eq(UnDefType.NULL));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user