From 6ad005ae851d2f08cfced2ccf45d9221568c923b Mon Sep 17 00:00:00 2001 From: Thomas Weiler <18066810+toweosp@users.noreply.github.com> Date: Sat, 1 Aug 2020 12:48:16 +0200 Subject: [PATCH] [lcn] Make auto discovery use segment id and module id for ThingID (#8120) Signed-off-by: Thomas Weiler --- bundles/org.openhab.binding.lcn/README.md | 100 +++++++++--------- .../internal/LcnModuleDiscoveryService.java | 6 +- .../lcn/internal/LcnModuleHandler.java | 26 +++++ .../resources/ESH-INF/thing/thing-types.xml | 4 + 4 files changed, 84 insertions(+), 52 deletions(-) diff --git a/bundles/org.openhab.binding.lcn/README.md b/bundles/org.openhab.binding.lcn/README.md index ed111d701e4d2..f59f4d5e734cd 100644 --- a/bundles/org.openhab.binding.lcn/README.md +++ b/bundles/org.openhab.binding.lcn/README.md @@ -191,7 +191,7 @@ To do so, simply start openHAB's discovery. If not all LCN modules get listed on the first run, click on the refresh button to start another scan. -When adding a module by discovery, the new *Thing*'s UID will be the module's serial number. +When adding a module by discovery, the new *Thing*'s UID will be a combination of segment and module id using the following format: *SM* where *segment_id* and *module_id* are formatted as three-digit numbers with leading zeros. ### Discover PCK Gateways @@ -285,12 +285,12 @@ The Rollershutter Channels provide the boolean parameter `invertUpDown`, which c LCN transponder readers can be integrated in openHAB e.g. for access control. The transponder function must be enabled in the module's I-port properties within *LCN-PRO*. -Example: When the transponder card with the ID "12ABCD" is seen by the reader connected to LCN module "17B308349E", the item "M10_Relay7" is switched on: +Example: When the transponder card with the ID "12ABCD" is seen by the reader connected to LCN module "S000M011", the item "M10_Relay7" is switched on: ``` rule "My Transponder" when - Channel "lcn:module:b827ebfea4bb:17B308349E:code#transponder" triggered "12ABCD" + Channel "lcn:module:b827ebfea4bb:S000M011:code#transponder" triggered "12ABCD" then M10_Relay7.sendCommand(ON) end @@ -309,7 +309,7 @@ The trigger *Channel* `lcn:module:::code#remotecontrolkey ``` rule "Remote Control Key 3 on Layer 1 hit" when - Channel "lcn:module:b827ebfea4bb:17B3073D6A:code#remotecontrolkey" triggered "A3:HIT" + Channel "lcn:module:b827ebfea4bb:S000M012:code#remotecontrolkey" triggered "A3:HIT" then M10_Relay7.sendCommand(ON) end @@ -324,8 +324,8 @@ The serial number of a remote control can be used for access control via the cha ``` rule "Remote Control Key 3 on Layer 1 hit (only executed for serial number AB1234)" when - Channel "lcn:module:b827ebfea4bb:17B3073D6A:code#remotecontrolcode" triggered "AB1234:A3:HIT" or - Channel "lcn:module:b827ebfea4bb:17B3073D6A:code#remotecontrolcode" triggered "AB1234:A3:MAKE" + Channel "lcn:module:b827ebfea4bb:S000M012:code#remotecontrolcode" triggered "AB1234:A3:HIT" or + Channel "lcn:module:b827ebfea4bb:S000M012:code#remotecontrolcode" triggered "AB1234:A3:MAKE" then M10_Relay7.sendCommand(ON) end @@ -344,9 +344,9 @@ The ramp parameter is not available for Color *Item*s. ``` // Dim output 2 in 0.25s -Switch M10_Output2 {channel="lcn:module:b827ebfea4bb:17B4196847:output#2"[profile="lcn:output", ramp=0.25]} // with ramp of 0.25s (smallest value) +Switch M10_Output2 {channel="lcn:module:b827ebfea4bb:S000M010:output#2"[profile="lcn:output", ramp=0.25]} // with ramp of 0.25s (smallest value) // Dim output 3 in 486s -Dimmer M10_Output3 {channel="lcn:module:b827ebfea4bb:17B4196847:output#3"[profile="lcn:output", ramp=486]} // with ramp of 486s (biggest value) +Dimmer M10_Output3 {channel="lcn:module:b827ebfea4bb:S000M010:output#3"[profile="lcn:output", ramp=486]} // with ramp of 486s (biggest value) ``` The optional parameters *controlAllOutputs* and *controlOutputs12* can be used to control multiple outputs simultaneously. @@ -354,12 +354,12 @@ Please note that the combination of these parameters with the *ramp* parameter i ``` // Control outputs 1+2 simultaneously. Status of Output 1 is visualized. Only ramps of 0s or 0.25s are supported. -Dimmer M10_Outputs12a {channel="lcn:module:b827ebfea4bb:17B4196847:output#1"[profile="lcn:output", controlOutputs12=true]} -Dimmer M10_Outputs12b {channel="lcn:module:b827ebfea4bb:17B4196847:output#1"[profile="lcn:output", controlOutputs12=true, ramp=0.25]} +Dimmer M10_Outputs12a {channel="lcn:module:b827ebfea4bb:S000M010:output#1"[profile="lcn:output", controlOutputs12=true]} +Dimmer M10_Outputs12b {channel="lcn:module:b827ebfea4bb:S000M010:output#1"[profile="lcn:output", controlOutputs12=true, ramp=0.25]} // Control all outputs simultaneously. Status of Output 1 is visualized. -Dimmer M10_OutputAll1 {channel="lcn:module:b827ebfea4bb:17B4196847:output#1"[profile="lcn:output", controlAllOutputs=true, ramp=0]} // ramp only since firmware 180501 -Dimmer M10_OutputAll2 {channel="lcn:module:b827ebfea4bb:17B4196847:output#1"[profile="lcn:output", controlAllOutputs=true, ramp=0.25]} // ramp compatibility: all -Dimmer M10_OutputAll3 {channel="lcn:module:b827ebfea4bb:17B4196847:output#1"[profile="lcn:output", controlAllOutputs=true, ramp=0.5]} // ramp only since firmware 180501 +Dimmer M10_OutputAll1 {channel="lcn:module:b827ebfea4bb:S000M010:output#1"[profile="lcn:output", controlAllOutputs=true, ramp=0]} // ramp only since firmware 180501 +Dimmer M10_OutputAll2 {channel="lcn:module:b827ebfea4bb:S000M010:output#1"[profile="lcn:output", controlAllOutputs=true, ramp=0.25]} // ramp compatibility: all +Dimmer M10_OutputAll3 {channel="lcn:module:b827ebfea4bb:S000M010:output#1"[profile="lcn:output", controlAllOutputs=true, ramp=0.5]} // ramp only since firmware 180501 ``` ## Actions @@ -386,7 +386,7 @@ rule "Hit key C4 hourly" when Time cron "0 0 * * * ?" then - val actions = getActions("lcn","lcn:module:b827ebfea4bb:17B4196847") + val actions = getActions("lcn","lcn:module:b827ebfea4bb:S000M010") actions.hitKey("C", 4, "HIT") end ``` @@ -411,7 +411,7 @@ rule "Send dynamic Text to GT10D hourly" when Time cron "0 0 * * * ?" then - val actions = getActions("lcn","lcn:module:b827ebfea4bb:17B3073D6A") + val actions = getActions("lcn","lcn:module:b827ebfea4bb:S000M012") actions.sendDynamicText(1, "Test 123 CO₂ öäü߀") // row 1 end ``` @@ -435,7 +435,7 @@ rule "Flicker output 1 when window opens" when Item M10_BinarySensor5 changed to OPEN then - val actions = getActions("lcn","lcn:module:b827ebfea4bb:17B4196847") + val actions = getActions("lcn","lcn:module:b827ebfea4bb:S000M010") // output=1, depth=2=100%, ramp=0=2s, count=3 actions.flickerOutput(1, 2, 0, 3) end @@ -475,73 +475,73 @@ Config .items ``` // Dimmer Outputs -Dimmer M10_Output1 {channel="lcn:module:b827ebfea4bb:17B4196847:output#1"} -Switch M10_Output2 {channel="lcn:module:b827ebfea4bb:17B4196847:output#2"[profile="lcn:output", ramp=0.25]} // with ramp of 0.25s (smallest value) -Dimmer M10_Output3 {channel="lcn:module:b827ebfea4bb:17B4196847:output#3"[profile="lcn:output", ramp=486]} // with ramp of 486s (biggest value) +Dimmer M10_Output1 {channel="lcn:module:b827ebfea4bb:S000M010:output#1"} +Switch M10_Output2 {channel="lcn:module:b827ebfea4bb:S000M010:output#2"[profile="lcn:output", ramp=0.25]} // with ramp of 0.25s (smallest value) +Dimmer M10_Output3 {channel="lcn:module:b827ebfea4bb:S000M010:output#3"[profile="lcn:output", ramp=486]} // with ramp of 486s (biggest value) // Dimmer Outputs: Control all simultaneously. Status of Output 1 is visualized. -Dimmer M10_OutputAll1 {channel="lcn:module:b827ebfea4bb:17B4196847:output#1"[profile="lcn:output", controlAllOutputs=true, ramp=0]} // ramp=0: only since firmware 180501 -Dimmer M10_OutputAll2 {channel="lcn:module:b827ebfea4bb:17B4196847:output#1"[profile="lcn:output", controlAllOutputs=true, ramp=0.25]} // ramp=0.25: compatibility: all firmwares -Dimmer M10_OutputAll3 {channel="lcn:module:b827ebfea4bb:17B4196847:output#1"[profile="lcn:output", controlAllOutputs=true, ramp=0.5]} // ramp>=0.5: only since firmware 180501 +Dimmer M10_OutputAll1 {channel="lcn:module:b827ebfea4bb:S000M010:output#1"[profile="lcn:output", controlAllOutputs=true, ramp=0]} // ramp=0: only since firmware 180501 +Dimmer M10_OutputAll2 {channel="lcn:module:b827ebfea4bb:S000M010:output#1"[profile="lcn:output", controlAllOutputs=true, ramp=0.25]} // ramp=0.25: compatibility: all firmwares +Dimmer M10_OutputAll3 {channel="lcn:module:b827ebfea4bb:S000M010:output#1"[profile="lcn:output", controlAllOutputs=true, ramp=0.5]} // ramp>=0.5: only since firmware 180501 // Dimmer Outputs: Control outputs 1+2 simultaneously. Status of Output 1 is visualized. Only ramps of 0s or 0.25s are supported. -Dimmer M10_Outputs12b {channel="lcn:module:b827ebfea4bb:17B4196847:output#1"[profile="lcn:output", controlOutputs12=true, ramp=0.25]} +Dimmer M10_Outputs12b {channel="lcn:module:b827ebfea4bb:S000M010:output#1"[profile="lcn:output", controlOutputs12=true, ramp=0.25]} // Dimmer Outputs: RGB Control -Color M10_Color {channel="lcn:module:b827ebfea4bb:17B4196847:output#color"[profile="lcn:output"]} +Color M10_Color {channel="lcn:module:b827ebfea4bb:S000M010:output#color"[profile="lcn:output"]} // Roller Shutter on Output 1+2 -Rollershutter M10_RollershutterOutput1 {channel="lcn:module:b827ebfea4bb:17B4196847:rollershutteroutput#1"} +Rollershutter M10_RollershutterOutput1 {channel="lcn:module:b827ebfea4bb:S000M010:rollershutteroutput#1"} // Relays -Switch M10_Relay1 {channel="lcn:module:b827ebfea4bb:17B4196847:relay#1"} +Switch M10_Relay1 {channel="lcn:module:b827ebfea4bb:S000M010:relay#1"} // Roller Shutter on Relays 1+2 -Rollershutter M10_RollershutterRelay1 {channel="lcn:module:b827ebfea4bb:17B4196847:rollershutterrelay#1"} +Rollershutter M10_RollershutterRelay1 {channel="lcn:module:b827ebfea4bb:S000M010:rollershutterrelay#1"} // LEDs -String M10_LED1 {channel="lcn:module:b827ebfea4bb:17B4196847:led#1"} -String M10_LED2 {channel="lcn:module:b827ebfea4bb:17B4196847:led#2"} +String M10_LED1 {channel="lcn:module:b827ebfea4bb:S000M010:led#1"} +String M10_LED2 {channel="lcn:module:b827ebfea4bb:S000M010:led#2"} // Logic Operations (legacy name: "Sums") -String M10_Logic1 {channel="lcn:module:b827ebfea4bb:17B4196847:logic#1"} -String M10_Logic2 {channel="lcn:module:b827ebfea4bb:17B4196847:logic#2"[profile="transform:MAP", function="alertSystem.map"]} +String M10_Logic1 {channel="lcn:module:b827ebfea4bb:S000M010:logic#1"} +String M10_Logic2 {channel="lcn:module:b827ebfea4bb:S000M010:logic#2"[profile="transform:MAP", function="alertSystem.map"]} // conf/transform/alertSystem.map: // NOT=All windows are closed // OR=Some windows are open // AND=All windows are open // Binary Sensors -Contact M10_BinarySensor1 {channel="lcn:module:b827ebfea4bb:17B4196847:binarysensor#1"} +Contact M10_BinarySensor1 {channel="lcn:module:b827ebfea4bb:S000M010:binarysensor#1"} // Variables // The units of the variables must also be set in the Channels configuration, to be visualized correctly. -Number:Temperature M10_Variable1 "[%.1f %unit%]" {channel="lcn:module:b827ebfea4bb:17B4196847:variable#1"} // Temperature in °C -Number:Temperature M10_Variable2 "[%.1f °F]" {channel="lcn:module:b827ebfea4bb:17B4196847:variable#2"} // Temperature in °F -Number M10_Variable3 "[%d ppm]" {channel="lcn:module:b827ebfea4bb:17B4196847:variable#3"} // Indoor air quality in ppm -Number M10_Variable4 "[%d lx]" {channel="lcn:module:b827ebfea4bb:17B4196847:variable#4"} // Illuminance in Lux -Number:Illuminance M10_Variable5 "[%.1f klx]" {channel="lcn:module:b827ebfea4bb:17B4196847:variable#5"} // Illuminance in kLux -Number M10_Variable6 "[%.1f mA]" {channel="lcn:module:b827ebfea4bb:17B4196847:variable#6"} // Electrical current in mA -Number M10_Variable7 "[%.1f V]" {channel="lcn:module:b827ebfea4bb:17B4196847:variable#7"} // Voltage in V -Number M10_Variable8 "[%.1f m/s]" {channel="lcn:module:b827ebfea4bb:17B4196847:variable#8"} // Wind speed in m/s -Number M10_Variable9 "[%.1f °]" {channel="lcn:module:b827ebfea4bb:17B4196847:variable#9"} // position of the sun (azimuth or elevation) in ° -Number M10_Variable10 "[%d W]" {channel="lcn:module:b827ebfea4bb:17B4196847:variable#10"} // Current power of an S0 input in W -Number:Power M10_Variable11 "[%.1f kW]" {channel="lcn:module:b827ebfea4bb:17B4196847:variable#11"} // Current power of an S0 input in kW +Number:Temperature M10_Variable1 "[%.1f %unit%]" {channel="lcn:module:b827ebfea4bb:S000M010:variable#1"} // Temperature in °C +Number:Temperature M10_Variable2 "[%.1f °F]" {channel="lcn:module:b827ebfea4bb:S000M010:variable#2"} // Temperature in °F +Number M10_Variable3 "[%d ppm]" {channel="lcn:module:b827ebfea4bb:S000M010:variable#3"} // Indoor air quality in ppm +Number M10_Variable4 "[%d lx]" {channel="lcn:module:b827ebfea4bb:S000M010:variable#4"} // Illuminance in Lux +Number:Illuminance M10_Variable5 "[%.1f klx]" {channel="lcn:module:b827ebfea4bb:S000M010:variable#5"} // Illuminance in kLux +Number M10_Variable6 "[%.1f mA]" {channel="lcn:module:b827ebfea4bb:S000M010:variable#6"} // Electrical current in mA +Number M10_Variable7 "[%.1f V]" {channel="lcn:module:b827ebfea4bb:S000M010:variable#7"} // Voltage in V +Number M10_Variable8 "[%.1f m/s]" {channel="lcn:module:b827ebfea4bb:S000M010:variable#8"} // Wind speed in m/s +Number M10_Variable9 "[%.1f °]" {channel="lcn:module:b827ebfea4bb:S000M010:variable#9"} // position of the sun (azimuth or elevation) in ° +Number M10_Variable10 "[%d W]" {channel="lcn:module:b827ebfea4bb:S000M010:variable#10"} // Current power of an S0 input in W +Number:Power M10_Variable11 "[%.1f kW]" {channel="lcn:module:b827ebfea4bb:S000M010:variable#11"} // Current power of an S0 input in kW // Regulators -Number:Temperature M10_R1VarSetpoint "[%.1f %unit%]" {channel="lcn:module:b827ebfea4bb:17B4196847:rvarsetpoint#1"} // Temperature in °C -Switch M10_R1VarLock {channel="lcn:module:b827ebfea4bb:17B4196847:rvarlock#1"} // Lock state of R1Var +Number:Temperature M10_R1VarSetpoint "[%.1f %unit%]" {channel="lcn:module:b827ebfea4bb:S000M010:rvarsetpoint#1"} // Temperature in °C +Switch M10_R1VarLock {channel="lcn:module:b827ebfea4bb:S000M010:rvarlock#1"} // Lock state of R1Var // Thresholds -Number:Temperature M10_ThresholdRegister1_Threshold1 "[%.1f %unit%]" {channel="lcn:module:b827ebfea4bb:17B4196847:thresholdregister1#1"} // Temperature in °C -Number:Temperature M10_ThresholdRegister4_Threshold2 "[%.1f %unit%]" {channel="lcn:module:b827ebfea4bb:17B4196847:thresholdregister4#2"} // Temperature in °C +Number:Temperature M10_ThresholdRegister1_Threshold1 "[%.1f %unit%]" {channel="lcn:module:b827ebfea4bb:S000M010:thresholdregister1#1"} // Temperature in °C +Number:Temperature M10_ThresholdRegister4_Threshold2 "[%.1f %unit%]" {channel="lcn:module:b827ebfea4bb:S000M010:thresholdregister4#2"} // Temperature in °C // S0 Counters -Number:Energy M10_S0Counter1 "[%.1f kWh]" {channel="lcn:module:b827ebfea4bb:17B4196847:s0input#1"} +Number:Energy M10_S0Counter1 "[%.1f kWh]" {channel="lcn:module:b827ebfea4bb:S000M010:s0input#1"} // Key Locks -Switch M10_KeyLockA1 {channel="lcn:module:b827ebfea4bb:17B4196847:keylocktablea#1"} -Switch M10_KeyLockD5 {channel="lcn:module:b827ebfea4bb:17B4196847:keylocktabled#5"} +Switch M10_KeyLockA1 {channel="lcn:module:b827ebfea4bb:S000M010:keylocktablea#1"} +Switch M10_KeyLockD5 {channel="lcn:module:b827ebfea4bb:S000M010:keylocktabled#5"} ``` Config .sitemap diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleDiscoveryService.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleDiscoveryService.java index 8ab89ce32297b..3de4a925a8af3 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleDiscoveryService.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleDiscoveryService.java @@ -153,8 +153,10 @@ protected void startScan() { ThingUID bridgeUid = localBridgeHandler.getThing().getUID(); String serialNumber = matcher.group("sn"); - ThingUID thingUid = new ThingUID(LcnBindingConstants.THING_TYPE_MODULE, bridgeUid, - serialNumber); + + String thingID = String.format("S%03dM%03d", addr.getSegmentId(), addr.getModuleId()); + + ThingUID thingUid = new ThingUID(LcnBindingConstants.THING_TYPE_MODULE, bridgeUid, thingID); Map properties = new HashMap<>(3); properties.put(SEGMENT_ID, addr.getSegmentId()); diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleHandler.java index 6fd07bb345161..be31b581abc37 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleHandler.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleHandler.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; +import java.util.regex.Matcher; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -68,6 +69,7 @@ public class LcnModuleHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(LcnModuleHandler.class); private static final Map CONVERTERS = new HashMap<>(); + private static final String SERIAL_NUMBER = "serialNumber"; private @Nullable LcnAddrMod moduleAddress; private final Map subHandlers = new HashMap<>(); private final List metadataSubHandlers = new ArrayList<>(); @@ -93,6 +95,30 @@ public void initialize() { LcnAddrMod localModuleAddress = moduleAddress = new LcnAddrMod(localConfig.segmentId, localConfig.moduleId); try { + + // Determine serial number of manually added modules + if (getThing().getThingTypeUID().equals(LcnBindingConstants.THING_TYPE_MODULE) + && getThing().getProperties().get(SERIAL_NUMBER).isEmpty()) { + PckGatewayHandler localBridgeHandler = getPckGatewayHandler(); + localBridgeHandler.registerPckListener(data -> { + Matcher matcher; + + if ((matcher = LcnModuleMetaFirmwareSubHandler.PATTERN.matcher(data)).matches()) { + if (isMyAddress(matcher.group("segId"), matcher.group("modId")) + && matcher.pattern() == LcnModuleMetaFirmwareSubHandler.PATTERN) { + String serialNumber = matcher.group("sn"); + updateProperty(SERIAL_NUMBER, serialNumber); + } + } + }); + synchronized (LcnModuleHandler.this) { + Connection connection = localBridgeHandler.getConnection(); + if (connection != null) { + connection.sendSerialNumberRequest(localModuleAddress); + } + } + } + // create sub handlers ModInfo info = getPckGatewayHandler().getModInfo(localModuleAddress); for (LcnChannelGroup type : LcnChannelGroup.values()) { 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 d35174ae235cd..23bc8941491fc 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 @@ -40,6 +40,10 @@ + + + + serialNumber