diff --git a/bundles/org.openhab.binding.lcn/README.md b/bundles/org.openhab.binding.lcn/README.md index 09a1243135407..da4232e58fa25 100644 --- a/bundles/org.openhab.binding.lcn/README.md +++ b/bundles/org.openhab.binding.lcn/README.md @@ -152,10 +152,11 @@ If a special command is needed, the [Hit Key](#hit-key) action (German: "Sende T | Send Keys | Sende Tasten | N/A | N/A | N/A | Action: "hitKey": Hits a key of a key table in an LCN module. Can be used to execute commands, not supported by this binding. | | Dimmer Output Control Multiple | Mehrere Ausgänge steuern | output | 1-4 | Dimmer, Switch | Control multiple outputs simultaneously. See below. | | Transponder | Transponder | code#transponder | | Trigger | Receive transponder messages | -| Fingerprint | Fingerprint | code#fingerprint | | Trigger | Receive fingerprint code messages | +| Fingerprint | Fingerprint | code#fingerprint | | Trigger | Receive fingerprint code messages | | Remote Control | Fernbedienung | code#remotecontrolkey | | Trigger | Receive commands from remote control | | Access Control | Zutrittskontrolle | code#remotecontrolcode | | Trigger | Receive serial numbers from remote control | | Remote Control Battery Low | Fernbedienung Batterie schwach | code#remotecontrolbatterylow | | Trigger | Triggered when the sending remote control has a low battery | +| Host Command (Send Keys) | Kommando an Host (Sende Tasten) | hostcommand#sendKeys | - | Trigger | Receive *send keys* command from LCN module | | Status Message | Statusmeldungen | - | - | - | Automatically done by openHAB Binding | | Audio Beep | Audio Piepen | - | - | - | Not implemented | | Audio LCN-MRS | Audio LCN-MRS | - | - | - | Not implemented | @@ -218,6 +219,35 @@ then end ``` +### Command from an LCN Module to openHAB + +LCN modules can send commands to openHAB, e.g. by pressing a physical LCN key. +The command must be programmed into the LCN module by the programming software LCN-PRO. +Only the *send keys* command (German: "Sende Tasten") is supported. + +Program a command to a key of an LCN module via LCN-PRO. +When LCN-PRO asks you for the target address, don't select any module, but manually enter the PCK host ID, configured within PCHK (default: 4). +Select the *send keys* command and "A-C (former command)", as PCHK 3.2.2 only supports the old command. +Then, select any key(s) you want to send to openHAB. These can be freely chosen, as they are only evaluated by openHAB. + +![Screenshot, showing the send keys command](doc/host_command_send_keys.png) + +The following rule can be used to trigger any action: + +``` +rule "Module 12 sent A1 Hit" +when + Channel "lcn:module:b827ebfea4bb:S000M012:hostcommand#sendKeys" triggered "A1:HIT" +then + M10_Relay7.sendCommand(ON) +end +``` + +`A1` is the key of the *send keys* command, programmed by LCN-PRO. +After the colon, the LCN "hit type" follows: HIT, MAKE or BREAK (German: kurz, lang, los) + +If multiple keys or key tables are programmed in a single "send keys" command, multiple triggers will be executed. + ### Remote Control To evaluate commands from LCN remote controls (e.g. LCN-RT or LCN-RT16), the module's I-port behavior must be configured as "IR access control" within *LCN-PRO*: diff --git a/bundles/org.openhab.binding.lcn/doc/host_command_send_keys.png b/bundles/org.openhab.binding.lcn/doc/host_command_send_keys.png new file mode 100644 index 0000000000000..fe35d91642b93 Binary files /dev/null and b/bundles/org.openhab.binding.lcn/doc/host_command_send_keys.png differ diff --git a/bundles/org.openhab.binding.lcn/pom.xml b/bundles/org.openhab.binding.lcn/pom.xml index 43b946c1e35e0..5cfc94ed59d85 100644 --- a/bundles/org.openhab.binding.lcn/pom.xml +++ b/bundles/org.openhab.binding.lcn/pom.xml @@ -1,4 +1,6 @@ - + + 4.0.0 diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnChannelGroup.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnChannelGroup.java index d9d6a6da4f38c..555dfea2e7c32 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnChannelGroup.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnChannelGroup.java @@ -20,6 +20,7 @@ import org.openhab.binding.lcn.internal.subhandler.AbstractLcnModuleSubHandler; import org.openhab.binding.lcn.internal.subhandler.LcnModuleBinarySensorSubHandler; import org.openhab.binding.lcn.internal.subhandler.LcnModuleCodeSubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleHostCommandSubHandler; import org.openhab.binding.lcn.internal.subhandler.LcnModuleKeyLockTableSubHandler; import org.openhab.binding.lcn.internal.subhandler.LcnModuleLedSubHandler; import org.openhab.binding.lcn.internal.subhandler.LcnModuleLogicSubHandler; @@ -59,7 +60,8 @@ public enum LcnChannelGroup { KEYLOCKTABLEB(8, LcnModuleKeyLockTableSubHandler::new), KEYLOCKTABLEC(8, LcnModuleKeyLockTableSubHandler::new), KEYLOCKTABLED(8, LcnModuleKeyLockTableSubHandler::new), - CODE(0, LcnModuleCodeSubHandler::new); + CODE(0, LcnModuleCodeSubHandler::new), + HOSTCOMMAND(0, LcnModuleHostCommandSubHandler::new); private int count; private BiFunction handlerFactory; diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnDefs.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnDefs.java index 6ad123bc8affe..6d47e729cf76c 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnDefs.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnDefs.java @@ -14,6 +14,8 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.NoSuchElementException; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -31,6 +33,10 @@ public final class LcnDefs { public static final int THRESHOLD_REGISTER_COUNT = 4; /** Number of key tables of an LCN module. */ public static final int KEY_TABLE_COUNT = 4; + /** Number of key tables of an LCN module before firmware 0C030C0. */ + public static final int KEY_TABLE_COUNT_UNTIL_0C030C0 = 3; + /** Number of keys per table of an LCN module */ + public static final int KEY_COUNT = 8; /** Number of thresholds before LCN module firmware version 2013 */ public static final int THRESHOLD_COUNT_BEFORE_2013 = 5; /** @@ -114,10 +120,25 @@ public enum RelVarRef { /** Command types used when sending LCN keys. */ public enum SendKeyCommand { - HIT, - MAKE, - BREAK, - DONTSEND + DONTSEND(0), + HIT(1), + MAKE(2), + BREAK(3); + + private int id; + + SendKeyCommand(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public static SendKeyCommand get(int id) { + return Arrays.stream(values()).filter(v -> v.getId() == id).findAny() + .orElseThrow(NoSuchElementException::new); + } } /** Key-lock modifiers used in LCN commands. */ diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleHostCommandSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleHostCommandSubHandler.java new file mode 100644 index 0000000000000..f1a2f485d7658 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleHostCommandSubHandler.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2010-2020 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.lcn.internal.subhandler; + +import java.util.Collection; +import java.util.Collections; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.LcnModuleHandler; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnDefs; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.connection.ModInfo; + +/** + * Handles 'send key' commands sent to this PCK host. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleHostCommandSubHandler extends AbstractLcnModuleSubHandler { + private static final Pattern SEND_KEY_PATTERN = Pattern + .compile("\\+M(?\\d{3})(?\\d{3})(?\\d{3})\\.STH(?\\d{3})(?\\d{3})"); + + public LcnModuleHostCommandSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + // nothing + } + + @Override + public void handleStatusMessage(Matcher matcher) throws LcnException { + int keyTableAndActionMask = Integer.parseInt(matcher.group("byte0")); + int keyNumberMask = Integer.parseInt(matcher.group("byte1")); + + if ((keyTableAndActionMask & (1 << 6)) == 0) { + return; + } + + // PCHK 3.22 supports only the old 'send key' command with key tables A-C + for (int keyTableNumber = 0; keyTableNumber < LcnDefs.KEY_TABLE_COUNT_UNTIL_0C030C0; keyTableNumber++) { + String keyTableName = LcnDefs.KeyTable.values()[keyTableNumber].name(); + + for (int keyNumber = 0; keyNumber < LcnDefs.KEY_COUNT; keyNumber++) { + int actionRaw = (keyTableAndActionMask >> (keyTableNumber * 2)) & 3; + + if (actionRaw > LcnDefs.SendKeyCommand.DONTSEND.getId() + && actionRaw <= LcnDefs.SendKeyCommand.BREAK.getId() + && ((1 << keyNumber) & keyNumberMask) != 0) { + String actionName = LcnDefs.SendKeyCommand.get(actionRaw).name(); + + handler.triggerChannel(LcnChannelGroup.HOSTCOMMAND, "sendKeys", + keyTableName + (keyNumber + 1) + ":" + actionName); + } + } + } + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Collections.singleton(SEND_KEY_PATTERN); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/i18n/lcn_de.properties b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/i18n/lcn_de.properties index 17b1f505b52e9..e8df2d867b4d7 100644 --- a/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/i18n/lcn_de.properties +++ b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/i18n/lcn_de.properties @@ -170,3 +170,5 @@ channel-group-type.lcn.codes.channel.fingerprint.label = Fingerprint-Code channel-group-type.lcn.codes.channel.remotecontrolkey.label = Fernbedienung Tasten channel-group-type.lcn.codes.channel.remotecontrolcode.label = Fernbedienung Tasten mit Zutrittscode channel-group-type.lcn.codes.channel.remotecontrolbatterylow.label = Fernbedienung Batterie leer +channel-group-type.lcn.hostcommands.label = Kommandos an Host (openHAB) +channel-group-type.lcn.hostcommands.channel.sendKeys.label = Sende Tasten diff --git a/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/thing/thing-types.xml index 52ab3ebc003cf..782edc75707f1 100644 --- a/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/thing/thing-types.xml @@ -39,6 +39,7 @@ + @@ -654,4 +655,94 @@ + + + + + + + + + + trigger + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleHostCommandSubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleHostCommandSubHandlerTest.java new file mode 100644 index 0000000000000..f755ca764a23f --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleHostCommandSubHandlerTest.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2020 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.lcn.internal.subhandler; + +import static org.mockito.Mockito.verify; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; + +/** + * Test class. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleHostCommandSubHandlerTest extends AbstractTestLcnModuleSubHandler { + private @NonNullByDefault({}) LcnModuleHostCommandSubHandler subHandler; + + @Override + @Before + public void setUp() { + super.setUp(); + + subHandler = new LcnModuleHostCommandSubHandler(handler, info); + } + + @Test + public void testA1Hit() { + subHandler.tryParse("+M004000005.STH065001"); + verify(handler).triggerChannel(LcnChannelGroup.HOSTCOMMAND, "sendKeys", "A1:HIT"); + } + + @Test + public void testA1Make() { + subHandler.tryParse("+M004000005.STH066001"); + verify(handler).triggerChannel(LcnChannelGroup.HOSTCOMMAND, "sendKeys", "A1:MAKE"); + } + + @Test + public void testC8Break() { + subHandler.tryParse("+M004000005.STH112128"); + verify(handler).triggerChannel(LcnChannelGroup.HOSTCOMMAND, "sendKeys", "C8:BREAK"); + } + + @Test + public void testC1Hit() { + subHandler.tryParse("+M004000005.STH080001"); + verify(handler).triggerChannel(LcnChannelGroup.HOSTCOMMAND, "sendKeys", "C1:HIT"); + } + + @Test + public void testMultiple() { + subHandler.tryParse("+M004000005.STH121034"); + verify(handler).triggerChannel(LcnChannelGroup.HOSTCOMMAND, "sendKeys", "A2:HIT"); + verify(handler).triggerChannel(LcnChannelGroup.HOSTCOMMAND, "sendKeys", "A6:HIT"); + verify(handler).triggerChannel(LcnChannelGroup.HOSTCOMMAND, "sendKeys", "B2:MAKE"); + verify(handler).triggerChannel(LcnChannelGroup.HOSTCOMMAND, "sendKeys", "B6:MAKE"); + verify(handler).triggerChannel(LcnChannelGroup.HOSTCOMMAND, "sendKeys", "C2:BREAK"); + verify(handler).triggerChannel(LcnChannelGroup.HOSTCOMMAND, "sendKeys", "C6:BREAK"); + } +}