From da086cdd12c34fc2f1725d9609f20fce724a03a7 Mon Sep 17 00:00:00 2001 From: Dan Cunningham Date: Wed, 24 Sep 2025 14:21:11 -0700 Subject: [PATCH 1/5] [matter] adds support for door lock bolting (EU style locks Signed-off-by: Dan Cunningham --- bundles/org.openhab.binding.matter/README.md | 3 +- .../internal/MatterBindingConstants.java | 3 + .../devices/converter/DoorLockConverter.java | 68 ++++++++++++++--- .../resources/OH-INF/i18n/matter.properties | 4 +- .../main/resources/OH-INF/thing/channels.xml | 13 +++- .../converter/DoorLockConverterTest.java | 76 ++++++++++++++++++- 6 files changed, 154 insertions(+), 13 deletions(-) diff --git a/bundles/org.openhab.binding.matter/README.md b/bundles/org.openhab.binding.matter/README.md index df2b0a7861c83..7b1ee52977248 100644 --- a/bundles/org.openhab.binding.matter/README.md +++ b/bundles/org.openhab.binding.matter/README.md @@ -225,7 +225,8 @@ Possible channels include: | colorcontrol-color | Color | Color | The color channel allows to control the color of a light. It is also possible to dim values and switch the light on and off. | ColorLight | | | | colorcontrol-temperature | Dimmer | Color Temperature | Sets the color temperature of the light | ColorLight | | | | colorcontrol-temperature-abs | Number:Temperature | Color Temperature | Sets the color temperature of the light in mirek | ColorLight | | %.0f %unit% | -| doorlock-lockstate | Switch | Door Lock State | Locks and unlocks the door and maintains the lock state | Door | | | +| doorlock-lockstate | Switch | Door Lock/Latch State | Locks and unlocks the door and maintains the lock state. If the door lock supports unbolting, this will reflect the latched state. | Door | | | +| doorlock-boltstate | Switch | Door Bolt State | Bolts and unbolts the door and maintains the bolt state. | Door | | | | fancontrol-fanmode | Number | Fan Mode | Set the fan mode | HVAC | | | | onoffcontrol-onoff | Switch | Switch | Switches the power on and off | Light | | | | levelcontrol-level | Dimmer | Dimmer | Sets the level of the light | Light | | | diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterBindingConstants.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterBindingConstants.java index 606890cf36f2c..4f0eb26266241 100644 --- a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterBindingConstants.java +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterBindingConstants.java @@ -85,6 +85,9 @@ public class MatterBindingConstants { public static final String CHANNEL_ID_DOORLOCK_STATE = "doorlock-lockstate"; public static final ChannelTypeUID CHANNEL_DOORLOCK_STATE = new ChannelTypeUID(BINDING_ID, CHANNEL_ID_DOORLOCK_STATE); + public static final String CHANNEL_ID_DOORLOCK_BOLTSTATE = "doorlock-boltstate"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_BOLTSTATE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_DOORLOCK_BOLTSTATE); public static final String CHANNEL_ID_WINDOWCOVERING_LIFT = "windowcovering-lift"; public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_LIFT = new ChannelTypeUID(BINDING_ID, CHANNEL_ID_WINDOWCOVERING_LIFT); diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java index 4d69f5fa56c5a..07b8f87cfed41 100644 --- a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java @@ -12,10 +12,12 @@ */ package org.openhab.binding.matter.internal.controller.devices.converter; +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_DOORLOCK_BOLTSTATE; import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_DOORLOCK_STATE; +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_ID_DOORLOCK_BOLTSTATE; import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_ID_DOORLOCK_STATE; -import java.util.Collections; +import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -32,6 +34,7 @@ import org.openhab.core.thing.binding.builder.ChannelBuilder; import org.openhab.core.types.Command; import org.openhab.core.types.StateDescription; +import org.openhab.core.types.UnDefType; /** * A converter for translating {@link DoorLockCluster} events and attributes to openHAB channels and back again. @@ -48,19 +51,35 @@ public DoorLockConverter(DoorLockCluster cluster, MatterBaseThingHandler handler @Override public Map createChannels(ChannelGroupUID channelGroupUID) { + Map channels = new HashMap<>(); Channel channel = ChannelBuilder .create(new ChannelUID(channelGroupUID, CHANNEL_ID_DOORLOCK_STATE), CoreItemFactory.SWITCH) .withType(CHANNEL_DOORLOCK_STATE).build(); + channels.put(channel, null); + if (initializingCluster.featureMap.unbolting) { + Channel boltChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_DOORLOCK_BOLTSTATE), CoreItemFactory.SWITCH) + .withType(CHANNEL_DOORLOCK_BOLTSTATE).build(); + channels.put(boltChannel, null); + } - return Collections.singletonMap(channel, null); + return channels; } @Override public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof OnOffType onOffType) { - ClusterCommand doorLockCommand = onOffType == OnOffType.ON ? DoorLockCluster.lockDoor(null) - : DoorLockCluster.unlockDoor(null); - handler.sendClusterCommand(endpointNumber, DoorLockCluster.CLUSTER_NAME, doorLockCommand); + String id = channelUID.getIdWithoutGroup(); + if (id.equals(CHANNEL_ID_DOORLOCK_STATE)) { + ClusterCommand doorLockCommand = onOffType == OnOffType.ON ? DoorLockCluster.lockDoor(null) + : DoorLockCluster.unlockDoor(null); + handler.sendClusterCommand(endpointNumber, DoorLockCluster.CLUSTER_NAME, doorLockCommand); + } + if (id.equals(CHANNEL_ID_DOORLOCK_BOLTSTATE)) { + ClusterCommand boltCommand = onOffType == OnOffType.ON ? DoorLockCluster.lockDoor(null) + : DoorLockCluster.unboltDoor(null); + handler.sendClusterCommand(endpointNumber, DoorLockCluster.CLUSTER_NAME, boltCommand); + } } super.handleCommand(channelUID, command); } @@ -70,8 +89,7 @@ public void onEvent(AttributeChangedMessage message) { switch (message.path.attributeName) { case "lockState": if (message.value instanceof DoorLockCluster.LockStateEnum lockState) { - updateState(CHANNEL_ID_DOORLOCK_STATE, - lockState == DoorLockCluster.LockStateEnum.LOCKED ? OnOffType.ON : OnOffType.OFF); + updateLockState(lockState); } default: break; @@ -81,7 +99,39 @@ public void onEvent(AttributeChangedMessage message) { @Override public void initState() { - updateState(CHANNEL_ID_DOORLOCK_STATE, - initializingCluster.lockState == DoorLockCluster.LockStateEnum.LOCKED ? OnOffType.ON : OnOffType.OFF); + updateLockState(initializingCluster.lockState); + } + + private void updateLockState(DoorLockCluster.LockStateEnum lockState) { + switch (lockState) { + case LOCKED: + // both the lock and bolt state are locked + updateState(CHANNEL_ID_DOORLOCK_STATE, OnOffType.ON); + if (initializingCluster.featureMap.unbolting) { + updateState(CHANNEL_ID_DOORLOCK_BOLTSTATE, OnOffType.ON); + } + break; + case UNLOCKED: + // both the lock and bolt state are unlocked + updateState(CHANNEL_ID_DOORLOCK_STATE, OnOffType.OFF); + if (initializingCluster.featureMap.unbolting) { + updateState(CHANNEL_ID_DOORLOCK_BOLTSTATE, OnOffType.OFF); + } + break; + case UNLATCHED: + // the lock state is locked (latched), but the bolt state is unlocked + updateState(CHANNEL_ID_DOORLOCK_STATE, OnOffType.ON); + if (initializingCluster.featureMap.unbolting) { + updateState(CHANNEL_ID_DOORLOCK_BOLTSTATE, OnOffType.OFF); + } + break; + case NOT_FULLY_LOCKED: + // we don't know the state of the lock or bolt, something is wrong + updateState(CHANNEL_ID_DOORLOCK_STATE, UnDefType.UNDEF); + if (initializingCluster.featureMap.unbolting) { + updateState(CHANNEL_ID_DOORLOCK_BOLTSTATE, UnDefType.UNDEF); + } + break; + } } } diff --git a/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/i18n/matter.properties b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/i18n/matter.properties index d8bf2093fbd44..88b2ad5fd0f7c 100644 --- a/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/i18n/matter.properties +++ b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/i18n/matter.properties @@ -77,8 +77,10 @@ channel-type.matter.colorcontrol-temperature-abs.label = Color Temperature channel-type.matter.colorcontrol-temperature-abs.description = Sets the color temperature of the light in mirek channel-type.matter.colorcontrol-temperature.label = Color Temperature channel-type.matter.colorcontrol-temperature.description = Sets the color temperature of the light +channel-type.matter.doorlock-boltstate.label = Door Bolt State +channel-type.matter.doorlock-boltstate.description = Bolts and unbolts the door and maintains the bolt state. channel-type.matter.doorlock-lockstate.label = Door Lock State -channel-type.matter.doorlock-lockstate.description = Locks and unlocks the door and maintains the lock state +channel-type.matter.doorlock-lockstate.description = Locks and unlocks the door and maintains the lock state. If the door lock supports unbolting, this will reflect the latched state. channel-type.matter.electricalenergymeasurement-energymeasurmement-energy.label = Energy channel-type.matter.electricalpowermeasurement-activecurrent.label = Active Current channel-type.matter.electricalpowermeasurement-activepower.label = Active Power diff --git a/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml index 09a986b680ac3..d564b66a670fb 100644 --- a/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml @@ -421,7 +421,18 @@ Switch - Locks and unlocks the door and maintains the lock state + Locks and unlocks the door and maintains the lock state. If the door lock supports unbolting, this will reflect the latched state. + + Status + OpenState + + veto + + + + Switch + + Bolts and unbolts the door and maintains the bolt state. Status OpenState diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverterTest.java index abd3ea9b8f061..ad6c537948b88 100644 --- a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverterTest.java +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverterTest.java @@ -32,6 +32,7 @@ import org.openhab.core.thing.ChannelGroupUID; import org.openhab.core.thing.ChannelUID; import org.openhab.core.types.StateDescription; +import org.openhab.core.types.UnDefType; /** * Test class for DoorLockConverter @@ -56,7 +57,9 @@ void setUp() { } @Test - void testCreateChannels() { + void testCreateStandardChannels() { + mockCluster.featureMap = new DoorLockCluster.FeatureMap(false, false, false, false, false, false, false, false, + false, false, false, false, false); ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); Map channels = converter.createChannels(channelGroupUID); assertEquals(1, channels.size()); @@ -65,6 +68,21 @@ void testCreateChannels() { assertEquals("Switch", channel.getAcceptedItemType()); } + @Test + void testCreateChannelsWithUnbolting() { + mockCluster.featureMap = new DoorLockCluster.FeatureMap(false, false, false, false, false, false, false, false, + false, false, true, false, false); + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(2, channels.size()); + boolean hasLockState = channels.keySet().stream() + .anyMatch(c -> c.getUID().toString().equals("matter:node:test:12345:1#doorlock-lockstate")); + boolean hasBoltState = channels.keySet().stream() + .anyMatch(c -> c.getUID().toString().equals("matter:node:test:12345:1#doorlock-boltstate")); + assertEquals(true, hasLockState); + assertEquals(true, hasBoltState); + } + @Test void testHandleCommandLock() { ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#doorlock-lockstate"); @@ -81,20 +99,76 @@ void testHandleCommandUnlock() { eq(DoorLockCluster.unlockDoor(null))); } + @Test + void testHandleBoltCommands() { + ChannelUID boltChannelUID = new ChannelUID("matter:node:test:12345:1#doorlock-boltstate"); + converter.handleCommand(boltChannelUID, OnOffType.ON); + verify(mockHandler, times(1)).sendClusterCommand(eq(1), eq(DoorLockCluster.CLUSTER_NAME), + eq(DoorLockCluster.lockDoor(null))); + converter.handleCommand(boltChannelUID, OnOffType.OFF); + verify(mockHandler, times(1)).sendClusterCommand(eq(1), eq(DoorLockCluster.CLUSTER_NAME), + eq(DoorLockCluster.unboltDoor(null))); + } + @Test void testOnEventWithLockState() { + mockCluster.featureMap = new DoorLockCluster.FeatureMap(false, false, false, false, false, false, false, false, + false, false, true, false, false); AttributeChangedMessage message = new AttributeChangedMessage(); message.path = new Path(); message.path.attributeName = "lockState"; message.value = DoorLockCluster.LockStateEnum.LOCKED; converter.onEvent(message); verify(mockHandler, times(1)).updateState(eq(1), eq("doorlock-lockstate"), eq(OnOffType.ON)); + verify(mockHandler, times(1)).updateState(eq(1), eq("doorlock-boltstate"), eq(OnOffType.ON)); + } + + @Test + void testOnEventWithLockStateUnboltingUnlocked() { + mockCluster.featureMap = new DoorLockCluster.FeatureMap(false, false, false, false, false, false, false, false, + false, false, true, false, false); + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "lockState"; + message.value = DoorLockCluster.LockStateEnum.UNLOCKED; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("doorlock-lockstate"), eq(OnOffType.OFF)); + verify(mockHandler, times(1)).updateState(eq(1), eq("doorlock-boltstate"), eq(OnOffType.OFF)); + } + + @Test + void testOnEventWithLockStateUnboltingUnlatched() { + mockCluster.featureMap = new DoorLockCluster.FeatureMap(false, false, false, false, false, false, false, false, + false, false, true, false, false); + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "lockState"; + message.value = DoorLockCluster.LockStateEnum.UNLATCHED; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("doorlock-lockstate"), eq(OnOffType.ON)); + verify(mockHandler, times(1)).updateState(eq(1), eq("doorlock-boltstate"), eq(OnOffType.OFF)); + } + + @Test + void testOnEventWithLockStateUnboltingNotFullyLocked() { + mockCluster.featureMap = new DoorLockCluster.FeatureMap(false, false, false, false, false, false, false, false, + false, false, true, false, false); + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "lockState"; + message.value = DoorLockCluster.LockStateEnum.NOT_FULLY_LOCKED; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("doorlock-lockstate"), eq(UnDefType.UNDEF)); + verify(mockHandler, times(1)).updateState(eq(1), eq("doorlock-boltstate"), eq(UnDefType.UNDEF)); } @Test void testInitState() { + mockCluster.featureMap = new DoorLockCluster.FeatureMap(false, false, false, false, false, false, false, false, + false, false, true, false, false); mockCluster.lockState = DoorLockCluster.LockStateEnum.LOCKED; converter.initState(); verify(mockHandler, times(1)).updateState(eq(1), eq("doorlock-lockstate"), eq(OnOffType.ON)); + verify(mockHandler, times(1)).updateState(eq(1), eq("doorlock-boltstate"), eq(OnOffType.ON)); } } From ea5bc5bf499287e8a429bb56a6503dad717667c3 Mon Sep 17 00:00:00 2001 From: Dan Cunningham Date: Wed, 24 Sep 2025 14:46:43 -0700 Subject: [PATCH 2/5] Copilot review feedback #1 Signed-off-by: Dan Cunningham --- .../devices/converter/DoorLockConverter.java | 20 ++++++++++--------- .../main/resources/OH-INF/thing/channels.xml | 3 ++- .../converter/DoorLockConverterTest.java | 5 +++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java index 07b8f87cfed41..4f96f2612e71e 100644 --- a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java @@ -70,15 +70,17 @@ public DoorLockConverter(DoorLockCluster cluster, MatterBaseThingHandler handler public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof OnOffType onOffType) { String id = channelUID.getIdWithoutGroup(); - if (id.equals(CHANNEL_ID_DOORLOCK_STATE)) { - ClusterCommand doorLockCommand = onOffType == OnOffType.ON ? DoorLockCluster.lockDoor(null) - : DoorLockCluster.unlockDoor(null); - handler.sendClusterCommand(endpointNumber, DoorLockCluster.CLUSTER_NAME, doorLockCommand); - } - if (id.equals(CHANNEL_ID_DOORLOCK_BOLTSTATE)) { - ClusterCommand boltCommand = onOffType == OnOffType.ON ? DoorLockCluster.lockDoor(null) - : DoorLockCluster.unboltDoor(null); - handler.sendClusterCommand(endpointNumber, DoorLockCluster.CLUSTER_NAME, boltCommand); + switch (id) { + case CHANNEL_ID_DOORLOCK_STATE: + ClusterCommand doorLockCommand = onOffType == OnOffType.ON ? DoorLockCluster.lockDoor(null) + : DoorLockCluster.unlockDoor(null); + handler.sendClusterCommand(endpointNumber, DoorLockCluster.CLUSTER_NAME, doorLockCommand); + break; + case CHANNEL_ID_DOORLOCK_BOLTSTATE: + ClusterCommand boltCommand = onOffType == OnOffType.ON ? DoorLockCluster.lockDoor(null) + : DoorLockCluster.unboltDoor(null); + handler.sendClusterCommand(endpointNumber, DoorLockCluster.CLUSTER_NAME, boltCommand); + break; } } super.handleCommand(channelUID, command); diff --git a/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml index d564b66a670fb..f5e117c9cee2e 100644 --- a/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml @@ -421,7 +421,8 @@ Switch - Locks and unlocks the door and maintains the lock state. If the door lock supports unbolting, this will reflect the latched state. + Locks and unlocks the door and maintains the lock state. If the door lock supports unbolting, this will + reflect the latched state. Status OpenState diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverterTest.java index ad6c537948b88..1618f6a26df03 100644 --- a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverterTest.java +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverterTest.java @@ -13,6 +13,7 @@ package org.openhab.binding.matter.internal.controller.devices.converter; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -79,8 +80,8 @@ void testCreateChannelsWithUnbolting() { .anyMatch(c -> c.getUID().toString().equals("matter:node:test:12345:1#doorlock-lockstate")); boolean hasBoltState = channels.keySet().stream() .anyMatch(c -> c.getUID().toString().equals("matter:node:test:12345:1#doorlock-boltstate")); - assertEquals(true, hasLockState); - assertEquals(true, hasBoltState); + assertTrue(hasLockState); + assertTrue(hasBoltState); } @Test From 77d58078b2a247dcbc2698f7b9738a43c5b648d1 Mon Sep 17 00:00:00 2001 From: Dan Cunningham Date: Wed, 24 Sep 2025 14:50:11 -0700 Subject: [PATCH 3/5] Update bundles/org.openhab.binding.matter/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Dan Cunningham --- bundles/org.openhab.binding.matter/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.matter/README.md b/bundles/org.openhab.binding.matter/README.md index 7b1ee52977248..82b62381cfbfd 100644 --- a/bundles/org.openhab.binding.matter/README.md +++ b/bundles/org.openhab.binding.matter/README.md @@ -225,7 +225,7 @@ Possible channels include: | colorcontrol-color | Color | Color | The color channel allows to control the color of a light. It is also possible to dim values and switch the light on and off. | ColorLight | | | | colorcontrol-temperature | Dimmer | Color Temperature | Sets the color temperature of the light | ColorLight | | | | colorcontrol-temperature-abs | Number:Temperature | Color Temperature | Sets the color temperature of the light in mirek | ColorLight | | %.0f %unit% | -| doorlock-lockstate | Switch | Door Lock/Latch State | Locks and unlocks the door and maintains the lock state. If the door lock supports unbolting, this will reflect the latched state. | Door | | | +| doorlock-lockstate | Switch | Door Lock State | Locks and unlocks the door and maintains the lock state. If the door lock supports unbolting, this will reflect the latched state. | Door | | | | doorlock-boltstate | Switch | Door Bolt State | Bolts and unbolts the door and maintains the bolt state. | Door | | | | fancontrol-fanmode | Number | Fan Mode | Set the fan mode | HVAC | | | | onoffcontrol-onoff | Switch | Switch | Switches the power on and off | Light | | | From cc01a9f2dc5a76883e2d7a6e437343c9e9e464fc Mon Sep 17 00:00:00 2001 From: Dan Cunningham Date: Fri, 9 Jan 2026 10:36:05 -0800 Subject: [PATCH 4/5] Merge main Signed-off-by: Dan Cunningham --- .../binding/matter/internal/MatterBindingConstants.java | 3 --- .../controller/devices/converter/DoorLockConverter.java | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterBindingConstants.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterBindingConstants.java index 076b852bcbc92..a6c5fa2ad6289 100644 --- a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterBindingConstants.java +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterBindingConstants.java @@ -130,11 +130,9 @@ public class MatterBindingConstants { public static final String CHANNEL_ID_DOORLOCK_STATE = "doorlock-lockstate"; public static final ChannelTypeUID CHANNEL_DOORLOCK_STATE = new ChannelTypeUID(BINDING_ID, CHANNEL_ID_DOORLOCK_STATE); -<<<<<<< HEAD public static final String CHANNEL_ID_DOORLOCK_BOLTSTATE = "doorlock-boltstate"; public static final ChannelTypeUID CHANNEL_DOORLOCK_BOLTSTATE = new ChannelTypeUID(BINDING_ID, CHANNEL_ID_DOORLOCK_BOLTSTATE); -======= public static final String CHANNEL_ID_DOORLOCK_DOORSTATE = "doorlock-doorstate"; public static final ChannelTypeUID CHANNEL_DOORLOCK_DOORSTATE = new ChannelTypeUID(BINDING_ID, CHANNEL_ID_DOORLOCK_DOORSTATE); @@ -144,7 +142,6 @@ public class MatterBindingConstants { public static final String CHANNEL_ID_DOORLOCK_LOCKOPERATIONERROR = "doorlock-lockoperationerror"; public static final ChannelTypeUID CHANNEL_DOORLOCK_LOCKOPERATIONERROR = new ChannelTypeUID(BINDING_ID, CHANNEL_ID_DOORLOCK_LOCKOPERATIONERROR); ->>>>>>> main public static final String CHANNEL_ID_WINDOWCOVERING_LIFT = "windowcovering-lift"; public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_LIFT = new ChannelTypeUID(BINDING_ID, CHANNEL_ID_WINDOWCOVERING_LIFT); diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java index 41292936138d0..080f1970fd625 100644 --- a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java @@ -54,7 +54,6 @@ import org.openhab.core.library.CoreItemFactory; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OpenClosedType; -import org.openhab.core.types.UnDefType; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelGroupUID; import org.openhab.core.thing.ChannelUID; @@ -64,6 +63,7 @@ import org.openhab.core.types.StateDescription; import org.openhab.core.types.StateDescriptionFragmentBuilder; import org.openhab.core.types.StateOption; +import org.openhab.core.types.UnDefType; /** * A converter for translating {@link DoorLockCluster} events and attributes to @@ -206,7 +206,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { String channelId = channelUID.getIdWithoutGroup(); OctetString pinCode = getPinCodeForRemoteOperation(); ClusterCommand doorLockCommand; - + if (channelId.equals(CHANNEL_ID_DOORLOCK_BOLTSTATE)) { // Bolt state: lock (bolt) or unbolt doorLockCommand = onOffType == OnOffType.ON ? DoorLockCluster.lockDoor(pinCode) From 8840efceb7d097b5eed4f907c4be1152dbf63ef8 Mon Sep 17 00:00:00 2001 From: Dan Cunningham Date: Thu, 15 Jan 2026 15:24:37 -0800 Subject: [PATCH 5/5] PR Feedback Signed-off-by: Dan Cunningham --- .../src/main/resources/OH-INF/thing/channels.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml index 5ecc7541fc2cf..2260b6ef71192 100644 --- a/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml @@ -397,7 +397,7 @@ reflect the latched state. Status - OpenState + LockState veto