From ab44bd89cde49e0be33fe9716157a0f83d376328 Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Mon, 16 Mar 2020 23:41:22 +0100 Subject: [PATCH 01/16] [lcn] Add LCN binding Migrates the Local Control Network Binding from OH1 to OH2. Closes #108 Signed-off-by: Fabian Wolter --- CODEOWNERS | 1 + bom/openhab-addons/pom.xml | 5 + bundles/org.openhab.binding.lcn/.classpath | 32 + bundles/org.openhab.binding.lcn/.project | 23 + bundles/org.openhab.binding.lcn/NOTICE | 13 + bundles/org.openhab.binding.lcn/README.md | 597 ++++++++++++++ .../doc/LCN-PRO_output_steps.png | Bin 0 -> 200517 bytes .../org.openhab.binding.lcn/doc/dyn_text.png | Bin 0 -> 106065 bytes bundles/org.openhab.binding.lcn/doc/ir.png | Bin 0 -> 110156 bytes .../doc/module_discovery.png | Bin 0 -> 96808 bytes .../org.openhab.binding.lcn/doc/overview.jpg | Bin 0 -> 72598 bytes .../doc/pck_discovery.png | Bin 0 -> 66933 bytes bundles/org.openhab.binding.lcn/doc/unit.png | Bin 0 -> 94240 bytes bundles/org.openhab.binding.lcn/pom.xml | 17 + .../src/main/feature/feature.xml | 9 + .../lcn/internal/DimmerOutputProfile.java | 115 +++ .../lcn/internal/LcnBindingConstants.java | 43 + .../LcnChannelVariableConfiguration.java | 26 + .../lcn/internal/LcnGroupConfiguration.java | 25 + .../binding/lcn/internal/LcnGroupHandler.java | 55 ++ .../lcn/internal/LcnHandlerFactory.java | 75 ++ .../lcn/internal/LcnModuleActions.java | 192 +++++ .../lcn/internal/LcnModuleConfiguration.java | 26 + .../internal/LcnModuleDiscoveryService.java | 199 +++++ .../lcn/internal/LcnModuleHandler.java | 421 ++++++++++ .../lcn/internal/LcnProfileFactory.java | 71 ++ .../lcn/internal/PckGatewayConfiguration.java | 54 ++ .../lcn/internal/PckGatewayHandler.java | 297 +++++++ .../internal/common/DimmerOutputCommand.java | 65 ++ .../binding/lcn/internal/common/LcnAddr.java | 79 ++ .../lcn/internal/common/LcnAddrGrp.java | 102 +++ .../lcn/internal/common/LcnAddrMod.java | 102 +++ .../lcn/internal/common/LcnChannelGroup.java | 115 +++ .../binding/lcn/internal/common/LcnDefs.java | 185 +++++ .../lcn/internal/common/LcnException.java | 37 + .../internal/common/NullScheduledFuture.java | 80 ++ .../lcn/internal/common/PckGenerator.java | 779 ++++++++++++++++++ .../lcn/internal/common/ReverseNumber.java | 53 ++ .../binding/lcn/internal/common/Variable.java | 272 ++++++ .../lcn/internal/common/VariableValue.java | 99 +++ .../connection/AbstractConnectionState.java | 93 +++ ...bstractConnectionStateSendCredentials.java | 56 ++ .../internal/connection/AbstractState.java | 69 ++ .../lcn/internal/connection/Connection.java | 505 ++++++++++++ .../connection/ConnectionCallback.java | 44 + .../connection/ConnectionSettings.java | 159 ++++ .../connection/ConnectionStateConnected.java | 60 ++ .../connection/ConnectionStateConnecting.java | 105 +++ ...ectionStateGracePeriodBeforeReconnect.java | 53 ++ .../connection/ConnectionStateInit.java | 46 ++ .../ConnectionStateSegmentScan.java | 90 ++ .../ConnectionStateSendDimMode.java | 50 ++ .../ConnectionStateSendPassword.java | 43 + .../ConnectionStateSendUsername.java | 43 + .../connection/ConnectionStateShutdown.java | 48 ++ ...ConnectionStateWaitForLcnBusConnected.java | 70 ++ ...itForLcnBusConnectedAfterDisconnected.java | 36 + .../lcn/internal/connection/ModInfo.java | 501 +++++++++++ .../lcn/internal/connection/PckQueueItem.java | 75 ++ .../internal/connection/RequestStatus.java | 195 +++++ .../lcn/internal/connection/SendData.java | 41 + .../lcn/internal/connection/SendDataPck.java | 78 ++ .../connection/SendDataPlainText.java | 62 ++ .../lcn/internal/connection/StateContext.java | 66 ++ .../lcn/internal/connection/StateMachine.java | 109 +++ .../converter/AbstractS0Converter.java | 53 ++ .../AbstractVariableValueConverter.java | 113 +++ .../internal/converter/AngleConverter.java | 41 + .../lcn/internal/converter/Co2Converter.java | 41 + .../internal/converter/CurrentConverter.java | 41 + .../internal/converter/EnergyConverter.java | 36 + .../internal/converter/IdentityConverter.java | 62 ++ .../internal/converter/LightConverter.java | 47 ++ .../internal/converter/PowerConverter.java | 36 + .../converter/TemperatureConverter.java | 42 + .../internal/converter/VoltageConverter.java | 41 + .../converter/WindspeedConverter.java | 41 + .../internal/pchkdiscovery/ExtService.java | 39 + .../internal/pchkdiscovery/ExtServices.java | 33 + .../LcnPchkDiscoveryService.java | 160 ++++ .../lcn/internal/pchkdiscovery/Server.java | 73 ++ .../pchkdiscovery/ServicesResponse.java | 47 ++ .../lcn/internal/pchkdiscovery/Version.java | 43 + .../AbstractLcnModuleSubHandler.java | 267 ++++++ .../AbstractLcnModuleVariableSubHandler.java | 140 ++++ .../LcnModuleBinarySensorSubHandler.java | 62 ++ .../subhandler/LcnModuleCodeSubHandler.java | 108 +++ .../LcnModuleKeyLockTableSubHandler.java | 95 +++ .../subhandler/LcnModuleLedSubHandler.java | 66 ++ .../subhandler/LcnModuleLogicSubHandler.java | 126 +++ .../LcnModuleMetaAckSubHandler.java | 90 ++ .../LcnModuleMetaFirmwareSubHandler.java | 55 ++ .../subhandler/LcnModuleOutputSubHandler.java | 185 +++++ .../subhandler/LcnModuleRelaySubHandler.java | 86 ++ ...cnModuleRollershutterOutputSubHandler.java | 82 ++ ...LcnModuleRollershutterRelaySubHandler.java | 77 ++ .../LcnModuleRvarLockSubHandler.java | 66 ++ .../LcnModuleRvarSetpointSubHandler.java | 79 ++ .../LcnModuleS0CounterSubHandler.java | 58 ++ .../LcnModuleThresholdSubHandler.java | 104 +++ .../LcnModuleVariableSubHandler.java | 88 ++ .../resources/ESH-INF/binding/binding.xml | 10 + .../main/resources/ESH-INF/config/config.xml | 100 +++ .../resources/ESH-INF/i18n/lcn_de.properties | 171 ++++ .../resources/ESH-INF/thing/thing-types.xml | 637 ++++++++++++++ .../lcn/internal/ModuleActionsTest.java | 193 +++++ .../LcnPchkDiscoveryServiceTest.java | 58 ++ .../AbstractTestLcnModuleSubHandler.java | 43 + .../LcnModuleBinarySensorSubHandlerTest.java | 77 ++ .../LcnModuleKeyLockTableSubHandlerTest.java | 173 ++++ .../LcnModuleLedSubHandlerTest.java | 84 ++ .../LcnModuleLogicSubHandlerTest.java | 106 +++ .../LcnModuleOutputSubHandlerTest.java | 198 +++++ .../LcnModuleRelaySubHandlerTest.java | 114 +++ ...duleRollershutterOutputSubHandlerTest.java | 66 ++ ...oduleRollershutterRelaySubHandlerTest.java | 89 ++ .../LcnModuleRvarLockSubHandlerTest.java | 64 ++ .../LcnModuleRvarSetpointSubHandlerTest.java | 138 ++++ .../LcnModuleS0CounterSubHandlerTest.java | 57 ++ .../LcnModuleThresholdSubHandlerTest.java | 133 +++ .../LcnModuleVariableSubHandlerTest.java | 89 ++ bundles/org.openhab.binding.test/.classpath | 32 + bundles/org.openhab.binding.test/.project | 23 + bundles/org.openhab.binding.test/NOTICE | 13 + bundles/org.openhab.binding.test/README.md | 56 ++ bundles/org.openhab.binding.test/pom.xml | 16 + .../src/main/feature/feature.xml | 9 + .../test/internal/testBindingConstants.java | 34 + .../test/internal/testConfiguration.java | 26 + .../binding/test/internal/testHandler.java | 98 +++ .../test/internal/testHandlerFactory.java | 56 ++ .../resources/ESH-INF/binding/binding.xml | 10 + .../ESH-INF/i18n/test_xx_XX.properties | 17 + .../resources/ESH-INF/thing/thing-types.xml | 32 + bundles/pom.xml | 1 + 135 files changed, 12807 insertions(+) create mode 100644 bundles/org.openhab.binding.lcn/.classpath create mode 100644 bundles/org.openhab.binding.lcn/.project create mode 100644 bundles/org.openhab.binding.lcn/NOTICE create mode 100644 bundles/org.openhab.binding.lcn/README.md create mode 100644 bundles/org.openhab.binding.lcn/doc/LCN-PRO_output_steps.png create mode 100644 bundles/org.openhab.binding.lcn/doc/dyn_text.png create mode 100644 bundles/org.openhab.binding.lcn/doc/ir.png create mode 100644 bundles/org.openhab.binding.lcn/doc/module_discovery.png create mode 100644 bundles/org.openhab.binding.lcn/doc/overview.jpg create mode 100644 bundles/org.openhab.binding.lcn/doc/pck_discovery.png create mode 100644 bundles/org.openhab.binding.lcn/doc/unit.png create mode 100644 bundles/org.openhab.binding.lcn/pom.xml create mode 100644 bundles/org.openhab.binding.lcn/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/DimmerOutputProfile.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnBindingConstants.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnChannelVariableConfiguration.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnGroupConfiguration.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnGroupHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnHandlerFactory.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleConfiguration.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleDiscoveryService.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnProfileFactory.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayConfiguration.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/DimmerOutputCommand.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnAddr.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnAddrGrp.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnAddrMod.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnChannelGroup.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnDefs.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnException.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/NullScheduledFuture.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/PckGenerator.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/ReverseNumber.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/VariableValue.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionCallback.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionSettings.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnected.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendPassword.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendUsername.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateShutdown.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnectedAfterDisconnected.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/PckQueueItem.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/RequestStatus.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendData.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPck.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPlainText.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateContext.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateMachine.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractS0Converter.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractVariableValueConverter.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AngleConverter.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Co2Converter.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/CurrentConverter.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/EnergyConverter.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/IdentityConverter.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/LightConverter.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/PowerConverter.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/TemperatureConverter.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/VoltageConverter.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/WindspeedConverter.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtService.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtServices.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Server.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ServicesResponse.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Version.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleVariableSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleBinarySensorSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleCodeSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleKeyLockTableSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLedSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLogicSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleMetaAckSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleMetaFirmwareSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleOutputSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRelaySubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterOutputSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterRelaySubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarLockSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarSetpointSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleS0CounterSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleThresholdSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleVariableSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/binding/binding.xml create mode 100644 bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/config/config.xml create mode 100644 bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/i18n/lcn_de.properties create mode 100644 bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/thing/thing-types.xml create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/ModuleActionsTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryServiceTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/AbstractTestLcnModuleSubHandler.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleBinarySensorSubHandlerTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleKeyLockTableSubHandlerTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLedSubHandlerTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLogicSubHandlerTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleOutputSubHandlerTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRelaySubHandlerTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterOutputSubHandlerTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterRelaySubHandlerTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarLockSubHandlerTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarSetpointSubHandlerTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleS0CounterSubHandlerTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleThresholdSubHandlerTest.java create mode 100644 bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleVariableSubHandlerTest.java create mode 100644 bundles/org.openhab.binding.test/.classpath create mode 100644 bundles/org.openhab.binding.test/.project create mode 100644 bundles/org.openhab.binding.test/NOTICE create mode 100644 bundles/org.openhab.binding.test/README.md create mode 100644 bundles/org.openhab.binding.test/pom.xml create mode 100644 bundles/org.openhab.binding.test/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testBindingConstants.java create mode 100644 bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testConfiguration.java create mode 100644 bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandler.java create mode 100644 bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandlerFactory.java create mode 100644 bundles/org.openhab.binding.test/src/main/resources/ESH-INF/binding/binding.xml create mode 100644 bundles/org.openhab.binding.test/src/main/resources/ESH-INF/i18n/test_xx_XX.properties create mode 100644 bundles/org.openhab.binding.test/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/CODEOWNERS b/CODEOWNERS index c19b8b4d24cf4..0cb4b46d301b1 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -94,6 +94,7 @@ /bundles/org.openhab.binding.konnected/ @volfan6415 /bundles/org.openhab.binding.kostalinverter/ @cschneider /bundles/org.openhab.binding.lametrictime/ @syphr42 +/bundles/org.openhab.binding.lcn/ @fwolter /bundles/org.openhab.binding.leapmotion/ @kaikreuzer /bundles/org.openhab.binding.lghombot/ @FluBBaOfWard /bundles/org.openhab.binding.lgtvserial/ @fa2k diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index df3a28ecb3107..42d1b43afd348 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -466,6 +466,11 @@ org.openhab.binding.lametrictime ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.lcn + ${project.version} + org.openhab.addons.bundles org.openhab.binding.leapmotion diff --git a/bundles/org.openhab.binding.lcn/.classpath b/bundles/org.openhab.binding.lcn/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.lcn/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.lcn/.project b/bundles/org.openhab.binding.lcn/.project new file mode 100644 index 0000000000000..5302a4a4a785d --- /dev/null +++ b/bundles/org.openhab.binding.lcn/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.lcn + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/bundles/org.openhab.binding.lcn/NOTICE b/bundles/org.openhab.binding.lcn/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.binding.lcn/README.md b/bundles/org.openhab.binding.lcn/README.md new file mode 100644 index 0000000000000..f201154f033bb --- /dev/null +++ b/bundles/org.openhab.binding.lcn/README.md @@ -0,0 +1,597 @@ +# LCN Binding + +[Local Control Network (LCN)](http://www.lcn.eu) is a building automation system for small and very large installations. +It is capable of controlling lights, shutters, access control etc. and can process data from several sensor types. +It has been introduced in 1992. + +A broad range of glass key panels, displays, remote controls, sensors and in- and outputs exist. +The system can handle up to 30,000 bus members, called modules. +LCN modules are available for DIN rail and in-wall mounting and feature versatile interfaces. The bus modules and most of the accessories are developed, manufactured and assembled in Germany. + +Bus members are inter-connected via a free wire in the standard NYM cable. Wireless components are available, though. + +![Illustration of the LCN product family](doc/overview.jpg) + +This binding uses TCP/IP to access the LCN bus via the software LCN-PCHK (Windows/Linux) or the DIN rail device LCN-PKE. +**This means 1 unused LCN-PCHK license or a LCN-PKE is required** + +It is recommended to configure *Thing*s in [Paper UI](https://www.openhab.org/docs/configuration/paperui.html) and *Item*s in text files. +*Channel*s and *Link*s are configured automatically, then. + +## LCN Overview + +LCN modules and their connecting peripherals are explained in the following. + +### LCN Modules + +Active LCN components connected to the LCN bus are called *LCN modules*. +LCN modules are addressed by their numeric id: Valid range is 5..254 + +In larger buildings, a second topologic layer is added: *segments*. +Valid range is 5..128 or 0 (= no segments exist) or 3 (= target all segments) + +LCN modules within the **same** segment can be grouped: Valid range is 5..254 or 3 (= target all groups) + +### LCN Firmware Versions + +Each LCN module has a feature-set based on its firmware version. +This version is written as follows: \[year since 1990\]\[month\]\[day\] + +Each component is written in hexadecimal with 2 characters. Examples: + +- 090101 = 1. january 1990 +- 0D0C01 = 1. december 2003 +- 170206 = 6. feb. 2013 + +### LCN Dimmer Outputs + +LCN modules support 2 to 4 dimmer output ports (number depends on firmware version). +If the module hardware type doesn't feature physical dimmer outputs, the outputs can still be used as virtual. + +Status values are always in percent. +Modules since 170206 have a 0.5%-steps resolution. Older modules have a 2%-steps resolution. + +The time it takes the output port to reach its setpoint is called *ramp*. + +### LCN Variables + +LCN modules support: + +- 3 or 12 (since 170206) analog variables for general purpose +- 2 regulators with configurable setpoints +- 5 or 4x4 (since 170206) thresholds (trigger levels) +- 4 S0-input counters (since 170206, LCN-BU4L must be connected) + +### LCN Regulators (additions to variables) + +LCN modules have 2 regulators. +Each one has a setpoint and uses one variable as its value source. +A regulator can be locked, so that the target actuator keeps switched off, also if the value source is in control range. + +### LCN Thresholds + +LCN modules since firmware 170206 have 4 threshold registers. Each threshold register comprises 4 thresholds. + +A threshold register uses one variable as its value source (see [LCN Variables](#lcn-variables)). +Arbitrary LCN commands can be send into the bus, when the value-source falls below a threshold or exceeds one. +A threshold can be locked, so that the configured LCN command is not fired, also if the value source passes the threshold. + +### LCN Relays + +LCN modules support 8 relays. If no hardware relays are connected, the relays can still be used as virtual. + +### LCN Binary Sensors + +LCN modules support 8 binary sensors (e.g. motion detectors; hardware periphery must be connected). + +### LCN LEDs (legacy name: *lamps*) + +12x multi-state variables can be used for logic operations or visualization (hardware periphery must be connected). + +Values: OFF, ON, BLINK, FLICKER + +### LCN Logic Operations (legacy name: *sums*) + +4x multi-state variables each representing the result of a logic operation of the associated LEDs. + +Values: NOT (all LEDs off), OR (some LEDs on), AND (all LEDs on) + +### LCN Keys + +LCN keys are data-points in the module with bound commands. +LCN modules support 3 ("A-C") or 4 ("A-D") key-tables (number depends on firmware version). + +Each key-table holds 8 keys. Examples: A1, A7, D8 + +Each key has 3 command types: HIT(press), MAKE(long press), BREAK(long press release) + +These keys can be locked. The bound (LCN-)commands cannot be executed, then. + +### LCN Access Control & Remote Controls + +LCN can interface several transponder readers and finger print sensors, used for access control. + +Remote controls can not only be used for triggering commands, but also for access control, by evaluating the transmitted serial number. + +## Supported Things + +### Thing: LCN Module + +Any LCN module that should be controlled or visualized, need to be added to openHAB as a *Thing*. + +LCN modules with firmware versions 120612 (2008) and 170602 (2013) were tested with this binding. +No known features/changes that need special handling were added until now (2020). +Modules with older and newer firmware should work, too. +The module hardware types (e.g. LCN-SH, LCN-HU, LCN-UPP, ...) are compatible to each other and can therefore be handled all in the same way. + +### Thing: LCN PCK Gateway + +PCK is the protocol spoken over TCP/IP with a PCK gateway to communicate with the LCN bus. +Examples for PCK gateways are the *LCN-PCHK* software running on Windows or Linux and the DIN rail mounting device *LCN-PKE*. + +For each LCN bus, interfaced to openHAB, a PCK gateway needs to be added to openHAB as a *Thing*. + +Several PCK gateways can be added to openHAB to control multiple LCN busses in distinct locations. + +The minimum recommended version is LCN-PCHK 2.8 (older versions will also work, but lack some functionality). +Visit [https://www.lcn.eu](https://www.lcn.eu) for updates. + +### Thing: LCN Group + +LCN modules can be assigned to groups with the programming software *LCN-PRO*. + +To send commands to an LCN group, the group needs to be added to openHAB as a *Thing*. + +One LCN module within the group is used to represent the status of the whole group. +For example, when a Dimmer Output is controlled via a LCN group *Thing*, openHAB will always visualize the state of the Dimmer Output of the chosen module. The states of the other modules in the group are ignored for visualization. + +## Discovery + +### Discover LCN Modules + +Basic data of LCN modules can be read out automatically by openHAB. +Click on one of the check marks to add the LCN modules to openHAB: + +![Paper UI screenshot, showing the discovery result for LCN modules](doc/module_discovery.png) + +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. + +### Discover PCK Gateways + +PCK gateways in the LAN can be found automatically by openHAB. This is done by UDP multicast messages on port 4220. +The discovery works only if the firewall of the PCK gateway is not configured too strictly. +This means on Windows PCs, that the network must be configured as 'private' and not as 'public'. +Also, some network switches may block multicast packets. +Unfortunately, *LCN-PCHK* listens only on the first network interface of the computer for discovery packets. +If your PCK gateway has multiple network interfaces, *LCN-PCHK* may listen on the wrong interface and fails to respond to the discovery request. + +Discovery has successfully been tested with LCN-PCHK 3.2.2 running on a Raspberry Pi with Raspbian and openHAB running on Windows 10. +See the following screenshot: + +![Paper UI screenshot, showing the discovery result for PCK gateways](doc/pck_discovery.png) + +If discovery fails, you can add a PCK gateway manually. See [Thing: PCK Gateway](#thing-lcn-pck-gateway). + +Please be aware that you **have to configure** username, password and the dimmer output resolution also if you use discovery. +See [Thing: PCK Gateway](#thing-lcn-pck-gateway). + +When adding a PCK gateway by discovery, the new *Thing*'s UID is the MAC address of the device, running the PCK gateway. + +## Thing Configuration + +### Configure LCN-PCHK/-PKE Connection + +Click on the plus symbol under Configuration/Things in Paper UI. Select "Add manually". +When adding a PCK gateway manually, the *hostname/IP address*, *port* and *username* and *password* need to be configured. + +> **IMPORTANT:** You need to configure the dimmer output resolution. This setting is valid for the **whole** LCN bus.
+The setting is either 0-50 steps or 0-200 steps. +It **has to be the same** as in the parameterizing software **LCN-PRO** under Options/Settings/Expert Settings. +See the following screenshot. + +![LCN-PRO screenshot, showing the 50 or 200 steps for the dimmer outputs](doc/LCN-PRO_output_steps.png) + +When using a wrong dimmer output setting, dimming the outputs will result in unintended behavior. + +### Add LCN Module Manually + +Click on the plus symbol under Configuration/Things in Paper UI. Select "Add manually". +Now you can configure the *module ID* and the *segment ID* (0 if no segments are used). +After confirming, the new module should show up under Configuration/Things. + +openHAB's discovery function can be used to add LCN modules automatically. +See [Discover LCN Modules](#discover-lcn-modules). + +### Add LCN Group + +Click on the plus symbol under Configuration/Things in Paper UI. Select "Add manually". +Now you can configure the *group number*, as previously configured in the programming software *LCN-PRO*. +If you use segments, you need to configure the segment ID of the modules within the group. Enter 0, if you have no segments. +Lastly, you have to configure the *module ID* of any module within the group. +This module will be used for visualization, as representative for all modules within the group. + +## Supported LCN Features and openHAB Channels + +The following table lists all features of LCN and their mappings to openHAB *Channel*s. + +Although, there are many **Not implemented** entries, the vast majority of LCN features can be used with openHAB:
+If a special command is needed, the [Hit Key](#hit-key) action (German: "Sende Taste") can be used to hit a module's key virtually and execute an arbitrary command. + +| LCN Feature (English) | LCN Feature (German) | Channel | IDs | Type | Description | +|---------------------------------|----------------------------------|------------------------|------|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------| +| Dimmer Output Control Single | Ausgang | output | 1-4 | Dimmer, Switch | Sets the dimming value of an output with a given ramp. | +| Relay | Relais | relay | 1-8 | Switch | Controls a relay and visualizes its state. | +| Visualize Binary Sensor | Binärsensor anzeigen | binarysensor | 1-8 | Contact | Visualizes the state of a binary sensor. | +| LED Control | LED-Steuerung | led | 1-12 | Text (ON, OFF, BLINK, FLICKER) | Controls an LED and visualizes its current state. | +| Visualize Logic Operations | Logik Funktion anzeigen | logic | 1-4 | Text (NOT, OR, AND) | Visualizes the result of the logic operation. | +| Motor/Shutter on Dimmer Outputs | Motor/Rollladen an Ausgängen | rollershutteroutput | 1-4 | Rollershutter | Control roller shutters on dimmer outputs | +| Motor/Shutter on Relays | Motor/Rollladen an Relais | rollershutterrelay | 1-4 | Rollershutter | Control roller shutters on relays | +| Variables | Variable anzeigen | variable | 1-12 | Number | Sets and visualizes the value of a variable. | +| Regulator Set Setpoint | Regler Sollwert ändern | rvarsetpoint | 1-2 | Number | Sets and visualizes the setpoint of a regulator. | +| Regulator Lock | Regler sperren | rvarlock | 1-2 | Switch | Locks a regulator and visualizes its locking state. | +| Set Thresholds in Register 1 | Schwellwert in Register 1 ändern | thresholdregister1 | 1-4 | Number | Sets and visualizes a threshold in the given threshold register. | +| Set Thresholds in Register 2 | Schwellwert in Register 2 ändern | thresholdregister2 | 1-4 | Number | Sets and visualizes a threshold in the given threshold register. | +| Set Thresholds in Register 3 | Schwellwert in Register 3 ändern | thresholdregister3 | 1-4 | Number | Sets and visualizes a threshold in the given threshold register. | +| Set Thresholds in Register 4 | Schwellwert in Register 4 ändern | thresholdregister4 | 1-4 | Number | Sets and visualizes a threshold in the given threshold register. | +| Visualize S0 Counters | S0-Zähler anzeigen | s0input | 1-4 | Number | Visualizes the value of a S0 counter. | +| Lock Keys Table A | Sperre Tastentabelle A | keylocktablea | 1-8 | Switch | Locks a key on the given key table and visualizes its state. | +| Lock Keys Table B | Sperre Tastentabelle B | keylocktableb | 1-8 | Switch | Locks a key on the given key table and visualizes its state. | +| Lock Keys Table C | Sperre Tastentabelle C | keylocktablec | 1-8 | Switch | Locks a key on the given key table and visualizes its state. | +| Lock Keys Table D | Sperre Tastentabelle D | keylocktabled | 1-8 | Switch | Locks a key on the given key table and visualizes its state. | +| Dimmer Output Flicker | Ausgang: Flackern | N/A | N/A | N/A | Action "flickerOutput": Let a dimmer output flicker for a given count of flashes. | +| Dynamic Text | Dynamischer Text | N/A | N/A | N/A | Action: "sendDynamicText": Sends custom text to an LCN-GTxD display. | +| 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 | +| 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 | +| Status Message | Statusmeldungen | - | - | - | Automatically done by OpenHAB Binding | +| Audio Beep | Audio Piepen | - | - | - | Not implemented | +| Audio LCN-MRS | Audio LCN-MRS | - | - | - | Not implemented | +| Count/Compute | Zählen/Rechnen | - | - | - | Not implemented | +| DALI | DALI | - | - | - | Not implemented | +| Dimmer Output Memory Toggle | Ausgang: Memory Taster | - | - | - | Not implemented | +| Dimmer Output Ramp Stop | Ausgang: Rampe Stop | - | - | - | Not implemented | +| Dimmer Output Relative | Ausgang: Relativ | - | - | - | Not implemented | +| Dimmer Output Stairway | Ausgang: Treppenhauslicht | - | - | - | Not implemented | +| Dimmer Output Timer | Ausgang: Timer (Kurzzeit) | - | - | - | Not implemented | +| Display Set Language | Display-Sprache setzen | - | - | - | Not implemented | +| Dynamic Groups | Dynamische Gruppen | - | - | - | Not implemented | +| Free Input | Freie Eingabe | - | - | - | Not implemented | +| LED Brightness | LED-Helligkeit | - | - | - | Not implemented | +| LED Test | LED-Test | - | - | - | Not implemented | +| LED Transform | LED-Umwandlung | - | - | - | Not implemented | +| Light Scenes | Lichtszenen | - | - | - | Not implemented | +| Lock Keys by Time (Table A) | Sperre (Zeit) Tasten (Tabelle A) | - | - | - | Not implemented | +| Lock Outputs by Time | Sperre (Zeit) Ausgänge | - | - | - | Not implemented | +| Lock Relays | Sperre Relais | - | - | - | Not implemented | +| Lock Thresholds | Sperre Schwellwerte | - | - | - | Not implemented | +| Motor Position | Motor Position | - | - | - | Not implemented | +| Relay Timer | Relais-Timer | - | - | - | Not implemented | +| Send Keys Delayed | Sende Tasten verzögert | - | - | - | Not implemented | +| Set S0 Counters | S0-Zähler setzen | - | - | - | Not implemented | +| Status Command | Statuskommandos | - | - | - | Not implemented | + +**For some *Channel*s a unit should be configured for visualization.** By default the native LCN value is used. + + + +![Screenshot, showing the Channel configuration](doc/unit.png) + +### Transponder + +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: + +``` +rule "My Transponder" +when + Channel "lcn:module:b827ebfea4bb:17B308349E:code#transponder" triggered "12ABCD" +then + M10_Relay7.sendCommand(ON) +end +``` + +### 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*: + +![Screenshot, showing the I-port properties for remote controls](doc/ir.png) + +#### Remote Control Keys + +The trigger *Channel* `lcn:module:::code#remotecontrolkey` can be used to execute commands, when a specific key on a remote control is pressed: + +``` +rule "Remote Control Key 3 on Layer 1 hit" +when + Channel "lcn:module:b827ebfea4bb:17B3073D6A:code#remotecontrolkey" triggered "A3:HIT" +then + M10_Relay7.sendCommand(ON) +end +``` + +`A3` is key 3 on the first layer. `B1` is key 1 on the second layer etc.. After the colon follows the LCN "hit type" HIT, MAKE or BREAK (German: kurz, lang, los). + +#### Remote Control used as Access Control + +The serial number of a remote control can be used for access control via the channel `lcn:module:::code#remotecontrolcode`. See the following example: + +``` +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" +then + M10_Relay7.sendCommand(ON) +end +``` + +The command will be executed when the remote control button A3 is either pressed short or long. + +## Dimmer Outputs with Ramp and Multiple Outputs + +The *output* profile can be used to control multiple dimmer outputs of the *same* module simultaneously or control a dimmer output with a ramp (slowly dimming). + +The optional *ramp* parameter must be float or integer. +The lowest value is 0.25, which corresponds to 0.25s. The highest value is 486s. +When no *ramp* parameter is specified or no profile is configured, the ramp is 0 (behavior like a switch). +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) +// 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) +``` + +The optional parameters *controlAllOutputs* and *controlOutputs12* can be used to control multiple outputs simultaneously. +Please note that the combination of these parameters with the *ramp* parameter is limited: + +``` +// 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]} +// 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 +``` + +## Actions + +Actions are special commands that can be sent to LCN modules or LCN groups. + +### Hit Key + +This *Action* virtually hits a key of a key table in an LCN module. +Simply spoken, OpenHab acts as a push button switch connected to an LCN module. + +This *Action* can be used to execute commands which are not natively supported by this binding. +The function can be programmed via the software *LCN-PRO* onto a key in a module's key table. +Then, the programmed key can be "hit" by this *Action* and the command will be executed. + +When programming a "Hit Key" *Action*, the following parameters need to be set: + +*table* - The module's key table: A, B, C or D
+*key* - The number of the key within the key table: 1-8
+*action* - The key's action: HIT (German: "kurz"), MAKE ("lang") or BREAK ("los") + +``` +rule "Hit key C4 hourly" +when + Time cron "0 0 * * * ?" +then + val actions = getActions("lcn","lcn:module:b827ebfea4bb:17B4196847") + actions.hitKey("C", 4, "HIT") +end +``` + +### Dynamic Text + +This *Action* can be used to send custom texts to an LCN-GTxD display. +To make this function work, the row of the display has to be configured to allow dynamic text within *LCN-PRO*: + +![Screenshot of LCN-PRO, showing the dynamic text setting of an LCN-GT10D](doc/dyn_text.png) + +When programming a "Dynamic Text" *Action*, the following parameters need to be set: + +*row* - The number of the row in the display: 1-4
+*text* - The text to be displayed (UTF-8) + +The length of the text may not exceed 60 bytes of characters. +Bear in mind that unicode characters can take more than one byte (e.g. umlauts (äöü) take two bytes). + +``` +rule "Send dynamic Text to GT10D hourly" +when + Time cron "0 0 * * * ?" +then + val actions = getActions("lcn","lcn:module:b827ebfea4bb:17B3073D6A") + actions.sendDynamicText(1, "Test 123 CO₂ öäü߀") // row 1 +end +``` + +### Flicker Output + +This *Action* realizes the LCN command "Output: Flicker" (German: "Ausgang: Flackern"). +The command let a dimmer output flash a given number of times. This feature can be used e.g. for alert signals or visual door bells. + +When programming a "Flicker Output" *Action*, the following parameters need to be set: + +*output* - The dimmer output number: 1-4
+*depth* - The depth of the flickering: 0-2 (0=25% 1=50% 2=100% Example: When the output is fully on (100%), and 0 is selected, flashes will dim from 100% to 75% and back)
+*ramp* - The duration/ramp of one flash: 0-2 (0=2sec 1=1sec 2=0.5sec)
+*count* - The number of flashes: 1-15 + +This action has also effect, if the given output is off. The output will be dimmed from 0% to *depth* and back, then. + +``` +rule "Flicker output 1 when window opens" +when + Item M10_BinarySensor5 changed to OPEN +then + val actions = getActions("lcn","lcn:module:b827ebfea4bb:17B4196847") + // output=1, depth=2=100%, ramp=0=2s, count=3 + actions.flickerOutput(1, 2, 0, 3) +end +``` + +## Caveat and Limitations + +LCN segments are supported by this binding, but could not be tested, due to lack of hardware. + +LEDs do not support the *OnOffCommand* and respectively the *Switch* Item type, because they have the additional states *BLINK* and *FLICKER*. They must be configured as *String* Item. When used in rules, the parameter must be of type string. Example: `M10_LED1.sendCommand("ON")`. Note the quotation marks. + +## Full Example + +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 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 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 Outputs: RGB Control +Color M10_Color {channel="lcn:module:b827ebfea4bb:17B4196847:output#color"[profile="lcn:output"]} + +// Roller Shutter on Output 1+2 +Rollershutter M10_RollershutterOutput1 {channel="lcn:module:b827ebfea4bb:17B4196847:rollershutteroutput#1"} + +// Relays +Switch M10_Relay1 {channel="lcn:module:b827ebfea4bb:17B4196847:relay#1"} + +// Roller Shutter on Relays 1+2 +Rollershutter M10_RollershutterRelay1 {channel="lcn:module:b827ebfea4bb:17B4196847:rollershutterrelay#1"} + +// LEDs +String M10_LED1 {channel="lcn:module:b827ebfea4bb:17B4196847:led#1"} +String M10_LED2 {channel="lcn:module:b827ebfea4bb:17B4196847: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"]} +// 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"} + +// 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 + +// 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 + +// 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 + +// S0 Counters +Number:Energy M10_S0Counter1 "[%.1f kWh]" {channel="lcn:module:b827ebfea4bb:17B4196847:s0input#1"} + +// Key Locks +Switch M10_KeyLockA1 {channel="lcn:module:b827ebfea4bb:17B4196847:keylocktablea#1"} +Switch M10_KeyLockD5 {channel="lcn:module:b827ebfea4bb:17B4196847:keylocktabled#5"} +``` + +Config .sitemap + +``` +sitemap lcn label="My home automation" { + Frame label="Demo Items" { + // Dimmer Outputs + Default item=M10_Output1 label="Output 1" + Default item=M10_Output2 label="Output 2" + Default item=M10_Output3 label="Output 3" + + // Dimmer Outputs: Control all simultaneously. Status of Output 1 is visualized. + Default item=M10_OutputAll1 label="All Outputs ramp=0 since firmware 180501" + Default item=M10_OutputAll2 label="All Outputs ramp=250ms all firmwares" + Default item=M10_OutputAll3 label="All Outputs ramp>=500ms 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. + Default item=M10_Outputs12a label="Outputs 1+2 Ramp=0" + Default item=M10_Outputs12b label="Outputs 1+2 Ramp=0.25s" + + // Dimmer Outputs: RGB Control + Colorpicker item=M10_Color + + // Roller Shutter on Outputs 1+2 + Default item=M10_RollershutterOutput1 label="Roller Shutter on Output 1+2" + + // Relays + Default item=M10_Relay1 label="Relay 1" + + // Roller Shutter on Relays + Default item=M10_RollershutterRelay1 label="Roller Shutter on Relay 1-2" + + // LEDs + Switch item=M10_LED1 label="LED 1" mappings=[ON=ON, OFF=OFF] // Don't display "Blink" or "Flicker" + Switch item=M10_LED2 label="LED 2" + + // Logic Operations (legacy name: "Sums") + Default item=M10_Logic1 label="Logic Operation 1" + Default item=M10_Logic2 label="Logic Operation 2" + + // Binary Sensors + Default item=M10_BinarySensor1 label="Binary Sensor 1" + + // Variables + Setpoint item=M10_Variable1 label="Variable 1" + Default item=M10_Variable2 label="Variable 2" + Default item=M10_Variable3 label="Variable 3" + Default item=M10_Variable4 label="Variable 4" + Default item=M10_Variable5 label="Variable 5" + Default item=M10_Variable6 label="Variable 6" + Default item=M10_Variable7 label="Variable 7" + Default item=M10_Variable8 label="Variable 8" + Default item=M10_Variable9 label="Variable 9" + Default item=M10_Variable10 label="Variable 10" + Default item=M10_Variable11 label="Variable 11" + + // Regulators + Setpoint item=M10_R1VarSetpoint label="R1Var Setpoint" step=1 minValue=-10.0 + Default item=M10_R1VarLock label="R1Var Lock" // Lock state of R1Var + + // Thresholds + Setpoint item=M10_ThresholdRegister1_Threshold1 label="Threshold Register 1 Threshold 1" + Setpoint item=M10_ThresholdRegister4_Threshold2 label="Threshold Register 4 Threshold 2" + + // S0 Counters + Default item=M10_S0Counter1 label="S0 Counter 1" + + // Key Locks + Default item=M10_KeyLockA1 label="Locked State Key A1" + Default item=M10_KeyLockD5 label="Locked State Key D5" + } +} +``` diff --git a/bundles/org.openhab.binding.lcn/doc/LCN-PRO_output_steps.png b/bundles/org.openhab.binding.lcn/doc/LCN-PRO_output_steps.png new file mode 100644 index 0000000000000000000000000000000000000000..d86eaabe932fa6888deb4a2d95afa3fd8253570c GIT binary patch literal 200517 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 z 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�z+DlDaSy^T{D5kk71ssku;(viu@{A9A=^a%BZDUI%$*fxz(@vDz9I+=NJ=RzsTVwAn!9} ziN5~@&t*IlkoR{Cb`OjYo}~TW8tY zaLBuIY-J)}m@)I9PtH}*ub)M=;VMKc_?E`HWU*6ZHJfxVewbf@q_=YY)0)b5d?Xt1 z9AwwelHF8|0|*)lG!QG{=8|b9mIc*t=G8bjIMn8G)F4$Njk2@^TUzdXN-@(cm3@N@ zqdm(RCZE?~XPoHfJvz46dSYUtLtEuUTXO%1pebv`x z7ah*iD^ohEuJKQy4(Na<*d24t7+8qG)MXpajERo~zhi?oU+f)63G^;Q1_`qc#lk#< z@Tpke-940W(-AJKagzRvP!6RFYcta7i1=UoMY zd`?msm>^9^PbZAF;gTwEZ{TP`Y?7zWB)AKP zFW%$tH!YwZj98*&6#PNN^*LZ@P!p(_ONh78uuK>gDlaXAvZ$iAe+3(}bndu27uLj$ zXlKj)Njpfyr+WgV8~F@W(itosyH9oNn|rgN|m2L9Mo^7!HNy z*_!)zniLULkJJ%6j$o}mV|$<7fs6eHA58@Na1`W4qSw2TY}RS9lhYqAKfPq|nPWPs zm3QU4wc*EoI9fqco7&7bdG48i*q?O$Qyl z7?#&ad4~naEqs_gfOX30Ib_))|H!4?@D6vo& zFvTGep+aCVnh$b+U_}mwlYEvqmLvMwan!+?u{^IIxBsdqp8m6&#xRnpAfRG|Lsd=2 z%F4Q|ve+%2FS5mwFP?;x_|@2vQt^0T66qXDx1Q*F!NxK+Xl-S{ z72aV`H4^qad~9xSLJdbx8|@G2O}BtBUgq3h9Df4?r;2dS-rYiNCNPm0kS&BF;&{nx>uK~5KrjV0 zO<-jigG)Zn@>EafpFmOLtN>gEY>;_Ll$5B_ts`Hc9`nA~_c}Nu@VLYwdrUn>%L)v| zGBh#dAAr`PlAn=we=tSEib{j(!)Sp74Y|Vw;9MB8X`YqgSGQika}3-UarGV9Xi>z_WaZ4REMCXr7}J!SDr%l zg6+L!@jxOxayZfP163Geu!QDhU^I)d;z#&YHZ-;OLRb4%CC+%dhse{#HJ+Y!V^b*gW7@~r*ezxZ=;7T{Hilrf zO^XN&KTlgu_PU@of_7)pYoqiz2Xb>q#l zYs%R5EGwxT&_gIGQyPSz(7NI1qC=w)7EY%&$4@=Er_-BK{5M&u7qesQ77n-}LJU26 zqpZgz&=S^Icwqd1h7&}0ite~g(m{|_#KDoa9XUWocw0Gr;hhm^F;$q&gZ?r6IwaIuXS61FGht2aL%RD`OJPKl$-naN#Q4Y&I=qh#y zO0aFk@1I7v*_K?l5|y>v1$%<*(1*>k$Z6b8$uhD{l;PO1#3ISFCY-nK&Go&{H(yD% zlZ#q!1?>H9Mrd=649k*!IzPZ0nj4mU1ln_E;yVoC#SFB$F>iPtfww((R87c%MGqXl z_k*31g|bs~31d@dwaM~h!Mlh=f5NY@V8I zzP&d(0pD~M3UQZ$+V%~mC5#fHW~iT2jWa3p9+5K|!+*=;`E-zjkQqbt!|4RPXbY`q z0uv7&d0Yy}Z91&HMQq#=F%}1E>&6i8peS3>ahNw!a=kJ4Xdiw4I-=pnLRRLF#i!8KhyW-h z3Yu>N$xdQb#uJC$A9s_S^w#q1W&AhLi-&rvPefumrmq{?H|t?LB+nNA#I(ZNWPA5Y z;QjbIK|wKO*-e$dEEvdPBEL_Pvo9o_dtbg+4n??UN{W%Dxr;#+A2uhlXGoXblam4m z!zx$yb}70}**OXStc%h?DX|Z9!-V*Fd5fg(_!ulaeP9UM9bJ&jX?yMM4Z7>UNWOT9 zvGaNx#?j&sR+Md3;+NA^kO!SVH41SHB^KZ+6c88mMHIta8!vnVidc^<||Nz<0m=f=SKJ z^i2l~7K6TGwwWFgzN=c$<>t$r3HOe7^cc*s$NTd8rSCspZpuHA3Pyc+o7Eum5ryM1 z*Xg6P9^WEIbdH5V3E<(MwB1*!m#1yd)e}TD2$+pgVXAbfiE2eOwA3yuowj?;An^JQ zZa_**CK<{5^aii`q)nIR>D4gT$T0JfG}8s}-1^G#scc?b1?FByj5s1>*@;-`pDQ7r z-s@Bs8SfB;PsfDr1;vxU^IVF+s#DH!EJcvUL2QzoXP(z%mk(Z2Yh15>>{qiK7c5PR z6nZ&3x3tK>fT+d?TbuBO&iYm|1pI@Vl|8e#JbEzSYeM#GJLKQsld`!?Ul_i2T&D(LesjD{{LDDw&1ON4edqWHPoh zzSP@=yPukrY*#{yga)$UqfU&75-%3XyZ7JjqlgD()B&K!IMR!L2k}lSKR`#FleA9h z*6!7Oaf+h$mPI40H!*ds4sR2#wYkjoGEPN^#Ol`8fMXhD?UiW!*IXyJaabimten9w ze@{I0if2!U^$j4FYTVB_h?3#7nGvDq`_Xc!uX}gks}ejAN}6;nsy&Hs{d`c>(fi=> zz`U$)66W$R5ae-cAHyWTv@*eq;V*?^DA!?dk!(!?tKEnVkgfj8Hm9DF|Ij1C3!E}M zTBC2{6&&2mXSetKc>F!>YxLLx{=d^=hnkxwnYG-H%cV$P_<-#ec|*Fs-AjPM=&vRY zVu`ZU12`86Bqn5jhQwh^Lxa;Vsqo8MbS%W1SmTgS^9JzKn0%(`g| z(d&5LcRU43aj5Rhlcann`wIfK#}tB(!4SsxG$2uSnt=H<#faHf$y(z?zIF7xaz$zQ z2?()W=FL?I5N~=kS9nQa@=->2Gs(wR7PIlcfJFOtp;qF$#R79&?M=kp z!AN2rr}1V)-?l&5Ebc{}MN)`wqfPql}|e7pJfKHCL0U9LOwtQqPPAdd|q z_%*|J8fzHxT5}D$s8G7!6>AO>wTF|<+}wO!o^B5v5i#yr@0!)kDqW8u9hl21$}f>w z>6)Okivx1-RvSZUDy3j0Xe|;uHfhU&pueH4iBufWCF-JSpT)Z6siS*74RPj1g`Qd6 zxhH2Rl7x$qlALZ)cO_%2D{W|~#e_%7kV+{k+J~8ccP}CF{9FjYAvKFe;Rp^|x(`(f z=8pC}jX<7CoOjx$2w?rrPB}iJkuA6oN~xlv@>O$srD0nh%{x`EUFqjK0K*WX7r#)a zWWHf1i-Y>5AnoyR;d~Qx^VKalB#lZeQeC@1t&E;u#iK|jB2->hu}YobANo$~o-P+VLL;2~9 zh9ieTVVH}Ep{^(oG@MSeO5vd7;o;!W(QrO_lCB02kQfcfu`N+W@((wBINgSE{$fEP zfrqD5<}(c%IYgSm&ay=FZk7&7e5svzo2CBvLLEWPtJUQ`5+dc}xVS0A(OIfD&^4Dr z{As@f>e}!Eh`{^;07R*%0zQC4M?r#AEg`FyX^GDs1jj+8~EWh>GClB!`xm~8NF zqrq-10t^$wb`lk7!XcxL5if-pl=QV5WOShLloTcF6jCtgpr*!?;ASvDuv2qSl2pJU10eny*!*yh6o9a^ z7?=zd>i{(C#H7ncLn%3N9@DcRNzl9P!?o3w^PL=tz<$iyld20pJo%0oCOIj zd}L&lA_6aBvHed@^tKTn#U+Sd5b+ybIWr#dLZcK!g=_5JLCUrm{IM`~k$d_xS|HjE zVV>H0v31Cn2Z1<11CMwJG2kj<>B9)c1_Lb;A4q=$S_h$_^ycSb6cSDgV}tBCMe5-T zRRJ*3y9gAE4zoqDHa;u@%Ac|0i?wpG2^Jc9KGfA9x-xj9rjVDn@>WEw)_@ zPp+I`JJZGS-FXsbP%^zUK;$i@TR*?gf_*0^H_sbSQlh_&4~ zYYwK4nCy58Hk)IKgC{FSf5WdL%b?hD-V2>14*mZ+k2U(7{yPCa>k?WnAl=5WOKp6H z_ZS`bzfj}>%_vo)9$~C#qeg+?(BNaUf9&U0W2aT^g6h}q88$rJe=%Sj z)qVjegTNjp1%-vWY9mugWXwF#))p3E{>#+Fgzckx!vDopzA|Saj2aa1et7Z`%%7%{ zo|)HPZ|~hTmHoXB9)D~sSUV|@Vfp`GUIoW8_#q(=_KHR2gse{97y@lwdjt@riM(0$ z;DaUoc(X1JX)whBpVkJEhye~q|0r^%7TetIwUS{)npYI6?NBKq9Uc4#`m1q2eriAf z)>6SY!-yg%_03^52fndC1U{63pJ3$70`xB-Zagk)%TR|~ZCP*yyLmQXjQCR@Avy&-FP!~Z zgIVm^(_l!g6X|NjRgr}5K^c&^;^3YGHF}+nPp^IgIht`*>3DXxfx%j|;U?~Q)6ps) z&^Mf_&rcS;wC!Xd%~4d@;_g8Dv4wD!v#05k=IVgeKhxu*r+*T;^}HsoexY zJXe{F)g|{uENGU565!_8XE@0QG~A7EdT+L?%}LBf*EGiSoiS+FD|1q$7qyo;pDQ@i zt_P8F3*@Xp5CC#v1VCW-T{!CkzpWw4+Yo}tZM;#07}#8t*i9o@WA0hmAG+uo&Uk(! z2JN(7+I=F8IAoncY=SYeQWF-XF(woQj||vq9E(nQ2*o!=pn{Mj*oi&V@sy}!hUFfo zGyUK(+iI&2ctysGWtEH*M9F!(BZDUt^BPZ|4x1l~gf^&LkN~l7`5FHp%gfSfZGgZG z>8$r#SS}p9X|iRyOxN3P{1W-H-p9rU%+Iq7=V^7O{kVH!lAyCZl07xgdN*1wy>!2q zmH9B3e|$Bze?1$kA=Be&-zBkrL5|bz5|~JrXAD1i$s<{4k6^&?SA|8rrvaE-J$LFV$#SLsl|EdQPAqgcQZiu} z8u&$;h7kE{J1d;#zYG_zU$d&x$=GV(mKC@J1zK{oo00F3B3o~rcTo49anZQA+EI@A zk}(RhOa!_+SYl}!eR)9pe~5C%=?tZbVBkbe%{Ub6ZT2f%>aGVQFpr_kfIWhrD*Vex zx+EC2v|k{COq-C*(&&bWGy+Ol%TxxZ0vA7N1lD!6iybBFq=%Y}ezP8U_Oq+SBS`3M zMf$T#sF(i1i!1u^eHy}{oRmK3J4~g37fd#M93oO~B-m`3l3WiUku}UJoXT|~&kGq! zDO+0x>_3g9=?GO_rX4FVZApeKW=p@LEUBd&nSPhlM6}<>2}1E2*Kd|1Bp+`==05Pu z2QEIGuncnlDrbCAfL&{P+*+FpsGS;91Zn8ZilMj{nWG5a8fE++7l4ebnMh5wpW^pT zuyc818UEv`05?~|pSw+*LT@w@D3<&ls$x_!i;)UHYb^=CZN&7DRAQF<`(u**!&VKV z8oMk1>%~J{^@^to1C^c@dP_sq>8cI0>(8%M&qB!gl~=~7p!BQbQHFwVrspdsHGXhU zZ9|Tk(-!O*6J~YG7VIx$@^6a|Zz;oRP~XX26^&_S<#K>w1-XqyMH}U(iLpym%hN7f z8O3HOigKZf_=3(K7f3B$yNfv=uG8ALWccTy#9prvZ3^_b80@3|z`^7B8x=;fR%6Zn zZUW}>agA|F0RfN^cvi#n5eJ$JwqKZVa5_YOAEbj8D0(K+YqhnJv$;Fc*8l1L6RS_@ zy!2jiO^|hbiKxxR=8Z_!HJ27~j(oUYENiMAqruD!U$ z^c!eYh+GA9@`&l1=r|ZP1U(T{q~Dn zW+6S-VDPxT%;stJnRInS$BBV)kY$Qh8N2A~nH)otx4A_tm$*Wl{iLjC(X?C#i^e2o ziB{Mdcdo0OnS9_+?-pn~nX0P=v)zxbehFAF_}=&Qa%C{j24vBiMBtPbFEk;0%wKaj zujrDD;twIuJcl~~qwBOqpv##QF#8zGPCOzxqLd+I2 zON}w}!9#4+h!rP7OQ9!=r|&H}fHFLObK$h~wb3fE@oAyd*RrZR+(pK~{C>mxk?HrV zdGJ=Y|2UE+%O4T~D4+Z#%!0&n0pc32Zkd_A`RxN_FqBQl&;j*&p&^fV1){);#ngdD zB>Qi3m^0M`HtPjjv1zEQL;24hw_miLzC2#qv*3fNEo}D2XT7s>23fICqTK)%I9pQP z)$1)R{sNeh$9!F-Pn*(p{~rFBzjMK`Qs2d5 zHrnf|DBAOq6~bl5vcXFoH$P&O+Tt;A$yFhg#%4Rx5##yD^kLU$)LAvv8ORs&=&`ry zu3o8E?>nDk8@S_su@P(E<@iyjkM`tuz7dFnIGL&Co_17|mOz#KX&l2PSn6@Rdg`8a zao+~wCkEd2)JobvKO-&cP8j{#<+)N#R?jh<@fDtOo|Z65mMT!*4iQ|33gOLD9Zu#n;cQ z+jBbSv47une)Xd5tKPE>irKmA$Qj$Dxl=QuoSa+bOj^x`?~b{<&6@x4&fhQn=CkOj zzrShQ=cl*ttgwpjZ`g6N=VSlAvue%a?^pcQnf}}2cUJE>HQ%NcFUTY_#v0yX(t8hs=Kc zI^%gsA6B{Z<&&iL=6`Ms+ws_*<$pONIdvwiHy8e}>7Zr8v-7{WZu^?=x9`r&2ovAi zxb{fT=74YCxLB*oRjP}kZ)|gQl2E-55XeL|0JRDtU=YO+M%_NjhgW_7@w2ae_{RuQ zb2n6HO?~Foxg(HKB^G}(T^J~J^^PI8=2p*LFxm01UcMAI1?ZQ$zqZ-m#)Pk+gJ@drL*y+#Df1;bAR-$}IYwe7JXhr#7esb7l?LKo_ zC)VU68vDPFXRWPeO}iY`G=XV0d^q0Mymh9X@@wX4tP3k)-WMZG~U@&E`iRhDE)(FDC&^?JM< zF`A8nM+$6@w0qRJQcoW3I1d^$sD<;PLx(!x42%Kh^(K>E1XYq`nP?0I-L>R=86X6d za-!bTRDJwkA8a{MyyGMt_(0bQ$uUWp5j`dj9poUU{IX3IRrQohM8J?n6njkPguXWo z84%G^+q-Zl-M@EV@wb0`WwX+Aa~vw)pNIO~^W4x08PS*;)P;qNrg83>X%5I)o8&#wUmNnRZ>=`jxx?^zQdc_f4^= zbl)D`s0Zq+<0G`F-`+iCxI?mxIJC8%P=bNVqAfi+K0C(}-n&O+^*;`q67 z?!RZ!V22C#ys-!E`*xGprbga(-<08zKJ&;!yOGL3uhX{>7DX%|s_>#(acbB4w_o{I z?ztrnmG9eyhCKD$9Z>~)&u%$=;;B34Opl6nh(4KejDp1&c5K(q{rM;6&YE*`m$=F= zzb)Xc9C9Xh4$Gc2bdW#(q|{fVItzy10#hBcoZn0Kh2dL-6S@ zKR#4%$^GqA`sttdG=}xOWkv?Aml#Iv?FT~AN+0qc%!K>{&!IzyR@Gm;R^yE|L;#8tvP3BXO9>$ z!eX)bt4FG;I$*$nO`A5g;_Sm1G^V#|r#8Q__@(#Pvtjo={pjyT^bD*1VfBlP*6g_^ zoLhi^DWJ<>1;>0`g*-S8nk0?mMMJ3Bz5A6fw(RlryXDTAsOJ|OCA*}^R&yw_=$J+t zB^l{Bo&#Wj05qzFTEc7=QBxEm*mR+woG+_N$sXEc+|${*N@B3uViFRdZ-cdV!V4Vli4x6nri(VS)~*rdcie zP(47cOK&pSjR1`v!imu2qE4M9$v`EAh&C}4d(NLn(Ry=|j!MK!LU=u*lpqGJ2vf!Y zaXhQ9sY=co+T9;I{rvFQ#1w}uH@Q#$!~6EU`S-n<6K6b?6OO%-+$dz+`S`1Y49Wc3*$;5-Asuoj7AaoKo2+ZZuqD5P>!5{F<%qYX*&9BG(KlZ*mzK-H*`^;>+ zdz-H6RqwsJ7ui4CmL*xm z>h1QsZDzhd?v*UrCUFvQlK1oZ>5sd+vop_}UG3SO^PF>vCjD^k>JqW<>I4In<0v7K zk#S8BXzlI-#mRxiL;x*5>tBB zjG>ZH`&z*4H|1sKY&_iM>+S{=+0}Vm79;H4r;j$*nFq}okrSIWX#Xn*PLx+qE69Ov zmGRsai}{bq<@zW`00@9FuqPLcnLdBw%vlTP-Ehb97@a$||FkMgJSSW24u{=rl1vim zsJEMvW-eZun`h+-C)*<&4u`{Lk$J?BAc~^E0b>lXAc`OeykN3hXlJXd%S}wY5aLQR z-~>T5nM@Xo-DZ{qPB7c7RJWQ)aYHcO(( zYIiss5mvKF-QD9>DVH*8^4O_YCC~uD(!l;jQ8Jlivpuv617i#evLwkox-{<{ zV+>>L_xpG5+?kY=v~1ZjgwPvrys>iS%9SfuzVXHz2%%-mmL(@A@7%f5@AqSj8ACiM zZ6~?-oMk`y`Load?1wAn4o)#nZQW4|sS_Xn=<$hZta`_`N@|sWQieGQv^2IfwgiB~ zY``X>gpiOU`yX*N_Q#x@oV;}D(#Xh2p65e7@jM?H8M$=n(v*~xKFm}{vO_ui%F`P^ z^UizagWHDe`Ex91eOESUQXLJ(A8#Gt^J)qB>y(}*HsqNC`ECw~3MXI|gi zVo}V)M=zQ-W%MYsS#AQ5C?f`;2=f?W)Jxx?z$8wr>S;Xa>*7SQt7fmSx-xC-n8LQy zG;em{oJC7YsjK72376Td-$lfa97|<$W61< zcPYoaJOI$saV+3!GMlW(#7nYpiBE|u(kGXF5u0d=*W;qT&UPV5k*yztuFPp|s^gAe@RxkHAjdi#A#C#R>UrA=M&$leyxvH7((UtM3r zY*rhs`DoqSZ+=)SIq8wjZ-4gs_aAuq7f)}jjo1A6gDQiwqwcCr_uf1;(K+enC-+q8RSgQMjqktnlOKKi zdvlXxCfxMoo+{mHG10cljiJ-8ziUHH5Sq8V^Verycxw5wM;>_m&mTQ|)0E_tl+|2=kKTIych5fm@EtSb<0js` zW_M*U!r|!Ly=L9IHRU}HGj|DLY>Y8kmJc6391I30Oqif3%5Q%2o0ndCsl2?ryuAFS zmtOkyuYav5%7h6Mg2CXSLx*HprWgys&epi3^o7^mGH%v_CDTWgrI7gbbbL|d1pz1}j1fvf5Cl;~2qd%3;fRQ^M_6QuLzn!Y+lN_El(A#S z0stWdArwmPj}Qv^D94T+t0>B$Lx*HpGI&YuJo0vnqiM#TJ zrfHf%5g-7jYnrAJ2nBI42DInY_J?j?n3cHrZIUtlTceY+ufBC?+KJUu;#0@p`N|pM z%uhr?9XJ*Yh{I-mdw5dqBiE*8r7e1_La|RBJ%8rhvgl)vPf1B!{1!=@xoT=`M|XgP z7_UHeP1gxY{v zn-~yH(+z`?Ac(`3eP?j$@zqn}Q^$YzS7&rvYUZff*Q9kGxg|Dp-a}8HanTgR&;Sra z07TO?!!QCM51q4gc+}CyrVce614V$xUc+` zeurgDzio7S{`7^JT}N(?&7AkpGj%Q{%AGQ5K~(4Iet?;#8;ZM!8+Pq?2B)0pMXN=n z7pCXfTDLtlFTL>TMp80wc8=lI5Yu%>T4_|T{EGCqMUUm0UEx)TTiF z_CMY^y>M{p;Gsi{a?+=-d~{>ah!ulOm468}tF{!(%1KHYW0g)leob=x4cnBf7T-Kg zhOD`z*6L7Kuhm%crsd|1S~eu3>y_)%a?)13(w;Kt)`j`4TORrA8&6&8Q{syB$z_ix z2|w64f!8&Nh)eM`p{8B$clpmIUh~tB?`74S4^`Je(uf;wagb5V<|l)y7q9sKW3vqt zclP5?&+OgY>2-CtdA%$|F=%W-(N>cvBo_{k&q(C7u~!d?r9lIb#G8!vPj;N}7cYPC zuJQ-o-uimNk{>Vp?9J1=_O#fAUvJuZa?suDZff87e25ot=B1B!pBlC5j}!TW_iym~ zNwA|IFQBEd83n_b*o~eRQK-lpX{p~eD}JW z+CP4F*ZNnYuS)u?@_1wEqDO9?fB2(c?pptwgtS{~w;rlZyymAL-wRb6_8i~7!{*z6 zprXP)@7{$4F}4V?_|c)an)s%TpVd~MwdBmYbupc>V2Jeoi>KY`*>&4q+_d}T;Jeq| z-2U;iyVk!PJw9dk-cL@)T=VdQe;u=XL;0Q^+tZ4#i_e)eDw(rbf*R%i{-SKCbll04 zCwZPPC@9#pY179af2=5q$z%cmMNu|v*f40&pcyk}a2$8))TwFHrZJ{7%5Z!0?hSWu zT7TRfyXxVm9+=tP6d|gPXge5Sk4aYRy)EsE5X&(G02l>clx+@d)$D*pn~4X+JN%We zJi2<#+b5&UYT3fOSKYa))cRF^>aV~oiegbwQC|&^KH)fyF;-YuxPJZmQ>RW%n>GbB zM$c3?I2_SK23vqB(S!37czkF_gs9nKE(hlG{|A5?Tx7|psqs;X{JVJrWfSp#>rIEF~-c$ar}@mE7ru0tZH-Foq4%=S#cH`HFnwa zab?vlD6P0K-zllnmd8-RihMpF%9(e465=s1q9d!%hvwY4ROhVBQn2`;-wj^TX^

  • +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

    B{N;H}+Lo@~-&)(1eCv zt9G{R-hO)GP;X~Ljp3J5a`P?K??3w6=il5b4l>iH-CgNqA|P1x?tk6;$V+c1lkfcT zyzFgzR{!K>W+_hp!L4Ir^`J^wm(92F`Nx0x^s9LC!rNA?m^7H#``+WvJ@?M;Mo1fZ z)er7_sHpo_e>xaU8Fk;(9D3~i$98qwd1gqAH`vx-&6}H=nr^%8wu*|1x88bd-@biiWo42iA&Pk+SiMin9alj_XZNOB z>AF0tOfWK4)u4#sEOy>PPaf|KL=gwa`An6y*&V&)Kv^{}MH03TTQC@$Iddk zEZeuk8~Sz3m@)6X^Ul6~`^w5j`hzBDGR>Mc{OBRI_Om^C*OUv069lV;p#Y4+GF3?m z(Uy(CGC#UWVx=2aoWD$5{zK4JBe9|4T1?B|EhS?XK=?h7lfe5m0hii!3N zUv&SKQ!-lL|K$re4Sp;&(YjL_IU~&>%Zdyp00J;nS&aVFz zE?j)w&1D${S@Wy@`rG>^=AH6nCybbX^YlWbnFyPbYAc<3P3J35|76kN(;Z3CQ9~Bo zGj3*vu;KM5ADUj&7);N0h$ zj0_}|P3cUjF``zPj|@?^)C%v*6yL3G5y5C8Ds^RM`A1*5KB zIzB}W=!mkWBP!nf_}33U_t$QrVAAbN#~$8r%Fpl&Q56#ECR0;`ojpS7yoDXLkN-66 z@SzLtu9%e@z5dVt{lGcBW1Mqi0MSiZM!E^enKZOAw~S{n9Q2=Yce_i5+Y{G37Ry)zb|@j?clo(Q#x~L$@ocWXmfb ztlJzjedPmDZGTzYa{M@#)O5URKRfESMGtwlZ9V+iLC%^`I<3+iQkv_@4K7@#F4Z^0S!jTaK;UyeHeuzOd%gP0`a=J`jE8m5)!JsO!=oHeqC* zlW_@J@A9U9lPR@WEJuzUVOh4QsOZyAKYjDfH+y<|L{S8Q?(Xh4-gu*+pkVy?@$bI- z?vW!$Dl034!2rcpt5kPT!z67&#pxZpn*$ws#F4*m?R4bNoS7~59Q){#nyOvz?b-O& zoANxGcHj5Mi$~V{>bc*oUPmi$T6z1eqccg5f|xIuzV%__dA_Kqh*HX)oAM^K)GI73 zT(f4)kt0VcD=U0K34`WilBZnZ!gBkjkDJREanvvjf&>Uwt^dulFI)}`p$mIC!T{93rZ0wa~+yCd`KYzlEyK&_`_m<-i-Z@acZ~cj* zpS&YgEdSw(<)dS`7fC@0r%#_32|Rw?=!54?Qj%cG)pJx!WEdXj z@9~r&eg3#}^2QSrt=n#c@aXw?`J`6^%~7Hg59F>!PFcb~Ko& z`Q@dK@)2!cunLlu|HNhsr6 z|MAwk=FR)Na~2GLc=5PH@7JtpI&J?*jKNujBcZ#~-~^jHifP=kkN6{Qx$X8viDX|- z{WcX5%@LhcTD)NHv;;Fb@QY0b8<9MGj+Yjer@8|jeuNkR0bt+GlQkW9q3hL1PbHg7rT%bH#|^`-^06OV6i-FN&rJ(-xCIAQM4(=Tq&TpSaTm^rp+!Bx`| z%#^?{)=TwUH<^sz8v?LDP`~~p2O1TUMi0Ua)h@Ch3jlrUg>`jxt*xytm&-5=0I=C? zCr_TNtE(G7emp`bwCJRSFoHEuw;y7en9|W>T8{3NJCD|#KGjfP=ZK%day<0Z9&GG1 zV^fMJjg^W=4UIkV;m|)Etr$1b`@p51wCSk13)0_VE z)T-D2Uo5ZtI=f15yrWE$x}>(4X)MRmp8BKpG~R2g*}ZKys<>yq=g9wC`(8uzl|Oy3 zWAk6vzw7msy4LL3QXe(>uAk-~*!cH#Z*wt8*EVg~S8E@8_o@Z-)Q;Mt8#cJEsZ2;4 zKkw#2vv{_B!;YG5n|kDRo8CDnj{Dw{pT%t3R=s}1hWLzGvB>`=Nnr>hLkcotT;;5V zlS0bLiYx~u#+@>HX72a_;=`t@>KbyV#Te;|BnM=IuyUpS&Nq~xp_nywK*R0C&Pt~b;)9kAoVs0bj0fb@M?Q6VkQ z9sVKLji{jni6gmudQRwCIiQ#b;n;pP>V~T7V$rBYBgPE`tVpsmv1CTps9v-I4cimT zresyj09}z~SqYys>N*k=%BC!;m;t&X$r@y3;Be|GqN=)vTxq#pL`@|qpXYGoI@ckz z-`pA@YRHzDVVM|+9@Htu3}65g+!=Y1h?=Tu$jFgcennE*6_@~`x}-`PvL_Cmky*wd zQ3oLrL0fT?zLhhJ9xHG0myO$TnB z^x^$KIvjqXUH1K0N(nJdL)QrQfbTf6lXUUa-(K1BYF+%Kkwb>so!fMaWEoOf@Fz+B zpFeFW%m;f}X5f`A;I7YSc7OEL#)z^TW|}W;-1x@vxQQc+huWRniC68Rj>^G8Lv!29 zFH={HCA%^Key;;I4c+UGI`<^Mour2WwEKHBVo<<{sQw^j7|daGk?>iL<3a~=7KghK-JBe`%-TkGkcSn-?bbBsBkK)2Y2D^padGUs59IaHSXPvflH+-en&GK$wAG z&@yyk$prZa9;c|(Y&}4Ik;`wndGXhd0bxW8RZPmCm^o@@CgTrEYVwF7+>Xrw zBCx&eM*(pR6BU;}X2fk*X1&!AEi>bnFJF|{6W93bU2VHtg`}vQ#F6u7mwCs=wESvo z*UsI$8`nJh>&;MhG?S_Q>{EHrl1C;^ujE+Yx=*&$9pALiH)LtywDj1dOm}f;{-iv* zwk1SZr8@lz?+;T~{XP{!=cs@ghM|TQ?2&zh0zwQEW}yK>7=VODl`#g>q<~CKl2sA} z1eBhY3pOsUIc%J(B#aR?)L@Ss=#9kbuZnD_O6WRDOu!7%R0B-lW0K3pO^?X!qTb}Z zVWlOptYm0JSCt@~E5#ft1PuU#dzG9KqJ$uXFvB8hnkLbMx(a|W!(a>-LUndv>8Bli zzU1=RYrleav~daA(Ete1{vyo?2IN|&XS+#P0;(K92m?YGBaF`W4xtnw%$$4pjWE@N z%DJ>bL=9Qe5oU0xc>x_lr|N)0C8|=7+zUEHv<;U7(94=Mb*ZPTuj4Tbnxs%bn7z1a z=f6=%`{lLEzP{wF4wu)%Iw4kX{rhk0b?eXtbGjefdfGnb`W08@b!@M1S>GykK<7_nv~H>+f3_)A+N4Rqw1n<&EWkVa$H1HG_3(cRs z^C2vlS6OJ$>M6zyM%d_gl1~-QH7sBLOBjIFbf~@ng9*hJhn-=_#rQsy(zv*|mX;P- zmP<=Z3knMM?AhaXy8)o5r>D5MxTK^+k|Y3#jg6(mq*$;i)$jJC#aBM?SWcwI&MDnk z>pix$3MbnILJXY{3J5esCZ?~;kci>$K$)Uvu!{s`)1F|7u&QzZP_HoM=UE6_Yinyy zPmjyx3JIq5|0~A0yStkZl8}%ZH`rL%MIE>!#Z+Mnoc7U)PglO)7SoLD{e;2$3il z?1>Xygdi=b5L8ixRmFcys{=ECM6Y=9l6c>xOoz@6l z*F2sm#v7iTVUgu93Q1jF>H`pJ62ZKU$3Qg6pok)i-NqtAm1F}DYjFr1Hr0S^P+qjL zlu!V`V1`)GgffB{CPkz37UEw*bGY{t5+XVxIA^sJUU;xpdoKZ-=v;K#_<8wLFaR|* zMV3_^Fa|TEu=gcuq`-(Gi-{qtw0}?#(WIcH8gR~4pQ{u-@Q2=1FQ0?qpVf{AKos!V zCZDYuIG)2mHBAQu1X#f)4B)OKgc*haw%A4Sg5vV%d;+{^!Bm$dgNBFkb1J}6L>jcWYl#^k|cZ+zq@hXP%^sY;5hnE(iosRsR>D6%+i)0#)Vzw-G*YJ`AHT{j(( zvBsfSel(|K{7Zf_uj1+}CS^HV`f5BbBhV?tm*#6#f^baB8xak%swk?WsEVp8YVS$D z5^V3bm7fLh^Pe2@cOXVYUE800{q-kzbbGAWBm-8M7-LzMOG-)%!`QfSV{UHl%{SjX za^y%^mStHUF=E8><;(N(@-}VSWSZu%VZ$Uz!WPjyvAaP=DRb^wvh0TIm)vsSybLbl zbi-yvs4^6m$4FI`9*)5}743E#W{dJITYOC~S5cc)uHO-8tno&8i1~%Pgnig_UEjWa zI{*wwY7_c($BrGkt`8eFOp#@bFaThT7!Ii>O`b3$`gG&&zr0hMGNOpz>}&k%Wy64a z7gc}&OaN`9^@(DjgiU$-tg4{ST|8hfVG<~yMA%Q_-h_~p-ORE z(0T8R-g^4{|N4g~tX`*;#~0o-y}+f^0N!I(zwpzafA7ilKC3;XvHOLs2-Rv4&8ltd zHtpOKbXXC&T-vPvCU)84NiYT@ND?ZR-s&uF=wiTB?Qw-=BZno}gR(GY$`rZi5ULs}jT_Bfut2pS>hLE_-*CnO`)H~H>|Mu0PwS7a@H`us@5Gl-fR?Im2ycdLwtiz?^?@c1?*=ny&JV@BpW z7^k2a^t|~nLP%BB^z`)1%*-uYwq$2#j~h2GJw3g-xfuYGl9EzVQW_f@w`|#xlarI4 zo-PFgg4c0m^C{Y)RpCvynqSUkck$Yb| zgP&QscuvP3pZwX3;^R_A{?*IAKQUJE>tC2n;(TnWsi~yjL&CCbe{8ZWAtVBrimb>A z#aNR0;!&eUskD!H`Rqe8ikpJzIalBE{V|cd_HFQO|HUJVR&H)e zDq48mjUj&N%TL`uG5?e&JHBM@t@9@go7`RZ-+#KP=+Tr!VW&KDdWu7q6&Z!+Pl~L_ zN_c4hGD}QK5m?l&V-1o&LoZJ7~di>OI4hY^FrIgBaM3GYPJtN(OZ@(iALRVM9L zXm~T>+=>tYU>sWZQA!bJp{L zjQckW`idRU97;@+5K6Jt!`7~T@u1IHc=h}&(j}V^>XPtoDkY&gd-y^ip)rI|z!-=2 zF7dhk3D-af0SaMNV{COX=Xs2pi1V7q->W&%#-uK>64__)7&>c@)^tV2&tfq(O*2eP zV?}qzjz90}Y0MjYOIZ|1x=Bov5)`g1yqACx05u6>QCHouCe^Ed_-<8?k~VvGX8%6A z`0W)6MLX=P`)qe?I25f*qYMg_Obo)=q5gx{kNR!-vu0$Vphgi1HyI&~iYb=1^|h_- zal;nHqfP@ZA|~^nXqRt479na7F`;C9MtL}2w5dswq-X$$btR9Pl{0~%rbz&?J%^rq zVA*eXCI9fjhX-4HJ(@c+FA`{&1=G2G?eAZFF+@7>l+T|qydW+aId#*}bPc)F^CA#2 zH8N_-G7eNp3a9PUbtER1P0gy93A!rFz5w;4jGdJ`F-)w`HBHtG9jwVCiy0uAq^QV~ zksA@3eD$8>yVJ5gjK$Kq{o_6W?(+H5M6){6`uI#bDd_hN z&fck~W~F&uPFJiqUa;NsNE@VEtZ|bo7kI?9ef4P2=<<>wF6@`gONnmkkL|hVo+~de zuc)Z-csu~m)z!6O!-g$ew&dsMhp>fCfFznSY3@v&^MGtpO|g%@_Q%gmH>?gEJ!#z4 z9!J`~y005UXzq3GE%u`NPQ)+>5nZ+>4=oxRpPIv^?Sr&l_9N+|Yi3NZoEK+L-dhLx zWur#tIgYM(My8IMSzg2!bZ3kiQyS)%E^<1ucGjCA24j>ZZrJP})3}4pd_wG8Pi9hr z-EQa#gyv7Fljpj1&O;^rizFQfG#2Z+8IwHZ^u_~?RrNOvvo)NGA3Y@dWJ>GKTACNr zwm&6?rkCA3G{Fd<=4J`37REFMU;skPS%$Y*>^8d-^;cSFSc}*j2_q@0LBc~Op~zyj zivmv#O;Hq8Ck$^9tyT_$AxpqvwWYZ;7->3q-tP+qD+8s773`wbg25f(w5mKWfB-^B zrkoYTaG;LJ@;skCdYrvmb69}GP7W~~&mcpS6j{|Nb55ADqRD5KxfqLJv07LFLL;+B z44HWAqC5&fj-p5Y_t=h|jbrC0f=>qo6!U@@ij$_MCd;y-BjIdwWJL#yXcsL!)iq65 zA_|5V8@z|egthk5g;z~N0dp+EEYF}&To5BzIF`W-hYdwFfVB!Jl<=MQ`G**?B&k%e zS}+reAR?*)92(#;ASP>dx9@wrS(XzoXn-8#Ibx6$ z^MX{pPtPA$0aj0foH%_gHj-1MhqXF zvv13Z&HLJ}A8PHCzuo#Dm({+}e9?Ue>3unt0SID6>L?s_?E|9{#uPe8P{SBhMFRk! z6bRy@lSWidb!T^hH#vXUh>{rE9nb*bUY>#h00?w{Ck-FS1DtzGk1)m<`e$`U2!*oS zQ!OZk&fEJ@9zxe61cs_oWWqoI(M*u&RFb8O>-!*t48yS7?Ng^tjf#pobm-8!b?bt` zAOP5Gw%FL%%F4=N!-m=I_K-xXp=sXC^e6;|qLL6TkT}xwTmURa#bu+BY3hWShGyt= z)YN4hs8Y~pI8sK<%pDgZ2l^%R65rwHW81iKW9Tqd6vgQ1=!%Mp{@4JB2F~JHsCLy8)gT$FUYDUgwz(5Tm#+QswFQ0ODI5rhYHWSMimsEmbnw03e-k+|CoXO=g zaw-|B1|B4He(_CiQjLnoS1GgPqlV~b8l^e(Q{VZb;r282tzV}QtY((H$MNwldE23CNI8q z#m$w2Jg4`pefF8>-`Z%F&;8-b1+Z?{>kq%iEyd~IzjaKkA?XycBI~bR|I7b=_AO#h zX@ZhblbSzy=VYgzI&*fO<7}XgeC*iq=BoAU&J^Bv&y?m5H=n9$X=&W>@)~2@vLD=f z^O!ktlNQ~w;zo;fagB zci*ka?e+Dih1|ScuKI(=S3Mung`ad=MN%C2PgR|0Y25Jg8ogrK11oQium5E0!4qfd z_kPeEGwJ%7SzDfY`(*a?`|g=Mh&!`)?X%B3|JEitYS!&PxWB^s*}FU1TFwf#I7v3TE)DuvcKY?>V$AOQ3 zwd#epJ~PHH_{q(a4!`^Nr=R(hozg7)=-y>?+fRPI>ixY2J zBB>L`w(Z<~BG7IO2t}){-ud31O>ey=&xKGZ+6TXW?)QIR50y9mV8!zClmO1k@cgc^ z_3)uHQJLt{sn?7FEhx!hIncgSZd2!?(k)nwpyY zem?*>9FCNfl!Sx?N~x~vA+7>K$W&FMe|x}G6_ZjOAQlr%sXUx1+aHDWkt?ghP_r+3K%*^ePd_0d5AVGg0xN%I^U>-PD(iAFHPT!>#9 zB1!hmtIoMOdM9UJg++*{p`ye=NmN!(S06K?>{8^oX)znNLPtv#>4=rZv1#V^=l2A? zZkr{{_X!)J)XMWU4GoR9(Kjtw8L@Wtr*CmFw=Zd2zpq9dbLUg@Xxq-Z!|Sna{**Y< zMZ~DnJDxh+RWs)P-`Mv*`tfS3%cwYhq}n?A&Zp**lRK-A9N@U*(kWxiqLNG((M(Dx z=eC~s;L#60+ZsRpo^rnCzc(2H>OX$0;{&Wv+*Y@1k5}1S+f_a4{@*zE{bK!F9@J)O z^&J~^->>a^pIG+~h?AYNZcQxN`tq7jKZ~4l_q|bVfBocS>v5JG-TBU5e%xIPe-ycO z^U=??Y#Xes{d7xn%FH{alV5)NZkI5Aere#tqZ@bRiQLXZVNagiR&#W-AhvJV@YVs_ zxVsiU7_oKxp$!LFYy9vjV|zUY01VG-HFYFXPO8ZEee}@#pKXbseor}H_vB|v(9WFL z@ae%iarB+Pook-jeYCF1@64MpNjJ*Jltog_Or1FIrox##+rD8}&GwH^cbsT$~1f-;*LA-yt7d3R7}7Y zYp`}dieY0)$Bb<`x=ZdnT6_9reO+HDTFt@wb~8GuXu_yqQCV@!@ehBoX2X`qnfL#& zecNjr-U8c=D~CEf39jx_!GU|zp{H&D zY5sDd`-%WUt0(|SOiWBqPw&fDrD@s)@>LCd52N0Yrvan<*_pxTa3lZ4*arT3cA9Zn zFarWZ{~RKcN$EnwwuZ zZ-zB@RH{4J?x(Cp)M~fxH+mwL-?3s*g1mKq%QiGXraYvp9uc2aUcTh&c=Py%0HF|- zm^G^C>bcVs%v9+Y>!tdw8%^+gm#?^UaU$I>H?2R(Ali@Dc>xOtWV4{z1%XhBQo`^~ zgoNP64V!C^Z`#+9JFj9$KH9(Y}))3O-Rj(sknY>Dzom)0ZZZHx$*MR zw>ulF5469(>Q|dkd37eP`ScS~as3s8yqS5U^A^mVk$6Rl918C)y~0ZiN2j_29RXkf zBan5)WirWT)gDtS533z zjvJByfd*uiH*MHd+q7}7ub9i9lpqO13^imvZq$sW z5rtg>&l;NINzRSqWrHH1l;O?OwY%0o_2Rl;{_SaP`&P@eAEsobx%xuUFoSI=xuHl;_O(K zgq}OK13bDsqqd_`ecPc44f5GU*nfTP0 ziC4yF6%MjX3UEmESqg8vGg^cUI2fmUuA8a;qgRy zBf??ItiwXu+exO?GdLWktP;a^URxmi2|XK-?LWa@rb zf;t_eH*gLmt?Tqy>0 z9s1+y4Vz9ySCkYC;w`(x2$L8LptF7sg=tb`s5OjViY4v9NhwQc9Vs+@vE(@f0Gs@}8X&4#`95t$%sz+j9J4sG>gghNUg6e!PH z>bg%JRC-ve;BVaRuRW45q9m;|cd&cH6}R1e&+OEATl*=^=|HBz2+poUuk1a2WXO&8 z|KjR|6DO)pkYrR$fx$7s6HGQ z71w0yhYXeFF;X?9hhaHvnxGp1m}4=*2oqxJMxWrg=E%|_*lRFOx54^ojx-3 zhWmfMAgZ?=tm!^lbp8FmSde)7^xn^!6dt+ra`?Wgi9_{a%wp3F!^4O{6d{B| zim8a>s7VbI5X&Lm5HeCTe1m#DnHFa|b4qvHh^`vvgm;6@<_dX?0zxJsE{C@T(}PU% zP+^C^HSD?rtw#?z^K&w~^ZU9c&eq;$cswisCxE}HuXT&(=f-(Yxy0xbw4a}S48tOX zSbR>66d^>3&bTrPN6wrvZPKJk(`L*ZS(xEsbV4cOZRYVk4H8Pb;s;A_xao$aD;`*o z!9}*!Z&EDPrqbJkEQ@up*&TMw7U%O@{Y@`dflZJbb_ANLy%Ao3uAXBYJ>hY>LgCKe z&~x&wvv246@;m#^{*N0Xy1^$z+fYoTgd!stSvFHMqHsnjvd1M`LDy78F;ol&p$M~} z%G5x;O6#U_=D@~>zqd@PtB%iGc(3);MyF+;AtYe8Z0na6+n$Qx(0$L_r~+tMxv z6j%A2skM(keM8!aK@)LPN8ZRl(#T1jYku;B;=#F#N5yyg>?LuEq9_s&0FWgGDJ*OD z6C9U0$gCFt03ZNKL_t)89X+=41yhcG|M8nr-*=3Rs#A-uNkfQI0==5)vMkFQfX&jl z_W57^_Vt%y=xKLcZpOUZt{Rucsj7?_L)N2*&KT10^outaKbn$g-R&zJHF!)!)QNXi z{pk6>1;y-<3zyCw+_>%UfB5ZFuLOkL@wYB5JGQ>ARVcjn>M2#>sw1kEvOCgEyHAmE z7@jPvSYa4Ll_&uKMOLM-dV8>=%Q|Ao!j7jOyMIzan>#bQWbyqoJj$-1Y$K>QyjzJ# z7#sJ-!z)*x(I3C})@-3eF%=c^^9McGdaC18%F0h9b!-t5P5;=2OvE&&ExSzMY5)0Xvix`Fm1sUu&{`l8k`%Z99^*RN2&Sy8zipgi5*(NW^)kI zfe-__L{lcuouzV-L7A&qcB_48eHR8(b;J!WFB_idHvB4LFhdQAjTu`y+XgWQkMoJK zS9wxn6H?=n)0RBx&Dv8%3d&2fbJ7dkd`x8Ko?}MA*rFks1yR}ED8dpwsIThBcbKQd zf*)PwXp!t#3Q9PA+T4hKQu1i>g4wDiDy-hldwF+Z<+VQ(b9U4lsRd=_!%~=NCR(*b zppcgOJda^>CyVyGf7*sJU93td#tc;gxM0SJPd+-i{}8);-U^$e=rEI$81G4sbw{wC z+;P_mwgCn55r#x#D@V?8;@G~{B__KP+&4Ue3;O$6Hx1sIWOJUa5uwnIz8p}c{s-F_ zV+=%6J*8L78D@!cX}V#tAWd@(oiRR(P7L_`gEDs*X<6xBx6>8pO|aPRd8mzM@M6N0 ziUn?K+P+#CGVsC@{R0x9}|LRa%!-f)HXYf>p5eM#31q z%0rYQ#v)h+o<+JM$%;Y{-~XG60T!Dmvf*Imu=D}q1;HxtU=WiUx}m5VBUnX&2g5KZ zH4TFTLYNw|3KlDi2&G|!21P8-W0tpc>|FbYmtI<5*CTqJX4%|ZrcIg^OL`2%AfUil zgkC*7(b}hnXKIopsm56zGNp*M2v#f4P(lzjG*y*!uvkSa&w#EK}6iib)+lruMFoM-8@C?#qzijg8_}agH+Qt;lSXeG< z0dgrx`Mv=QjWyf1Z>N-2R8*{6x6W?2f2qNU5aMt+y1Tn2N%DHV17yVpd{Y#~k3atS zyY=OF_MLrQhQe8?DfCNV-QNtwuTeml!HCjOTn+`qU_^B}s0}DAET;{+;#Xx#LrUv? zS~|KMP=kINUT7fHR~<2Ae^0PiuLh%m-vrgyBZX*_s8>mjs6oFJ=*@0|F# zy6jhjl!oJ^F~$J0zq_wF2*O_oJ5kqE{rsv)XOBWrsBHaQz#s?pbALeR_y?W+JbW!e z2-F_AzsC^Jpu*&j7#gJ#F6ir#RGnfJQh5Ko0?mP~V=n0D3)J{|rb}Vx_TB@A91LY? zCVqc!$+&-+XPt;J<`ES#Mor8WqTDnnQ}o3ZelynhWL-}CZoK@?zO%2_IF1{jApd{g zD8(sx(=rNY0000nbX_+ne_7r(7q0qi=X+8Lsp)QvsHW+_3txA2U!PG5$=P{`5M9^7 zYW=3+T+|35f*^1l7Ya7JJa$>fOG3z{%m@ai82HYak3 z6tK%>l!W!#(1jwY{|{E(KLYCC+P<|78E_`Zaoow1C)3l@FB^c%H(pXo1wpW0!h}VZ zWmQ$bg}wEieP>_F2qAX6Juxxy{P7Zfli@7i*?0EuXXkIgQc4pO6FnYJf90>Q)h^R` z8G3Pi@WBVGSFb+*-O*$cF%91{~064oDJhM}vfdR8Cq8<48H zfPL9K!F}sEfPqb*G@Jn>{P8(o4PWq2F8==SWi=kKu)AmjL(aW4|NWC*`qw)7E3aHb ztI7UZd3zvP7&DuiLcG%u+%3alW88H|10#qaF=zMz0F zloOa?FfmPR5gekG0{}ynBv}F8<`gV!f9Lm8lKu)t;cRy0Z={1lqvnA95`;iED8pMs z3x{Y(9XKe-it){ay3tUtAB_q& zIHkaVoEQI+7h-QAx~wA6&LN`9^5^6^rUZBg)W7%l25?v~QT>oMV@5jowg2$Q-t+o` z63U3|nXNDU;dd{sYql{)cWlvu+wQqzY!(E%G(N)Hz3GL=pIP-@RYA^-t5@7PrII_m z>X*;|{@sI--UtQ(H9I=m3+Jr3?Y7$niJi)qO)EqR@D9}Y!9S`3_M92hQt4&ypwJZ9 zG)+}i`|TG|LK%@ev-S6X_}xqEPl$}}i!GUV$BJ9Wq%%I1Uhd>c6j-asP>Ol2_WgfU z1?)L9r>A|9uPC9c$hK_>HQ~ZN`^2hM?^PFMPrrJ_om0kHj;wm*U)-||myGt?WfWMe zZQv`55+phTJGUJ6lgQHY94t{g6TWr(H;__-IJ)>`n+_^ zZ!efTI}3u6s$Y68oqyKJ1DZ(*5^Vah%^NxxM?v{;n_s?Q>ch`5(=_{sd{9b5k5&Ig z=htnQX}k;#{)CWFgH6*se@SkdCd;y>X@+tIQ%Wf%tVK9;;7Iqe^*iFycAQXW$DE(| zwBf_m$9(qOnbXoyP(Pdav^Uo)9hen1%qtHkg{72Y!HFB!tUl6hD_S(eeB=M^@wo@j zzbcb-%c0Kc%S_vM3BeYY>8?Fe2T>wxNbS@$jZLS7G8ly>Ref_RLMWvKAk6P3{W9J5 z{)zTQ1)SwWo%dxJkbM`2!oET!qj0UgP(LRZZoeK0=ixm+QElHoGaj7~BZ9m!&FylsFGp|5wYuw(vf8FN9v?;N~r%{Z) zh8rM!+Yd~(47?rc-(P*C%RXe;j6mI?W9^aA)0tF5GZ4oz7x67nN`bM2n=oeK+;Gpn z`23D>SKK=#JJhrOf}8K29@D)0<&R!G=3abzY1zNHXFF>SS9L|k%>3M*y=e013ue|n z_n|ny_VX{N7b@@qm_wiRzHL5OebjHun>jtLFXJ?@Uh~8U)hABkl*J-Zdh~VX>~rvZ9^M9!FFKcu<;~ zI)hOf%O&O(6&kh(q8O~z?hq{ufT=37EbEM=_Y$h96clW+3dmw%7(-wk4iHUN3sSjU z2($gN@2aXnC}ug9VX$c$gt8VMLvPkuMzC`1z*bALYJ}B0fl|s^Y*uTib+RNIjFn?( zI4HX}AuwV&7BegZhG78aF4P381v8;EynSx$kfKZ{&-;8D;t&7=LLe%CRPnewuFa(Y z<`L%Sf2!KHr*(2x*Y+Bfj9mD`yURqgvpll84GE&FaKeHSywLIKE>A6F&b@AYgr+Kn zs%Qi-EBt@<-a9_3<9Z*Sxo!7uk+!P$UXf_(LI_01##Cdv4W`(*#b7&*Q|!d~#XYXU zII)vBv56a|*>n)SHwh5xLcOh|Ew|r0?;pDoDxL;qovZ8_m92fx;Dl5wu>frEs8#9K&(azv&13;Q|2w5Fmss30Y}{ zs~09i&|rD;vrD@w9e2h0X$7PvRuSLO^E9{Uco7u z_dWG^jz+fSSUr|4`q3<_)zE4T7%aY8L3K0BA_R$R**#sXx5WZkU|5##^MMcO6s#wp zz|FEVtVF13jJWoM8w@cSz}}J-o`V$N>gzgKi{$Ah7(_CmHgsHgxsGLnGw-t!jLTrr zV(@weJ&e<}v`^}^)ywl!T-^@ryBMb_N~`x(8f)7eSnw?}PJ0uE4+b*Jae|Dida1mD zEU=u%ST|Oagc74zM&`zyKiY8UeB1r$>YpGJ^?&pIOQc{F%~qH2K#mww(0=-CgR4_C zU*34k=94&SVyxDFW%s_SiqqRp9@w;nodu%SJU&`iw)6Mv)_=Jljau^LqfcaGJ2suI zYH6=HyS>4G?EO<>55K#$Id;NRPfSd4UHJCpb?dgCsgWb{maMrq`P8|ue)$QpR39@t zo_E?hsl-Hc<&L-C``h1-TuRJYeE&nMic;*Szj^un^;^%>poomwPyOm)J*kMiOhHnM zSMB)2wh~|Km`8?%Ts`~7iAKdg{mBs)J0{lc*-=*2Tw8L?88|#^%J3t9{_TbXc>dk1 zo>)6deRS9P%V!TXmhIij+UEA~3?R6zb9Rb5fP0Kv-(TgAN#S54IY~`}-SQXPz zb>Q`P-~0NkCRvFdw>&Q-47$oH%S%r4!7*V&)ki=2>yc_KDr@$WPp=HE*>|k8v7@S= z){~xjd~_sKI0OIyG(l7yc<0@Z{&wV2Le}Dy&#Vfq{Q5$xCVtAKIIZ)_H(%8Pt%_1J z{#K^IM_lp`Xu;J$mk|UvD55 z;n7bl&-IsF0>A-)5J;@E$JQk~#5QC}8k@qil?ShM-J2a1QQgqoe!gaSW{@eqXhamp zv)y)wi0jn09=5w1^w<0x8 z^ukqr;Puzu+ob3s>QPE!A)yDhlHISrvwq8AkT-4B>W2ywj3Oi7$ zbEwQ6M-OgmJb&r@NmtwIYOu)6*-t*bGOT9bvC_u&>a*MH{fm}Ni8=D_mgd-rPyOufWXHj`UwQq#&# zA&__Xs?`q_CYVJ=0?-pTU1Xg-y=>3PMX4fJdFV>l-0Z0Enug|%3pFD$f^X5Xmns@t ztNK-Va<64yU-$l(`%%FIy*N}CuYc`NTNGVby^@kRhSY=3lHG5(Eqkqo5F&)rq_e8{ z_4nTQ300HFh?63cMb%hgTLPgdpL{fp^~f=x>Kg<3~NTa&F?O(yxC14}1Y0 z^ThJp0LdjtgidWc{gsS6!crf`+DnSxeq;UC>lEC4P|lzIiwBD?ZQc0JJ9~%;4ccdZ zwg?>m^&9Vgd%g~ZXU<;p%)_DOU!LgHB~FEV=1d#viIxmmIIr6rsMHX zuE-6L7zy5xaT>UBt1f=+)fjUhQHS#$hwPhN5Q<>p1`*z&DgN?RG^W7K25Bkx;~iyeM-^WI`KeB}h+ z`pq>RvY#3KiIdEiU*B^6k}m4r96wf6tDASf{@KAJY{tAo|Hca)E+x2d;j|IPwi-qe zZB5%w?LVa+x@7W5Q1{JMn`||rPd9#3?97}$DWvJ2=gLljAeUiwg@44-QEfSu`rBt;R0luGSsf_n%hfduv@< z@4^GpQp^e?5Vo%HV9UJ#KVrCQ-TAdA5HbqTB0D(1o_O$dMHI5X7 zAW6g4lgAr4O?Y-@Aml_$OWDsIX$6w+n4ENtpmbhZ|Cf(`^THSHAUI)Igj(ViK++nv za!Xkkv?SjhujfRB0fb1Mx@p&|pA?sB^5!S>>D6++jyFUFwEbn%_C09C$_a*sExmAk zvbz|jFD;584mS$gq_h#;Cr_021Wi~pZG^d_w%hK|B`5mIyz(DCUg^Gq2>|HdzYtPz z6=}b+Wy6PWz5DUD&;Inq(W}Yh7R(Q?d2hp>Lrli}Ng<7&oGm*I(^iZ()NiY9c7hD?s2T!;d#B^P^OaKXaaVI3O}9K9?>Oeq}W&oq>^i2jl0 z=0m41_5@5=a?c23M{SqGu1`+%l{pDvBn?|moM_-RVcD5MUX~TzmX$j%z5n4yzj^-i zHW2h(mTla5&9c1}K3~flVgg$KylMMhG-Blh-}S*1>hNkRr*T?AdcihWM&5!Vr|2S(G2xR%=%zTYXhcZMQimKT6A9*|xc) zokIkLBME49I^3K!`|hDZG>#NS#?&U};`^^Yx8(8P{_E3!zVpT_$6ASq^ihGh1L*aw zdtTmBd;q200OXo3hf|jnZ;%zApdpz>`9bZ~ZBBKtf9D6A{V+FFXDZe=p^?w%@@isLv@6lt2iiC@=!I=8yI z`oDL5i4edkxvlbK@tc1ye(m#j`C~`a6V^t=M*3=L5Ai8Z;M$195JE}zL&FOEy8dyT zvs;$0dT2ptcX-q5ryEZ;MutbmhKzn-Y9x8kc7aM=cyEYY^JR671J{}fop^lH@v8bm zr){ZA9-b04a)vLKIW$5zOz5pt!;#aDo`A)VJhdR0U>g$7)Hw9MTIA46C%!&jRe$Jo zSE?#;Ob~_v0HNd%{3g$_ez@z(j=E0Y9jbt?hzVJgvt7Zo2;!E&sG$6nb4C>Z@-M%5 z?gY#o6^C8fwNox%a(A+CVB+ZX*)wykV?x^BINf-%F)}nP#+3E&oaC*qTNtch z#7O_pXur`*r$$nJT32tZz1kxa7y^`5s@;6n(G##}^;7eM38pULl9MUlQqlud1ONyC zLQT_ck1*v?=J*lSXguKK#bEbsOjFDbEDC3s! ztl4IeGAh#OCbqrKs2Btc--Sa#tAY|4K6d2v1Q=CPdFt4vOU1&W(C0^4`0ndkt!FD3 z;MS1nn8Kw~!+Z4#7OJ*%SD!to$adrMl@Bcp>j`i2g6ok-7lv|2OB)YKT=Rtko8Nik z2$ok9hgIy|#vAfvD$u?xFlh0wGi+K@$|87oIaQOazwb%)*hO4HtX@qH_x$ znUl2nH8aB(tXjP=v?r|TwG*~u`}f+B-LUN8#}|ew=lF(WJj9_TC@yE{{YxepQDep7 zn&Zb$R9!uEx+{emG9ki0G}>CYbZR7d(01MpRQblXdRD+n&NN?i>etSCB17XBi1VT( zlcwn8sNwfmOnJjYq?3dpBqMk3y^})ZXz{mu9W@s&v~7FmjU!lIZ5&p4kMY5k4=oDo32W@dp{LKXO^0Nz@%+Kf@4RsY_pV{lEI+7dq^cw5 z_->PXKnQDm{ZzxrhUn0USo6?(=S&WfW0eQr5&vif-7*r!%r;= zA(`5Q%bZ-ZrIb+{(h8!CUX0VcLB=EGy8Y+49^>*yA6*bKh=OH1cc7w`GZV+mnMx&$ z9UcN)wYXk9@%3@H=~YSN!n7JSz>$Y>T8*i3nMeLHLntgc01O!wW%Od4_RpFy*C%0u zhjBVQ$)99INb9Al&FALH{jG0uZ)^f*>9IviFblR49-_Xdzm5%O! zMXR4)7)%VJCL}O`paK9z`J|zz4P}%|*d$+~vgY*D3cj*nj{sw?L?F_CDXG=c$ zZ2wnPhWym=F$kX3>v@i6y1G%1tl$bpU`%s6ir0h=GZ9@)O|oxBcD~ke;ZGmz+}~u# zNgW>p;8~p!0OX0Tskk_Lar`AfQ0Ee zGa_Nju*@mZxkJ#7eGMuzAOsf&+_iOdo6S0lABHN+dm`x2;U?L|%08iV0PL=*#^QCR zbc4dM?T&6iA!U&;8EJz>4Z4oK^~vEQO@^G5@i7RV)tMwov{Cgc3dKJ>pj-3rw ztJMcdqK)Tq4X%hHWAB#iJ+ef&sdRfck`!5zBvEv80^>Ty$3FlPgi34hi!2={w8bwtjTpoFwEt&2tY9lFsAAw(jliyS(6?88eE zfh2GOB)D?PY}Q%*AgZe9iO_@&_tL1k3dZY9Lk$YUw%gqe05_WY8o;3=&4%pM@v#V; z(V1jPvRHIb2T)VD)?f(m1<1z8NOtN|SFZY5-0ssSx4(VLI(*u!Rihws0l>2YW9ugE zvVuugi@%>)k0jA1aJa7;cD6PJ1;$O!nGxy6Ej}$x<6==CnI%Gsa?P@hxVF!-M!%>r z<5Q{czBmh8kpscOgH@ zr_ZweYRIq(LE&smyArGQ8)j5otl|@@wgOj=&1?xYoApQ%yLcYg5R_M^kmEHWy`3T_ zGaNs0@OYTYf4FJT>^6bNv;>AEqtV}V1H@W}Wlo9BO=UKJtEa<9n#r!FM#+>j+=s*u z9p6!pMfGQ#MhGKuuC-l>)0l_)4ys_wu*@mZIYZ7y@-4>TEa`b@7# zH~@n%PO}I&yQH4Jj;Ua{O0<<8+x7A8D<&TeW4FZ@EewcFGm--4#?5+zIk3-;$-paMK$q9X<%5`c2MNxo)eoAxWVDeuT0ieH78oBXwHA_U! zczWrIl`EG%`rMot%G_Lim{XTaJV9X?FK|61j*CF6*Xb}KEkSkIe)ZFGsHNGO6VBRl zqs3q3MMVaXAQeTCBmi+7;1q4IIoH!p=S_cX>5BX3B}e$%LMWV0hvLr?t~$xdn2j+&Fs zx^kn%Ule8DCBmd+?NNK(r{z#fbG0Y!S4zWk^T~rfO&cmP4Xu@Z1UrrqP?3-TDB!5S z(A`ew&A3)Eg+dDGuQh>r&S+o?C+HIsbcgNd|5?^%3<{>oU;pS|?-X}7(^x6rhbm5x z&_f7}lK_G1RR}kAb;t&tBJz?9aT3RH2r!5wpw;=B%{b+z(gnbM>x%XC-A)tKwSEHGOiWg+dDIM}~}Jh*0}>Uiw#QcSYvPXJ42V*wS=* zUyZ1vpaOu$YB;7QBuSQJ8R8^?5r~&G(P7c{)S;G%a~@l}c4mCIvAa$3(PN$y)CeJx z*85(wY?W9RICHF}{OjNT^X)gwyJ;E#sk0ig?WUVr7GRhxqXF#AfERoc6V%7-b)Q}V z{c&jev>Ml_*wzcpWu?BomhE3dB=Vw9QUZOOEywuxbfqRV% zp~4CJqy+5|M_;FSWBoths3LqWa<%^`8$5e2@3f*wqBJ;SPt8gQpP$^gDIvm!(#IW*H&F1YS@B`U?heIZA^;?5;HW*@ z-LCPNJ~hPB>PuT2kbKcSJPHEek8xV(Yw7El5Gf+A4^J3YG^Q{=KfiEH(XfPYJuV?x z5PXsn)W=*+8*T`4sD*LZY?by_Ro;xpm#$bcFE!M+NgbD>KVm;Wh?*vxLK0MIby_V& z0${h75vaka|4A}Y-|qR+eF9#-f%GK+0H`&@<-^UW)gBs(v$nR@PEMB?V($vKQjL}6 z&y5(eaLq5jEv<4o2-6V%Lpxu5;_1IOq|du6h1m1!X&J*7eAXF|d;eHVXPc7~5CAgA za15sapzxftv&}SgO1i23;}s*a7cF_}Uk58YXlt6^!R^0W^M?=4+ci?BO`S1eW|Hp2 zuNIBS%~a0-Vnp_$#cMYlzRDt*b#W}mbDY34t{xCsU}8r^5rSWu z1-TRturkZ?oFFnlpE+-KoU!=nacLRT9(napz2alQEnQs|FaBIlfWf2^gm z-Nj2H3w$%jk4skV{mrzDVe>!l3e2397oRq|z^``K@~GisAAIrGrOqx==3P81axCym zpPX*0_o^6>ScY@4vX|B}$8anwyFca`L7y>hX0q<2N4wosOya2Ng{rG(*A5@LXw7rq zmeg>v$T2ME;$#41Zg3SEkx9#SyBJ<}Q|WR}hYgyB1RUD_yERX*FXhZCOj3}dAcCT5 zc7OQz^!!Pq^M=h@y@80GG;2ZxV0A%5V9So@9>~tk&AR9L6G5?e&l#e2u?mJ^KoMk? zl?Bgq=2?}vnG?n(tM>ioZjVN@ z^Aghw3;b$#E{hs5_Q4l_eZkR9N}P*lM4khFLno)3>OWpNB74!|r#?M&l|d5Aux_Bd z5OX=48k2=N``2gg$(g*tg^!q85W>4er0}$Vn*X8gzgzp#2j{vqfOCl++GozyVWYF> z-y82+{LHwtjA@U&a;Q-@`#?cL5CTO(go>)&{oxZc@+XbX8$NUOr+ED2Sra1xs}CB2 zweEcGf$ZG8tb3k65g2#(y{R{~ti-Wg{|YNS>+0yyXH6QHtlIm$7Y;+mj1FwvyEHcE z{@=b*+T5k;)(oFFCt+X>Wr)cx!8dclxD*eJ?g7Et+2-H`1p)|w#4;SiNdQ0$$c&)R z96vKzfAUugN9JZM`puqNE^)||aRu?JeJ>22U1k}c<#>+cSzf*YVvoLd_^Lx6k~MmI zvi`&`7mm!$SorImHGKAnvDSTCf4BA-cQ=s{;(3sqm{dFshW9(m-+r=GcQLRKKfDF89NZ|>dW{IBl00mzTOdZgA##tbb=H8#43 z(5L@(xTamuXO5dWn3`*z`qL%c%AI=kU!T2x%Ap3G)&3SdJ}w6EKc!2v9=WaY3NQ?F zUn9B6eG(kU-Mg|f>+~Hq@vbyzHHea=Rs&Hq44YgKql|I(^dy8Im!jjN%?4jXkU2!H zUj2&}5JqZ4CKk>%s6$UzfFT8=vJ#DqaE~@a?Z>j9X!cZrva&1~ckg6D72qF%ram*@ zTvDeB37TSz4vr2^q{zpfZCAyn1o@}Qf{)5CZOmd7ophqQ#V;x|BReC;!n9!IeDj4m zDkN}mqQ&kuJAqs z9eK1$icHEKGdjHCFU7|2obj2-cvMf^=+Rk;Mn;&X%T${cms1@(btSWIubU#L@EtC+|Fv$xhovnGow zGhjqW;E3nckwt|Iebw>Dd$o)8W3}@&F#0Ttj*T*zjDA*9mL$Pl$gC-%%G_UtsFd9N z5s{c5?#ELWjBzno#Dtl?lGYfm(X9S?3yjr!sC1#^Qi4X!e?Xm7Wy68O>4S#mxwpYO3#b?v|sK73?$MmWujJWeVc?~}fGN(|8X@SK7}2m#g$CQn-XS#!=^ z0%=zH$BvofqX|9T3zr~hrc7Dp=M#SpCWOv6MF$#9q$$2|K266Qt&*Zs^Kvr7jSA1- znf?kP21Evg7tNd`k`};-0LSoaPkI}`>;O%0Ujtyps2QFHKvlndQf}2IKAo>`WONb( z0t&P4(jn`GIw~Y^vMDYyOxN20;J8^+c$GiPg7NoG=2Zb<(XsPiHbkDfET`t@42$sv z3aQgC(J72kivx&9ecD&j!wfOrE}ZR02cIejLkdP^CmLPC zJ=#o_*`wf0y&Hc|r-&*lT;QvYJ6_%D7nPNnJtEvs2^u}yK!*&L9gmnW!w6M4L^6+j z@f#wRoe{1fmBKNL^mM}U>Q=wVjLf{eFl`NBl}@~`n;_1KCEqo2&(4-}7pdj5R%*0K zrC8$i3Pz>m=8ufT{4gu8vh*{3XwVcxWKdYBA=I$=Hz@9$yCa+2%-#}Z2-oT!{&@=) zrzd#Dvyp@Z*1@?3+$X`H!n0JwumQxUBBJPl#HS+1unZ418WLV3 zgYEx^M&rfQDsYI>YDtCXWd%Vfvuq#BE0TGRWjPTe)z`>fMP5)82q01}4MGTv`tl;smrD^@hUF1?9hGjMEv}Id1B{|+?|66&!;28d zk)re#2i&#bJ!i0yf{|Jh2eRlAmA+tFy^*~JS`$eOBjGAqqfrq+V4VyjC=g;4O>1ct z5@lHt1rKenMx#*^Ko%Gm%YXO%%O4&R{U&_T-fg3It=0ELLICp+B?FOlx_C&lmx}-L z@y7?M?HZ$=A9>HRNfT#A;*21Rf;a6jf=J{ANrBj44d7a{2O`IC9IrqO0Du6bG#U+| z@VufR0A-%@7_>isvLdn!=Y`vKmSq`+8)#V|^X$KCSw&(wnNZWTr^YPDD*$4YTCGu2 zNKzCe3H=C}ud87n-!$+Z5U#b@8*~cq`aydc8j3uW+1a1 zFUb8{ogfIzwKN0Obh#u#qo&oUe9PZR$tAybujXAkQRMS~L< z;#kf@w>t0-)SD2xxl=J9^mMudrvLyT#Gu5xdRQky(Ea6wA!$GscP~?b!pWMr;j|0ywEx zd4ib8JSPE&tMxu4Nr%Vfjh$>x=tP#t)QsG$2)!VRBt=0?6nLI>@-D^AI@=c_(oKOY zdHn(U?ju8Zy>MZ(F=kixa^$66S&CpI)IQ@ zB|ML>(GV-5ixv6@*z5yyfHe^UDiukRzzr9s*7?!^0Dxqf=Qy`tgCsR7)ip>VNo!T# z-BWp<_l{Tjo$|dVS8sLtKXK!Et+ing!%3Mk2c~C^QiXH@b9hQdR#qVBc5nb<5Mprf zmU;7L@3w;H#Yk}2*}lf_ZBVa{0Q%Vo{O2v}UWrTnY77BD0_)^l?v{$V|G8ley`b?L z5c(_FhyTD%(GS@hWWSd8CWt`*29yj%NB?euLBI%PO6D*>UZEI=gW&~$VK-8+x8GfF zdWb_Wc&>HiK|1D51cToR@*uCq29B9tY7$UjSXor4*t~Ixw7&^54EXP&&$u_H!u#d&`l;{FfiT{@=s^(mWErGe zUvEtSfLc8e+aE3v!XJXIcR79Wx^7UnK_2%$y7pQ{SxNdym`< zNPB(B{r~7o+3(F5h7XeS`k-3_s|)*O^#0kqM*VU@?|R=tg|Dx1zn6Yj4ZX7Wa^zgE z;Pr|7ez|$}8x?qceFl{5QNg!Tj^kLCy_J+dDZXe3k;vF> zE&#E?D97BF*6Zr(6h$d0DCpxU`+h_@ee_7~1tUT_oo?^my$uZwAt50bFJ2@`@+Pgv z9rZgjG&DFkctC85pS0Wv+aDrs?ztZkTX*&!iaA0O$GBI!5F^y|?Taz}Ke=t<9uw5` zfA2NFA1VmBSs8wKNs6MV)oPL?1wr`XrQM?3R>SgsA7$8Z0)@c-n0@_$&|{j0gTIWjWxw!QTK?uX2OnIMGRL#?97-f@nv@gyS% z0fGP__}yC!?lIk6AcW9ju|!2haU6$Xm?(;BwHg4xah!W)gAh`y)poo6<~zqfUFpMC zR+K@=A&_#Xuyr3{3knLl2}k<>a7fB?WTpvV9MK)8Rp730HRmQ+y`S(X(9f#><4prG~Z z*8>2SFJIo&)TCCc-9x!3iZ|LU1lKK_JRm@C0t3%0u|B3L&x0R<9P5W;?#33!AjGh~ z#lCyr5Mp-*TL_^fNfwLcwvoU8PyQ$We-ng|AP9=`pA7`vC%1LJL;zE1wQ8>e=q`qn z!1YHv6hNqSw1z?)!*aq+5B?*7(;5OIkzpk8gZDkXOM@PQ)h(dtK6=lRzjeD#f;M|WIGnvgT`XHVpYV~T>l*L&S3s)B-owr<_}t6%*J z01y-uGFsxs{{+VZ<>FVkN04!Rx=nsGR!=goty1KeDWseTq@%n3TZ@FkSn=t@@($U$La?ca1r>C9#^ra8?HERjc?mv9y z!z&)li^6+&^lxz4{fL+&002p#`uG2^`O}Js#m`zg*Kgg$Tb4ZBy!UMVox#?#yXmGV z@fHXn&$ArMA#Wui006@w2LHQv`F|7#%&iylpY!+JHu+vE{BV7L;0*SC-qgF(fs|X> z=>Wh@Yu1r7b}P}6r4h}+6bt2vXn z>)Xm6E^P9YXv~X1d>|z~0;o5ydw&YYkmBC%LP)_>1a7-hS}mH{uQ!&-Jk)BCU5T7L zDH6E^fZ;g2W^mwb_*(`6002N|vn3^G(a1$}6BJ332?BE**zClx!trxvhSr1Q+v>Mo zc8s>_6@+e;98OUbS(aIr4G9T(_St7mCKCX_X0wHags?0t%krSN9XxhHkN|x9#gBhg z9MX}VxF*9uu(rz5@=pJ#qLE2MbLSc|Be*Wj)X){@P|$bcvH#WX3X8#Di{87QK}xheBEO+h{k}6j3Kn=%=?Zzge?F703635 zgnjp;`l<_XmENc$y@y4F5RiC|AbeV0;EkbiRC(84D?gzV(=AC@0IU6(sOgoK3V8i9CYt=cE;p2r_7OlEqV zP;(uDIFdL{LJ+4lYJez$(prsjeNC$a3-+Z{_Fe*UgfK#_(WprbApk^02=ofJh81mPaI2ek_lIT$l;NXaF~l>_Gz=VX$Q(r9Ty zP?<6(%*|HOoqJ2Hl^|y3gJaA*0P^v}ro{Yv3!hk&3_-)DHJ@MJdAWURm{AdBHyh}k zz}(YYs!GePp~kH7S^z}<+;oD#DmGAR-gHN>1;BlcM3N-;aUst`MaEP{_Tu{Y*T28D z#9}hz5GbO}#m7d4hmKxQkegKy>ix|(s9c-3Sg6%e5Qz-)A08jCIRmaZbjkm&GoY`K za;GkC^27+_5C8@cAhkNOzZQe0?0XFwEXxm5Z{S>Y!<OtcCziYjC7MQmDRx3b%sH z_xuo1pjz)bbM%x$HjT_qz?uJ4#7CsaQ01%C?0@OqZ;$UOj~kmg>EVY)M`AbfG9iR; z%3#*CUwn5*H40B(ooaBXq}t-X>=PeaSr}*NF5makd)triDUU78n)cN4z~*B$vOYCu znAX7|47zEU`<02~0D$k@Z~77CcAxiwBJ1q2bxC%i9axjc4UvS(gLSUirsi$0zWtXi zhvfWOKVLfb($=rudv8BEzR~CD#}}XzFTC|9H-Y%9C!SgrRCLa^j>o zt*c_k#w!l}kXfT#dtZ3#{cq0KqOgovYoA-CClndTOi%aKQ#(F8vh$0r;C@UmgT;aq8_w+Z?fVz-r3n15fL%SR~UpC5_-t! zDKm_aZ#l51DRT~~NU{Q;0$4{Euh5;{Om{cv?gR|zmW^unDPI5q077#IYm?0y6+j3e z1!0;yin+pxv2;t@=@*~-+qaht5B&U>kB(GaK8j?$Q`q*V^xmz67zU6?7!!u(-Deyr zUi|o26AH+AU}PZPeDHAX>B|Y3V?#tif+T^vkM~}G-WO0hRI~ldOWnF5_f3?){`)z* zZ_12m(Mk^oFo4|0wiNem@j*-QR`cI*22_b=<~yAMo-+!H0szEth-CL;q{xuKY!w&F zI*pczQ^F-dK%~})SAG3)sof`K*7O*qo0S3ZypHIqD81Zia;NOW1Sx>>ft4FQS)%1G zR)!E8sGjF=;Wgm;?RuyW(a7acyy)2BtF z9u8pu4U$qsP`QzTy0qzwCOaxFTxxZ=v z0Dxpb7*WIaD_5I9@LaXRF*3pcgl?07KL1>=*4HEBZUzv;utD04mthftlor(N+;^@~ z3ZMRqWapui=k^^5j~pM+=VkJ4V+tZPL{stG|L-qveE3zw_06^HC(iCY znG`?d!rrqDQuy>=Bs&hAIJM{Ogw(FG@~fReL+=X!HVIsRtk63i$nBBaem)jL48w7P zqzFy`KK|C8X4rUQugh7354-<`@k(dO<;u%lbWC=(LrNc>ZbX6{oIUy8h|wfzJ944o z_`bIGn(7wPI&o?;iMlFE%aB>G(@+NgFg2Yyj7v-o(L7I~zd?s1#ZB1*~mZB=p>Mb1KjjQiM21%&0649q zF(k4MiPWnxpa>3;)T;s1&?KeN>vd2|SZPrd70wVhBw19M6iy~+osUL^0VMJO@HnNW z`%+30&v_1)$viT}4M`R$gCY>Lk3~%ZS!7w3mpsu?6$I6^o>r5d=b~~A2Z>`C4ykDs zP7n|hB(Crrpdfp-ww`+xS>Haw}mFf$dD|b|MO$ql=MESa% z4hWE}(0<9;GZ!?~^aS#X0&u*$V&}E&=f14o-&*6ppHZmZCmSoa+hc#3eD2FD2QReU zpQg5P$`3w=|HI|Z-M>T_1TMB)rJ@{_NB4dHX0e>#oQRiw`>ix~`HZCT^X?%NCT4^J zS1o0R=f6E)Q-APeM-rV>6sFUtQ9lB4iARAk=@gzC42O(~HaR*Rh)`>#+RbNLyNq*J zKRGFkJJxMGCSxR})@T$Q00aP`qSb1GqDVqz1n0N+mY-M{h+_yqlF&z|4kuxHAWF$h z9STP5-_x`6s!hLx46>z;8v$h3=;0xR!aIfU^00kPNW6eO~1OQCYalY+51*lp~ z(`D-dz}(e!i2|b82Ld32TZrBdA#}N1EiElKxhvwq8==6!K%Gt}Nz(PhI)(w4Q#qZd84Y)M|Sy7t1JKd)QA`7p@6YsEt=3*rr&lhi3S2ezNw_}W+GV&B+Vv4SA= zbnS0(RaP`qp5NIRFm}Msg4VK-e0$VOJ4~_k&z)p2zNVR-g|%B zc})r)HFjBk&|uC0=iV3Id4K!)Iwfr6%r)1X0XVj)N5#=sdD?P&%X5OHevLl$Cp2l z6$yJdpe8U^TVsP$XHE?}{lOcry!Qn@Zr+NO4`fGZC014duGhMbe(=U0-}?+3ci-~m z59B8q+RnLgKsi%ZKDH)beR%ts_U87+>f%qgi$yD+njUlP{KjAZi(E*?JiaW)PhcQ{ zIh&h0Fxs}K#c3;VXsA6fASE9CvquZGWxL*5|L2Xxpy2+;moLqUlx!^Jqtp~{Ke_SM zFUT7In9+nE#*}~i%Cj$RxUpG{f>Xi(P!Oh(n`?Iba_#T8TruAN)SAa~!8e~?aQVbf zoDi#ZRqWVU=FktHnb-Wm?_TbwvVs&sMR%S0LZm^c-hOE7+ZV1}Jj(|~ho!5J{q65Z zYq7|TSx>B89#XmEL~~B*V!3d-Dob7HLbr z`G4y^*m@Y`-nC-Y^1P&ApT;9EA8mrcxob0x9Zsksgw|%7=CxKWkbu@|NIK>3=9AOd^Z<4LWm%U_V#v%!x0k`BM8C(pCN`}?x?C1MO9Z< z69f?)9PGUZ;AI#LNOnb)lOJ7z$X8~ga;L?BC|z;V2w zKnU(E=I$9ZCOP%5J)L!xAT^Eav`Kc-Cn8dRu~MwNqu35A5W@+=vv!9#0Whdh(^a)) zHT1|u6Ia5MXV2|Bd@fv%AGuhL4ZHvO@u>5B^`!%tX2uvlfd{^UVSZ5=>6(<`q0Ih| zF35gHpPEzq%KgVbnCEzF@1`~^Z$eVrzS4uI613E*i{-di3ECM&{!+I{81R;zySDGI z)8s6eude^&fs#}H!eGvTu;9!Ib0S8Oc=wSKFK57^tIe&Nz@(fsK4E0K>hs^7EDKNI zN1Cs0sH<=U1Wp9Vt^l(}>AX_aO7Ot2ZSV7&!Cf03zW-uniOuAz>#RR}Rv!Mq#FgO0 zsS5{>9g1r^cd5e?FwF4Q_AhqQu?v=Epo*`ze%0gGW9zH{B|+N0DU;F`OY?W|)UBM#zvJ}VC2ghYD}SlI z`1w3K4l1r()>$V^ zrATDEa;}0&^f~j-0V-!|Ivhx=5-(PX{$fOqx#`1?_LbAw_l-*qG6!jNb95sKvirz| zE5{CZId|{fQlidZF=Zur;_SJ-ht9HJYspBsd*PZ?s&U_*t4vRe$w;&yk_Ptb)3!Es z{{3raBy^wXeCcSro*q`&)8>>#g2FISm1YwUqa#>okVnk^@6LzA&UfsK~Z3x8ojIPu&rI4zhup zz+u^Q=S>ck6V|=3*HLx;e8;wT{&)-@)fA6k*}dJKH1FB0u`?}#V2VkN&6w$L&KnWU z9mQ3UCNX1R%&@{0_e5!q^qiwom&^~9YrgKRzH+{E>vbi}vO=Vh>NcNgu^H#Aesc0n zodKlU2+wc7P*Z>CWJeO6IQp71V90P1OAnM&GE;|w;eF14U|aIIEQ+xqNsS`nvU+Np z%TIk95Eh} zsjUGRF9HleKnmQsGi`NAeoxGKBF7p@T;BS{+aH|4@|xqZ%R9CUm5ZjO8l&T~6X)JH zHEd4&pMHB#CasbAbNx{2m}rxu!wyLR5TVg(fQrNofl=9MbMKpSEoI)lW@XsefbM^; zb9R{@eCW|d;oZACUq4iLvR+D$Q#zqFHZ?Y5roU-aW|(vq^9dTBd*98?I%cL72mr|< zZH!9KS~71+NVk8->!)f?wM3bHwF>6(sL?7w(?Z8Ul@SIp2pEww$&w$WBvU|aX7;jW zlZ>FL@@Uif^Ci{whfa1Rs#C{DSwo_-(%rhO`|Uoa;>ZD6>@qG}y>@Z9a!zSH@d z`@VkX&Exo}X0JALZ%m;UVMufeZilNUJXvpd@r(_M0x6@b>@RP<{p!bH;hIO5E=|yM z@d}Os05KepI0n5xcD=JBfV`hT2oVAV6@XADColkh_nCv+Ah+#42>?hz6h+nTUjN#= zUw`J0+I;<^f~GFLJ3q{*nR!qBr|W(`{>v^?xKGNQCnp=Z9v7?TSzt|{m|nf^pR2Oo zhzKB$btYt`^+jvaGE(?Qnmdcd@KCG|zwQi_dQCM8AJxX^g6abBMuX zrX_T~wQj-iH^Xv61bb2@3ePF9@Au^OZ*PauamEP~)2m+ia8=fu5drv-?%|W}OJo^d zz#yhbj3&fWru4!SD~1&s9N$D>tR>wna;({Y>XmOBL2&NcOyjMe_kkDyK+>m7nd%#0 z2;W-WH`;R+{|IIyN@;WCY0PC2=*Q%b@xictm$8)51vw)~0HViCd{FYc1K56F zktE4#3A9*zkR;j!j__5(&ep~NYt)p?iQ%9%acyBp($HbFlLG_+c$RnBx^TNJLxce! zin7(BLpp#OyYxnbzX6~Qhe*))pe3+~AaS;{gV1X&e&6j3_`F=5nV1{Qa2E{5Yr|cC zXFyT>CnunnGk}K#6a^3#5~squzAo-qm@;b?9#Oe}lbk;^%Ia@3_y+h`-J0^ah9EH{ zL5yu`gNfSk3^QeGY?O`p84)CPqO={E{0&GFy96HB5EKr9=h`a76sChtdw9j8aXU^O z-uBMY-rnmjv+?bxvc1{+K_y_#YH0trM_Uum_wx8IrHA1et-fQ=o%cAmbU4}7lf z&YU@O&fGg^&Yd|&Wi1TR)Ie*yj|x!=fGMFA>$>K4MSI*`OSX9RAklpFDAO7-z-0s^ z5|$`#=1RrO_YcuQSgB1Z)Oo{ zQwl;1T&~NA`T^SJFpLaci*Q*5j{wcP{`mK;ThE&dGDGTf5nYLJMMt*vMdrdxMtwcW zu(}4g3{h z#1~AQS1=JEWJmx-_V^Jq5=PAExbwA-8)J+mNh&BPc=z3R-+lL802n!PWI;i}Wm9u^ z)r%(5gfUacsk|E`gQ#j)(fmha#_LwME)s^LisHzhJ3a;M4n+Zmb`++f2Aw|bBL=>8 znPbMF5Y}tJ#CZcI0sy5zDMgNNNw!x9#9$n948g;DdN~Sei%#5b zclFgd1B8(ae>fwnd6)O8-Cj*k{3>U_U5TvEFJp!wQb6cGG4^-AJipCXGwY`lYS%no zQAP8Mr>8O>uQZ6D8;Ic;#2|_g!k{RQ-kHYRTMkwoZ+NnAoNwP#Z&e4*p0c>Z6h#BZ z!CzyMGkthO*W!vtAPH#Fm`)!GUmu%?qORrPW!XNeD< z-sd}I$uBC|d~}BxQi`gIL2J72$a_seUzaXtV=+jG+v+^WHc?s3rBqia5B}4iH`kj- zF8blkZR*oW3hgw;h^#sKWr*)=JAbN*`9)tVIAw3ZI;vOl_Iya{%$~!-FosRQB;?(p zx(-oMVZjeI`%=POOmb8x+J!~C%bA*vB;!q5qsMV%9}i!x%7u7o8I zn~^=bErGa9)AD;`qRWb;bKxnax8zLdoIu<*sO0v-@nGmWrMh-8f%xbtcXObKqNq`f z8PI}CSlrNQw~yvnvfq1X&4r+-;l6{$EG?XfLv?jslT}D5o0&7QBR9Q45FiQ^0iuRx z6ivFvZ1Np>{fQ~h?238tM~~-QM5z@}(2`i(p9TGE?)M2v0syFCbl$9ufD(hk)yD+y zhR}}cU@%xvQ1I4UZ_S-M7XaRR>#f+>Sg+TMF%BJvxV&C~s1Tmf#}mpmkC3Wx@k0xe zk)enRK@4c35IeLt1BNDQ1f$M{yShgZM&Gi`DIg3$4MV>u+;35~UyV*{cK|RXQ5FRQ z0Rk{oDcIC(nSa~OP0#=SCuJWrMmOjr6F+zTZ3k2q;J(z zKYsB&VoT{edDh4{DX1aJ5?h$v_wn;Te(4?ElHBK};)!EpPi*+>Gmn;i*z8Jl1p z@!mf__lv1(YXY$eH_g7kI7SI*41glED~L?fEg%Oq_$swhz?2e0syndtmk+mUrz;{7 zrp|e&a9Dcq=&^tT*m0hI%$jKmX{Nw=w>|yY z1spfxhl8D75hF>o^_`Yl{`DR*z{eF3+ zgzSL6G=aGPk_6)ZuTCIN0b!)8q9O$_&$vcR8mIDZBnBl(4TzzX;eZfEh<2slW!aBF z@p~0QOi6vFEXzwM%nzpl1!07MsKKD}b>sV7q>C;|P92H}*#7TpBD57Hv_B;#Cg#__ z{xtx^#Kbf-H1Ir62>J3;N}Utj10Y1ZbPi#n1tqop@2-WrQ~Gvg-nLBZCfsjTwpRv1 zn8@vGcc&uBII(1^P30qyD2gblc$!V&BFqte#@uG&leeAF)A|;T9G2`Nii{ahba$WP zxywkCIVmT0Fwg1~z)-*~Nl<+A1jQ63Nl-j}lEPn(f~4T9b-={MCM8b(p+Ee?(7T@eWA!Vh@o1LCf<7QU6Z(QUMJDR@4BNuSRx=GluRG5 zg?fsUN~b!Ji9uACNw_(x@0f)qe&>#=n#h#lBa4PQdncdw^BydUB1$ZtYEyYP6?KLI zktUQ(AE)rK3F#Sgct^sna=q{9p#w6VC@3;W_wz|prp<}=B1%qN8Ei;K+{ndq63QyQ z+SyYDea4I#I+*9=F{9_(ndH5+Ln(ku z{PXG#_1QH|hVYBbuo32%sdIjul(DZeV6J|8uinbx@Lr`wb4;e>?I-lKzC|O3Cb=}F z+0!T41Yvy0yMu_TS<;G17HHw+`|2YLateylESf5{DIPOSx0DjyM!P5zTYBf}qf*%* zn>wm&p%GEOuRf}OcK(>YcHj0xzA!m*c&;ER68;V>F9EGwxw53BL=?r&A7B`U@p`?2 zAb30;0FY%_mgUYbNJ^<72s?M~v{)&a-)g^_R1P&8TQotl|Uq%5a zp{&VlGMPDs?0?}GYc7P1{L#b1OcI!dF3tdx#UgMFr4&(3kqv@7I|EqWW;Xeby#CBj zpV=Ax;Ex`^t;jBSjR%cQ79NA41Z6N;ISfPzYK%o-(Z!xJKp5Q3U zXcw7;MpXL%)1-C|L;Eph5*jU(BGx1b9rq;z@g}n%w8=qIC?{~8rHu?-qr70{KoLbr z3nd5}NHYa^(nAOoN$zrSB?{Nj8DN+ab0;s$O>A=pFrA7ZM3kUN0bzy(Rg@_p z%pybX-qZHd9o*88{|Ong++8LiH^RjM*1;j}G+Iz6*A&JKW)NJ#iPcH+U42wtdAnjs zUH1FLj^?kZpsP_U0z(aoN`O%4Ac%o<^Qhq~bQvQM6~51qevy(+S_2w~8R+hxYg1nv zGcr|m;3A@W_Z>)FP2fq%A2xVcEF07{YKT6$gFSp1?+(JC$$k|OqKHzsiZ2~qJyn-h z?{tSyMn^*O(2@*Glp6gm3!V?x>Cby;XAPWWw&QUFx+*Fv%IS1o?ScAhfg2<9?^*ve`2YYQ07*naRI+S4$LqS{Y#Q1= z*y-0!*P+Xc?RO>8J&kIo-@9F0i2?v-7z{d%NY9NzRF@T_t2dic+SNMf)*dh{CNdK> zV#?wYP=cbQgksHHriX-dT($pviA=h43zzigl1g1U?Dk{#`sja>*+< z&s?_pmntPRBX<1-T}tT+ZK@)UiA%(Ys;Wj1f-9&mR4FuRa?$DI=k=L_5l};wL{U*G zLY>=eXQKN+1ptgFr9crzS03P9(3Qxg)kAjxV_k*F#8gCy7LX~vWK6+j(tF(iT(9}E z;~jD1yYwA|BuRbx^l>_!*H#+Q(b4ho@hr{t9m987wjq`{j=N#` zY`-xmr4(4R`J25EsG$S{fG}@1Ut_*{@U;<|a7^Z_Ht{J{#h^kdAcQbu`i5iRI)GA& zut~W5m?DIj&1RNmFPmTXI^nvArX#wv#bUXU$vnTapp+&hC8edMU31EYVQ89mm4&aA zM&o;p@2qSor5wkdIdkUZ$&(Dj^capk=)pBa==rLtsYyyo0sxNVZotd!8qb#<%lwLp ziq_WF8?&|Kdx7h;)b5=4;{PnZo3ibQsMplg#IkJ9eA$B@Tyr3V2qCSlt!K}k&CAQX z-pyU(`7)H%At@=z<#Kfn9@c{%{2v6RG%YPHw3GJR=A{Qc_|Jr{>xN+@BqT&fM_+A@ zpl^z6KVK3;oK9zSbo6)OBuNi?@a>{!rtCovt~Ww+WVgiZ*8|t(`z0YH5D4_VDSFU@ z9`v9GHwGQ&%B~--%X}F+xYqMstp`2mK@WQHAA{>MU$QLAvTVcqB6OBa5QKvV54NbQ5Ity5FW64=${b#{?QW91qF}#{0Kc|&zHPKI z4A+J4(4%BBnK}plHJMCDj~n^ChL!VzHR7BEaOO z>BD-^gYP3k4|yoWW!LdcDX>;YxL|6301Z`^WLc-z{yhV%Ih5f`);s0*zS8|K0RhHr z6D&NHMM+l4H|3NP;H`FSXgUDPF+>y<0t_!WtOAPwfGDyk%Q~g#l495-SOvj^fhdwF z$$EQSazZEo`U)GnOS?Qn0zs9vIs`M{{(@4apeSpIVW>g7HLX%WjM*w!Olt?$uTV_p#>PfX({4;KJh)-`NeCe}n{Dmdwab?;|B_gm2qD8Trc9YqT3Q+i1lorQ zB?h4(yYt4ddsU!>w7uOZ{icbduQ4bg6c9$VZGc}>o+$ZxRKITdtEPFqI(Y37ddPKM zmn5lk(hWix3){H+FK@o__NH@!0D%_w@L5ZjE*%-Ii+cAwfB#PV1UMTyvv&E3pf!DL zX#x#|QWRe4{+D!wAl61|_dfN^{~ULXf8f5mGR$6ueC=f>r3T`{$m!3Xere6_^(WGc z2aUaR>4@|&(onVIxffphWPf#P(tufu?iw*9h3Ea%Tc7?@Td}e`mlS43NR>NZd+oVD zZ=!_@?_0WXSc={vAqD{>SU5yA`N|W2_jbn&EWPXQ;?#ysum9yQ?`}CKV7)amZ~7hg z-Z4Ctw6-!%+?m6>ZXcdt2IKs`*Is}AFKfxj#Sh-TaB#BPEMW!%CCF?OKi|FIZ-f^N z&R|4hWzJqI*smkNi`wy9v!+Z)Lt;=?jjOY1zDoSShGZp5V72jxYNDupDdme*t}Na* zhCltVQu8DwI~u)`IeTb7i(jVKZYYPIBVE6kUM=K?MF`!)G)1|$<-p<9> zmhfem{N^3}Hi6pF_BW+~I5TSe;#i&6)Vp+Y25t1&(lb3~q8kP=fB-@aW)Kk4ZcVp2 z@M!x74MG98=RgYmMkyu4ptP+n0-%)WI$;^yc=SNI*X=5skVp)IF#|vhf;iMvaj?A6 z6)|=~GBqTFAOM6Ij1fYV5@HbA>2Cv@oZkI!?L52RnfZ%sJCQVl5CRk9V~0=8El=(L>8_>YD&HZ?1TMySU(&gO0A> zcPh|UZ11Ln{bHitc;}-PjVZI|I%<~h-AZik`_uU*Dlli({j0)nO&?d9j3ku;?J9@M zx4VDip1x^O6~}6tOj#w9({MfL6)(K?%AV0vA1aPJwQ7BP$zA*RuicsYz*zZ@Pc47@ zgnQDGaQ{0Wtqn3Wew1r#k_kd=IIUT8wE8^ib?ZnXwd%YBLIUqBSfG@#IK7|0we#%$ zu#8{y4g*;?h+$BILX1Ku3oxXe#-j%-B$u>-t!$Oz$Bs+E;#Z|(4)xR^Mq3c?j&WcR zgHlQ%d&Q=kwAPWVaRP4ZsbWFGF2r7-rzWf#b%F({-I?yEh%W zdF*H85uu7@lQ;VHr(U z3}6H^R%J<&Rf;*Y)nX3Sm1J2b2y#&li36oBp9-0P{auq3xL^dHd`$W zp_sKe92On}(G*FR6ax!_RWNg4XtE^9It2n|0WO?k!}?EqWNBHRAqEK-&al`}WQ{T= zfoD4L%(F~44#kjbl2XE$xR&Fe_`p2&raNY)p0Gdganst3pQJX{DG%TLT=s}n!3LsqO&gh9t9XY-M#PsxFp z0kfhxMRa;BA%SZqhCqj44LJyAxNwRK>p$&BOUrUZUjSJm92^1wLls3yXDwz-Raw^C zv_nn2$>wm8)0MS;92;gbwffa|&jTeLUtPO=`KA=}W{Y5MBP+=l2Zjj)Rb}8ouCA&J zdNNo(HaRm*66F#)@YO5(5xCv+xmtWgtfrw4a-mX zt?6S+k}k0>FKg|#+vAtmP_5RS8D+*te>>!N5ALRusOKrdQq7po9#;u!DIgB8ea zVGOa6SW6}*Q9>Dv+Fgd7)_v_J-$goImI9raq$u=w1L|V7V|1yVcxK)5;{j{>*pfsf z>VPQ49EfN49yli0GYc!vRo6R`r(x2n8whu?H9Iwp5da}Ng9R}j%yvEj4IQ#Ey!2R^+JHD>PElpU{pQlD1# z;Ig6&E^ut?i*LOC!Dmpo;JzjE2c?)L@9FI?Jom~8w=WzN&*>!G zcj%S3{`uOUKVv7hhEGY!Wb2O|KeRUx-G9X70sG$k?c1Ai;k*#fA0IkZ-%#~=XAZ|c zboa;vYABRqmeuNN%z|QZ3!sA#8Ob(&@y{($a&qgMy(^ww!QINIJ$QGa?f8b@zy8LD zTcB|MeM=V&PSkxe;7i)|geezDO`V?urM0E0S#MSAA+pc-e4pimmrRU%HnA&<(D;XJu ztR0%mH$MN$8y{~ohE4nNqA^ETefY|Yo7u9Oh)3?5jrRQa3$Ly{be1LznD)TKxA&@A zvEFOVEGtd329B>@ao%JNa1q8A8&~@3wytcQV$ObG@sKE8)DZ@Xd7*t$1OOVExD23} zw~9wMKl|Eis}G)`iG!vsyM5`fY)(41?bX-+`q%AWq>s4k_URdWj;#2fzq1QmnK!4& zipImiKYw>Jjqf?=3({sDeTC*lDHtqSZKfb*7&>16s=-daVXYD(*{O9j;xAAEY z-BZXNS@YB@uYbG)hEBZwjwQphVxXyN+Y5hqbtSd+I;CW0jZ_p!Nq4+>zM{s%}jG(r#rY%+S z+;j^=8Dz+^P7%WkcC*I2O!I$R5YM#Uhxv4jW23eX>5_PUrZT zh~iK5HY|S);pn>$?D<4A|rW|gJ*0`rTVAu9U1e#cv~u!F{=W6lAst7J&hvgfJ$neEMWvyM$9zTZ9?& zY?v9bm#Bs^ykHefotfl0hM^n>hC44ljq^dx(`v*!}Cu6Y2}*re7~Cqn$8~l zTn(U>69=2lAxp_*o}=Dl2gtzH)!$|A?k5IXf+RdGu~%Adj*!!@FMr^T54Ru10~X!nIraC|D=W}|IkUV6KU)7$ zxN8Xe(aO~|@n!Qba(I60!3~>}7EDM4pNtR^<)D-5F2Mqr2-Y|v9y}jVWG0*=DrI?| zL3%Kt6BAZNC)Hen1sJdh(HiwiL9ZbT+J$X@{L@eWv~%FSKb@Qbt)gxU^Tb5N=VhCc zveVtQ8=qRUb{#61GqdT?%8kpxw)DO!47oPS^W7u)xwI&FVj^WIvBn=&g4o&Cp0YoqJ^DMRvb zYHmz$`=58Wrj46qtlxH6gT~@?*!x!bb~Pt!9vkNFS2#JUfMHtqpFDcN5M9{?*=kbn z4Ex!QtG_rwbJ~VV`A$dFNb>3@Yd+=r-8{&A{)_#R7a;&}Cs>-B#FN$9sNR?=-}bY1 zP4Vr_m!U&506+*~7-q|sEl)rF^uU1w7cN}*)KgCd0s#O3pagSfx%#jSdcv5(#zTis zOHGEW;-kGS_Keb!6v1D)d42WC&sXi-u;OFoWtkG4sZz?D*3sb!nv!+*_O-{9^61ufM+fV6~AjZ0>_Ivi2NW{)>0H zg|5sQnOd+}vghYTp4{;2tAG9Lj$@fa=gzx*$*64K=WCvQHOh>>^pi92`rg}t>jGUwic2yals7M?q`?bTP``sCS>Jf3eDfAx(&PzA7gpDK1`HnGch_Hj^Zqtw_~fN`E*YL4 z4O->4*Is+;t?ftBN8EY)Etz|ceDs^Q**WHn`xg%JXrhim4O%j9DonwG$>JVds8#P< zw`T3W^=FQLwpt#s=>8@12c-yN-HA=Fzx>+D9WZ3V(t95mZr;82!121qv&XjXY))Ui zY*8L3p4j~QYj1qGg^p}nrik9t+h2U{)s*Yf2iILxO?v4A%SL6f-Y+)3`pRpoIx%^u zvf=EJ-TsK2f?H;n2%^dAE*M}Cdr&6$duqHQL=D{(5#MKM|KeV9ZFE6G)6N~frtl^A zKe9Nfc}>gH`>J-G&xq}n;Vf8o_XLOJ_lX*qF&iG8o>+X(%v8r7@c@^*bYTKMzr6YQ z=Ub@g4O?nQQmO0cj^yrd(g~9#2{7GWNUoU(0+$;vSakPFEeKUvXv994|z5IWBz1bVS^u9+H zCbfL(e|lTh?z4KIOeATSZMXeiJ8e*vR)%6TYYI@>sU#%~#$MUqph)TD3&C)0Bq_qBSROCEl7QKGz~^4D7orRLzqZ|LTJK2< zW-wWkhb2()j3w4mx#>{#xh*?uGp)I!6YN%jGPFZG7DiWaY-Rz^^oxf+Wf>9vrT}6Q zrbJ;Q@`sII8Z}Tbahf8zQ}ZKOg#ZEr^G=f`TBzJ`u=?EAT{Wqu{>j@;G&Ff;Klro7 z3A}i+&mmE-{}qZIRkRCSQVnj?pd8FQ}cu-sH}vhMVeBPSgZCF8S9)LXvyXic-=Nz2djCuXI& zYc@T#X6-uE@79^#Ln}6{bcPS+R;*lI8(%uFU&D)^eo$L7Xk^m4ZHG2}mX`=3g5^ zbWXpwe1%B;1NZM*vz&Hja@8lRE1csOr5t{C$L3mb%#0%Y*#k$u(0b2$WC&_KcA|Wz z!!r`zT>bv15FfG=SAH5(=T9D*&m^WsTc}16LI`vXMa0A)Ac_)}6lrQ%`MdYmpJs+F zm^1zGffegl;HbN9J-y}NaU*Bu!$Ya>NM*$yK{)s6nstZ6#?6b~{g0FTzi1h4EML85 z73z2E%;qmvtp68^dT`$9msftej?bSq(0um5=c))z2e%xpag6Ef`bzmkP%96V0s>+n znuuMo);6?`sQKX6Ye;*cinOK=mi&5b9T$Yt@|>p{QmNau1v07v;GUVU`A1T zYD%mtisMC{iH}1-)Ng*M;adhmh^UeyWAKdPA1uhy39!ocXE&r-V~4tpfJDNREs+pt zX^M;tLv9RWb3g^&3Yw-zdTcH`&~wcWmoq9H(FVW938c1;WLez<+(tkm&SXm*YOXkr zdpq0%Tt-k1KrC-ZhOV#_Fa=#B6bMC#P(U23)zw^xbjFP-7#>IK;_v&fUb%yZWhZ3Y zbJy-#zx>5x34?B$+M7iL8Pp#XTbg~y2Ncvuw=FuFAr;4(Q>+PHd5($-qqa^QHU<>n zoGb!L5i$sgu)Ateu&K2L!0Br)M`5OZ(Kee@-9iRL7=DTHg4NCm$ZQL5D@L6yttWU; zT&@UgmdxQPqi6IT5z+kHP31eztG%<4AxTQG$;$?a)?5d@t?t2X{0NavQm_>;14!@O zE_Jr}PVk6v*}|I)oe*8uHO;``5WE03zuA-RR$m7b+g_3dYp;kQjap7d{-T)!U1Q_O zGh5E?IIZSpVg-N#rI_QS^R+a~8Zp>q1mysY<(;6_oV^ek9yg}{@Hko*ch8dsQ0>PNmTTdR@ zvwBmd(=JH)2GTv%ARdGqFHpMADpzkc`Me}Cw+Wyii*N-4wBh7)_XzVP<87e4xn z{Mjb9zI$d`*dFO~E_dmoB(CoLlgE8%QiL_eTDj$m>a$yS)@01NV_fEl`5ruIaJsUG zwMCjQY~Nk$_1yO0&lV@}(&;`st6Cja2gk(PDmRy(KDT9OZKkbvQ4GTXAf)@%*om{; z|GVMDnlnDfr?^*BR>?4Puqj{;H%@=Dr>@C;+k-z{n81st^7ea;=3TqJP2o%L`{Cl` z5R<#kXT-*5gbllML8|ivcSsp@|Kg$Be*X63kMHM3m1HwVR(%v0w4kvr#n4XC{DVRL%<7ZI;IQ1X%#h)U0RVxnX#jwMDim}a7M4+1G;jV;kM~pYnO*01?f?AL z$`@bQXBt(~n>n)TBdKcc7z`8;0!;;XX1~m#Myz|};B;vZYcgAS#u=T~ zf7CrQ)Be_`ngo1dc~fP@LGOwep5McDWio3}?>q0F7$yf=eWHdLq^a8X>n!(WfElVv zfhMo+*Bdm4J2_2wZQJR(O6KvtYW2RrX?sHdf~NBo$N<1BgA4#b7#oBd1Q5p&LUjWW z$AP9gax$cMw^mnIGEekX&+PXfH)YMdrS|0&HPzM36M1TV`GqQ`aLADTXB&e=XL*cu zUGbZl7|?^NH8U<@XMaeZb?2iwZ0(i}Cso{ByRKT3#y;{y!awf#e>=CWJ2hijI*5QV z!*UGA00M?kkb^qT&K6$XcDkV}&u5!ZXAW7m{aZc)M0FjaqU^z1TBiU8wHB#T-n25y zs+H>wt{?OC*eE*$4?q9+J%L`6?@4v}Ym{0AFvztmtGeRlozJ{gj{D4dARik24TeU| zCZ14{MN~&Nhuwj>xJ>Sqt!L{hnaBGor}qWVsPSW5ME4>DUE5XacrIXxisEaL5ax8d zSO>%y^{ZYyN%ffV_j2Y|i^QW)$$As#<)atn^?tJQ%$d{1K$p_mY{;tF#8XNQLR?no z1yrl^36uFZkJr>yGmqz~4I+HUegFU<07*naRONNYA?@ag3ETTZ%B(vd&0%Xk+jN#l zH4))#E${;PDtFb_@*@{LHa*L?Tl(!zugw%+N$b04Cz=wSMN}hJN0=vqG5LrBEJBnR zSa8QA<>c@-fmfwKY>ZpLIw7nDH&*UysN+X2d~AA_Z@2urt@Wm~V)*;U!_~)|p2&}D z-T&<0&-$y6aWS!B5g5xDFvE}_xbpM4+TH(J1-DEY&_4psuif3)K}33LcdM#dvBTkT zvJl^^SFpb)ch;Rh?#_}nmgv^G+{nH4_M^K_Ffjs0sHPDDfIyaltnT+3BGFoFp|{07#BBsb5}qtXg1@CH zIx39X5k2P(iwKVjN3@|;#I|_l$oubadhaOdlag!8XabKlWJ8e_!(a$znc%rvnrlfK z=;6KR&J)+j!bGch^o`e7udRmMx%+tYSXc>V$2%747tB@GWL*rc->g3r(yjM@#k&Rt98*i34 z3tKR`sPXk@pPas_ zA>fG|ck{i&bNe?A-TAve-O*=j-$JGQT=GCMerTb0?UO$lGCX(opz!nc=KfKVCi$C) zb>7@5O@Dai(QzO7-My?S;}+do63guR@J}zj_&&BL^d2{Hc#IU(FoQssfAVV51!$`)sXHRq<}oxlIvALsp}#cAlN^MBf>mwI+bFE*JW=Csxp#Do8qg5NsC&D5 z@@;`TG}JjF;$7nwPaB*dq%Lo>Uiib0$1M-Ids)-U=1!hAVchUz|Mfy>m>sD} z>6c6>K>caA8w(k!pnm^0uYUz@=zJMEa|!_W-+%wBufF=uJMT=LI(6ByWlpEF{j_DL zHz6m$xxJN=Ieqq%%ZBk3aP#Vy-amhE?;7`jBtzqP%&3~`7nmqbw>oTg%=OA(U)g%5 zu9Ep#U-k69z-e22{{rt>FD8I^ju=EY05J@3W~=YS_J&%1Pw(+*nibpa zb`Qgdtf8u5{W6rdw;n!qyy@}$xYo~q|M!zl`OE>y%MZWpv0Fk_npGo#VN}4F;gHsX zlE&q7&;8GPpDkYUz|v&S=a(6zX!gtu;V;_`*Bo#9S)aJp&!7I^N@QlNU&yCxPT(i{ zb!HOj04xUpiljqEVv5*5CTG@NKkm)cZrONJ#MVKPfBfv=nG;Qq_laxW|J>VWTdOPh zxLyvIi{X7l?mj9hfSKWrwqCFq{zW!t!TvIUanO8oJXaY-6Sg~v&ij^isTGkSHLBB`i{AcXW};>(=!JYj~bC0Yl)wF zTevH3>mj{=>5u{0E))m=-=*~_;}mlw5d)U&X(gset_#oO`U+h*3>Bu51{h-?ie}Fk zRXRrwJG`ehdSKq5QAsAwQaa)0RI7bMd3B@VNf}mDJTloP;?&V)x2e0^ij7P#JNRig z&5g8g*i=C>`jw6;N^|3&M6hCb@=IneR%^gTZ7DaEOn0!Jo#ilKTv_1=57QDf7>uBM zySW2LCu7Aw{FXV8Skc6QAv@v+6&DXrb!*L{?#vurI!9^eusB2T>DIA@#da_@9>qCD zrG+EhI1n^ghAIIzcS@g)YwHgjaV(v_L}1qKXY-OKMP$agom^yA$!#V!e)BOsqkr+} zp=me@yCrj^CZV{}!ILbqZEuw+DSkvmMqEmQD=BQwqf%^V?P$7Y3Qx707B4%GVg#%c z2$_mRmHcwB>vo0Ib-g8EFU7WEov}e?$wFNa93tRR${IRExG4Bia@rw@x`|Z?L%es zh!mR{PnxjM#rN8c^O7b-WW;z<&A~n8ElxRdc&(#!^2fo#TbXqUlJ1f(hnvpcVyc+y;g~x$VlFLA2=hj2&x#-!d@_!5)nttmL zS8W}W9iQ3Hv;22Ytngc(xocKQ&^QL|CH>3t&KS&7nhdW|M~M@tmBy}il7FYErX{{YB>Jf6N`$1{wOO= zoBZIUqLRk7e|YP@!k3dhbc?s|*juxuprSGi2a=)(d@yi~tM!w3+Uo8X2|+Ky7z|Mh zA6zo2@tv2SSX317M_D0d!rimSWgDB{|1`a2R9j8kHjEc26e;fR?oN?Fp}1Rc*W&I{ z+}))FcP&nFcMVe9-TljTf6u#rWUZ|1y~pRw%pB4O@c?zfr=gb7z{bdyFjutqxk$;2 z2A;bQfoDheH(XS;XF^tw(?|@|ux_Q10x2`ox^KZ~1Y3}KUv4i8dzD%Zv6$d* zZYt=l`{^!6(NBLbI?H9OQ=k8_=jF5p_J0H05y@drgwd06s3EHqC0Tz<(CM6L_H2>2dfl0ei+ww!NUDOK znZ;S!5#3Hg6gQ;%H+qh{;unNN4y8UYd0W?TLt`{erEHBHw)f>H<*agkQ6$Ys{JLo2 zTqw&^1gFXAsfeBHTVgDF5}WC%a1o7ij>@#Ue4_fx^wmRj7ZPNRIjz=o*3g25-Z%I8 zU^k_iwA@BLqjr8abk=lWOTSJ^#v=`ot_2(`&5pYpSA=Jc7{qBSr#Ffy_jL!J0|Oj3@sAR zcIGd~(8OdO(e}_1k2XyH4>0O8m$&Rz@&>i7u9C1i=0JW3pIp#7|GaB?C~a)EuRjuk z1O_#PSriS0SKd^M)PS%o^0Yl&f(@Tkvc_x8us{zlzo5=fCjf@87%QZk8-MoQu^2XoUV#>GrDyZQ`TmLjA!Mkzy$j+@wUh5eq*c6ung7#LUiGsLZZZ#@MQU9F~M;?l=)@EoC;7 zy^3aon4>Cl8J;dD_^w0ycJLd7Cs5&Wn`PU^rc&VvSIO}_M9TBh2=6wX3tK6ibC0Lw z#HYmDQ2aQr{A$dx{bF%at{OhVnrrbFEF$P9LQ`b9dzi425a^7OYrCRqCVS33pOch& zT3Zno$c7;Qd&UTAL3S};#Aj=o4RA{yjX_raJ+(1nz`5@-bz*30$P@=VmLLJggep3d zBn$|S)f?TFH^GL4kGIr$nSq8J+i_5l*M7h@JEf{E48E?=)THf-&#RA|l#!96V`Nn# z+^egc?eOKP01F0LFhRokqge2bs*Kjf+SQIU>gPFABcCIpPq^(zrMcQlLe`c9Uqjc7 z!PbTywvp&w@(j5~7Cd&MK?t#=x_t8mboXURI399!?MnKyaPA7_Lo~;k$ki1IaS{tu zyM>9d_!NZauiF_>DG1JTQZ?^13Pk}Z_(F*xEam3M9xJ;z6gQC$JB~H-Ljvjj_9H|9 zYU1}`5>bdf+i^~x6NRMf$|mM-EOc|c*a8)$bCvu~BtbP#UB&896^T~DjFXFNb!rYd5oeE zP8Z-mBp%a?GWZReWe=TvI>j(*dB%;_`1ScM^tV$kA6FY{KyqxhpHOdrY5U**h5?EWlN!vuvs)h)i z5CmMP3eho?5@^k@p>4_R4B3Kez(p!owdgKHe|XD>PzAgazdV?dE}y4?A3B4s9kF-1-78brrLJ%s8>lS-`~K4MEe6 z_9}@~*f&9B=@ROtAUXs9R5ISOX-eG3)6>vLZC?Y#mvlcCS=9%@oW(7Y8;SS;i&|TQ zet#n9CxFIPn}^P29yzCb9`#%H2$M^;JC<3WX_F40lBBXqdkE6+{%<(A8!xvtElvF~ zzT3GzgG$tZRO}#hWBGmbmH6HT0qwV0PbW>Ws|xn`yB>oJk)VuONrd1w(-<3^KD1&q*h)J?fy% z)_rY(>fC<|slVa)a{I!|kC%|N@A43319v3o<}rl4tG3M* z8PNjY5bMBa5I}7qU7KEY)WJI|GZ9+G0PBGHO5-!hA&rRDnSl6J#c8xD^y1F zl44lG5Do(`i(%v{!c*m;Z|$V)2M32Wf5b^BUb@cGY1!F=(I%8cj-!NYgF_CNPcH^I zHKuLG_gx<}JG{Y*AoZQY3w3XN&O#04u+6GkpZMODX(l^-j>@dMD)(GNPHNe3g`q?} z&Cu)~2NNRqFvgU^Vv}N^P7!)RBYvKg`*i)$;GXNKfu-Fis!nY0zRAYV$!QBnG+XbxNs+|J7%`f|ntYGIn=(uH{Mj{U)`$<%C@?2b95j@6A zT4AN5qq7=MF44v7cyMAnB{B%MDr9L_=wOR(A;v5?A48G(oTDHSvsvMw5DRDJT{I^t@+(0K%J6m~XC z<#0Q9vpA&ft;doPpz*WQ{5;iD7$=z@s}>X#C@U*>_0Il^`vGBp0MRa6f4{ol zCqWW-{x>dR@B4V^==g+`>wnZYH1&0pgOpUnk3-KYDO?1SzpcI@PcizpkuHP8`=o@a zi8#B?I5bj=JtBk)@4MTromc+3l#U;!{+T<)?hmD8h?`m0iX#7 zZPh242+2Ad`8?^pq6LSL@r{lg(8eSncrzN85H*J^i}X#pG_@g3rVLPd6Lo%5r0S5> zg9sgZ5x2Gnw4S{2IS3?5z83Po-j$J`LqNg%=_xfa#-)!be(LrDlF!np)N+jNv_vEy ztKT2b;B-A!iyNAIPnC%Pl=Ak>?_oT2ekEJy; zxi!hC34>c+`d{7yT9EL*0=enLEm)JF82Kat&G@4AerSwX#0?Z)+s=66#3 zNT&?%z>VBZ42Lv4Yiog2kFa;BJQ3;cOH#`h3gmmrKEkURE_r}*8@^Bz-NnH*)|uDc z-9wkK77;1$>puL093HW>I@(ij4a>(wLWGJT;1@6-{zP-PSs)1{9|Niq=}P5nL1U8lJ@%y50VF7D2v`)Z;z{KyDr z>edqpFemP*38}*f5QtBNds1nmW>;2(5M`f|3(T`%;VwxwhT!zqisPu~d(C26CfUXh zXNnHSGj@a{e@hcw3hxms-VHU>D;+qcYz6KqKVSPFUGD(x6Q}LN{E?8 zQK|4Dd87XoZ8Aj&h7vNOkz@p*^9qLjx~ogOzc5)P5}uZ2rEy!WA~`t&DbE?0J>5b~OV#AF#8?tJ!#eFFT>gR(#!)Cc6R-TMCuYR*oQO6_9{)pdr*i0@%Iu|V3O00 zGH66 zCe_F|*NiQGlJ@kzL6jd99pbjqSsg5G^0$>NiVzyw$pDJi z>g;s~j`uTa#$q|O^qSw%(qwFlj>|ZE>CFuODzep>N}Clx|9_Phg&i!8rdynxPGf|x z2T4}RS#Ei_u9#4vd?6v&;Y*m*=SfG?%?yLXj+zwF-itIl1;ub)kloT*ak9z<_y97e zaLHFxR5%OdOgCRfu<;aJJb)NY5p<{X zr#EZifJ)P5Cl77trtQx){a<_Pp7VRg@gdNUa7}l8K-IWlNpoj&6cl1{02Cq^SZn>! zdZM)p5(O8pBr8o?Rp>R8{FWoW5uJLqCPT|l=|5#qgR@1Ka(e#EgAfiHu9pQQ9BARN z^#xdIBRVdta3>jO4Yv$!^UC#P8>Yb#B9t?R56K$o7ZYLP8txG2in*-7e+e%bjkI?2 z`PRcDh6(ot8fpd`pp0sWj*eVO_cOZ(#u!4)8ejR&RGOVgUoB5FF+%OkI9mLhw`Ifdm(3qXyN z<16!an}C6vH@1D3ANtVFZw^E2q)y~D{Xj>OF#I0SSIZ73z-3ZaMfOb}!!4$D>uNyr zE-g!FnB;UjzX(tX?2*(|OQKLnrh+1z64VRSP}cWk{BqNRG-1zc?LU4eGVU=k<*mQJ zGXAZ^VtKYqR`EOLkaRy)zm*EqR8;g=n@D?T%4PUef=FbZG^$#tZ?4!LyH>oS1qB1U zz%L_%O?e;1Sv@}s=UA56;KC3D)PnjDVq}(^JDrB=!UUfkQ3;Hy4>L-}IzMIwv4LKRxoQ=CK_I!qZ`VyRJmhrzL1=AvFaf*Ub*MWbe-l|i8hlcvOj)kU)z^j+c}R#LWo|3J}Cj(WL&A_X8X*mMRv&k&TK0bUUH>?spax z8tPmbL%k9Be1HT^#lAl|`uniI@d3{c>8F6T$4`D#0AnGM=s&3Ws_m9DEk&rVxgA1QNxj$wz+k#S3QDNeAhFMg3%U7c>7gd5P zFzl2y?HA{vCdLIa@V#O`n(bH_`05)d+`(S1YdPI|eEuFxHC#Cxcl$3F%$u=;n^*sG zTH@py)XyIu7kD?`3FQCmRk!2F$rlT5LZZ5RTER%m+QXP&;q5-iOI_lNih9RZMv1;& z)`959Qrx@K?+Ff+zQ35(li=rLFlpsFYLNwNm&H}9spz_iVOFq9mDee8_brcT$?q;M z+IDK&;m4J}FHBEECE(?V&m^bG$2snrBp7pKe%C=W3CiF3)sy{S^9dlJ#eiD(qgmkr zKDYk6ZlA~M(nR?z=ZfRVFLtFUc}^#dkEbn58q^cdn=fB?X;#pYo9%^XwUm_gJ9fq< z7DGJISXzB0%d2P{4hS#0TCT&>V|RwuxyQTa__f&{BgEE&)mD{>`KYf|9Za+%RBex@4d5r z5t}l5xV0pFTQQ&v&)CAXn#2jBkl)X-d};>9)HvjwiJfM~1Q~ z*?t9o0zl=5K}@d9p$B}>IJI1s4%PHWPKpo%Tjt zDK);O`0m{&XSlE4f85=*bPK8&4@CcYDPxsA&cFcg3xVrV4g#Vf5@VwQoOb4zbXVJotZD zfDpI~+PT;YJQIL2Uu*;T4C-GF-}#lmxTBu7HvW(!WbptLox1f}_a+%#tkUI=CFHfw zerS(<5a?rrNP_rmh2kQ7A^LB7;B38JbLkFp#?Bat5$*}z!E-8%_x*CwktiE$ z%VIPpGCdJXt$wHT;QjT)A{U%t^Gq9iN_Zwx|CG}GzM16za_~YJ_s0YQwJC2EXt}J_ zf9Z7<;aht88&iaZq49oKP%w_z(orEi)pSA}b1Loh>E9pGx95xlw|yPx2#K%8wSxW+ zV2DuEhby+z+&Pdjwd?kDN&0x?sK!9YzclP|i}mBZLi*$4WQRL2qKw@r5HIq|E)YMYw4w;;rZ>{Z1|vh2P_v+C@b`%eCzytRLUm`|nVjP6o2Ng;jL4HJ7GmNBiaR zmmA+m{e6zhx?R=h!9&dKZI(@@7_ zHTW1`&+4pTXM^1Cg|6DzRS7J2tswks9xMA-8m+07s{Cx@e*O8l)lp*B_%CH&&i%iB zqhMHuC@kBXw)5vB`(Qv zA`+>324?Axf}-7E@6Jpa$3^uDS+gS0aHh@W>4_9gNUanj#>;OM(nAB~O2aiRi1l~b zbAH`dOd{EHUIXItORbLZCXG;|2x%gx33UmR9HJ(~ULJqyhv2llw}G!8TK5aM7ai}{ zxdb%kJLB2tBZHF^JUczOco(Znc`?|;JbQ>aQWR?=@SQhE5X*Io(NgT!^$gPVs0~% zq$rd!pZ`@_0_U$^36<6q{r6d%m1y?Msp>`P(D;3Qr~~2$A30$=t=HF1P~lNgj4Uks zp6@;gMb+n44J&Hu+$eqC=NtS)89S|i|Nb3&_8Vc7Gcnm|b3NgU9UC5ILkZsDyD;Q8 z>Vpm?@X1`3os}U}dYq2kLrqG`(Q(i1 z@DM(=)nssREv+c4MGK<~#hc}ug-ug_bcIDdc3$NpPhnfx)U+B6hk#Z-4txlDI>MLO zXz_1c>!Kp_``T}DOT0PzVH=rSnyXEXJMF-&uT}0#HNXn$i4KOwyNb?|YrQJh^7hO~ zRrBJjD%T-2qCmqmpdm&R$}WlwVaAr6G;yTtXC~#!nb~VM zV&DqZW)BxFd%XIP5F=%q=;7b?Zoc?cxWM*|_mIWg)<((4&t)E0FVX$wNQcZD-1(*c zC0_WA>qFItL8RQElwUekb0-Q(3HM@ieSdk>&jnYN1Wl(YKa-eeaJscHRs)@k_>#mdSB!1huA%m{h z&g}|k6-2)eh^mCsboIwU>fE^?iP!acNSLc)$25(4r^!~4;ySS+8o#c)O?t0i!3ZV7$M#l5 za6ZV=|A)4#z0AseWj)+MespVoIMqj4+*)q^?!U25tq{^a;n+e=Ip2cuXQwNaApr?v z=`Xq6hnajfQ-o8t*1{Q06HZHlxbM$p?K?*p{!^*42-+LITi;L?vB3_v>+S|$!RR0T zQ7;PSn$+-rs3Pf=pa#t!KJSm!*ApJ=MR}Qi%Eu#*WBH^bCse-NQPVZ~=a5&CC04aw z;lr%GOp|z{5mI(EM10O?PU$pORT`Eqmbd4D7LGDrx$vf46$>Awr`*@XdSdOph-$y2 z!kjO4ohr(h@%`xxv9g4{oPFEVJu79s%y(7cfN;zh)bFGscRbzig+2J?n3>rmJ=Sn- zSz;@(mGxSV9)BnIBUgL=bia3d8{3^VtJ%iflJZyA_@y71&2f9!o0|@7BnGRiD`6(% zW%!LLA!zb#!;U@drzM{)Yp3$LU)|2jxQF&mYqPsN{V?cw-;*)AZgkmS9{pdBTj(!G ztrjHpzTSXy)jZm4<~r(!Ll6B9BOkdhR(eKw?LWcY9XBSRuainGqXGa7EE)JSBmQl5 z+;KZyzS@pr{es8#Z|fK7{lmjA7WNhTVid)b>wDAAEA7QBo3v70-kq|z{^I^8Fg$`P zR1eA+pGsJ?(Qqvuo`B%s&B&)FSLi4bPaj909f7A%SkLyc7pYsBFFDVbXTE7fK2Kyz z7aQI?p<%3_6?qCOFmu=g&AgFiN1H8Ds3$uioVDMfTjRry^s8cv_%!P0<1AIEU3W{6 z{$!yV`O7rN+6{??@GGHODo{(O*;f-#Sg{#`masy!9`iswP-pWsjj*)cnoiO@Z9>Q@8-FADMBC1j>e*Z z#DaxZK}pi-zE&-|@$gcD*7Oj;{Nz5rL^aQguzr1*^xq(UP5C9XXw?Nb_=wtCKEd8` zkev|*4VA8v6BPI+@ill*)a_w8$G_v^sJfWBv?4;40O&6Ayeo`!8$Z?{ZNiTZh^U2d ztFWn{qnPj*n$Rthvg^e5ygv(Kb6-{e6sp9vvq*c0371U`6LlGi&JHqow{m{^_sg%T zC(a0_FTQ{v6#z|Mej@tf`;1tMq}Qk}j<_TTFIu5Z&2(^kFdd8h zbUyf8%l2`pgyPEG0gQ~Q?=9OI%29c2B@YH_+_i=({x*-!KWULwnN4RaD5zXR&gEfA zNVoTeuJeiX=0!w>m><2c2@ic?N`tzQKgZAKrJYKJZAYS5p^!F*Xce{--5g? zwc_ySM$`SM?BtxzeVHKMZq$?Kwoch)r~^D8RoEPZrz#>ppyEd7;oShJf3& zA#NE-%cZ}`rx=nRZ?)0-Qkw-nHZc@fcN1UBQbwU5YT8&{suhz#$TZ7=`n~qyN_Gz! zpTyX6w=ESR<`N_(5k%Z;gvoX0Z*z2C1(0=ev6XWvBiL1BrevCr0EQ=XwFbfbg~ced zQ^&yVK@Wyvzm1O{o}PxXCO~Z)g-0|u!jA%q%j5=QSaep!?7YSvRND`FMy0^ zc!NCW4zBQw`zW3L324^qLj;-Y!2kMgQ9ksH8Tl5L4Ieo?$O;`fghG-|d|t^6IoGS9 zu9t%^nhrh^!=fn_n+V)Rxw%Qta1Np(T67;SMUS?9zm7l?94Bj2`}}!!bZi3p{!zK0 zxw^Tr8vR-4KVnBy!d>1*UPWo?>#gcfDGueO{*zyuZ%#L$FLs|~!$4lg#}gW`x1*!z z-j@FiFFNfT)Z2xP)xwXIh0zBDC1p{dKxasaf3f{3Ud@PEJIO!eFzY1e<$bK$)SQ7^ z93VnKM@L6!qOU&<4fCr{)|-ncvVP-MJ4CvG)|@o+$dY2*Nv!J+&gpxMgrrNY&>=`_ zbsg;*t*41z^wxogK+4h%cUl7s${%#%!CZ5qbudm_p zcau|yiHM0{S%3jt2-C=e29(A*s)|!w;)VS@(yO{EiGpYpBO1J&M1B0%pT)V0<9L7Q z;=&?QR;WA1W|MrB75Kky()H<8nm^D+rrPG;Ybp5>f2q<=GTCLVlZY@h7S$vm_9;bz*Wy$p5h45A zPh+&HzMXq=fSCqMK_($GrthB!+?T4N5bVJ1&nLW}cck^5&!C#uvHkWXs3D7WNi*2E zA7wh79knhyjolVu{JqY%O~qMAHVp^!dDW8^?+&$o&)-h0ylb(|;S4?D@)>Y` z_xD>*f;XT`$m4jp@vQv*dxUKU9@u(82cjeHi~JPbMr__k+ydCBJoMYpaKn9z54x( zv?!J7S#>g1h|$rdT%rUJv(R>KboMTL1pTL(viMJ(ehvysuDJ0nJ%e8$|CG*vYFaBCDdJ_P5Yj#GzT;8Y$vLf+WU=N>KleR z(*zYL6fbswNg4GZpX%vmUA-7Eo&-zu&!TZO9U4lK{*uXHO+~N7TdO@!#jCVf>rmA1 zda|RFpiYZ~6I47uvm7B)m3N;=Or8`^Hqwo$spE62z9wB#Zmqznct2+or&#{!4SxM&ZJLzN&sxz|*a?^tX=$G^I#K;*v)3#uzs}6Im7K{` zi~!jM{Tqa@%Q+JO2oDPN&@{BPjoy#9%{;{=INVMEAX09%DR5e@Je)d%ZJzq+XG?Tr zQ+9cU7OZ~Ad4w3S1!JS68+~Vl`$Ev8d125+V4un9V#${5-%`x8K~-230ee>Rb#IM8 zE1yfS`stk3V4XSG{TpQQcut*%bb+qg673c}rXtuuhy$QGktk!1_tS;JxmL$x0RYLT zLDwwaobQF^Gvw@8!?zs5!SY@E^?v6_{xkY5T}LHlN{G9)A|5i}_@Sn;z9trR`fk_w zC52VE+cVaCI7$vzfa8+)cwg6he*jE+WF=kVd0@ay%S8FhYYwe>RiXriU7e~!=l&{w z5=b4l>+ZIYxtwVHL}p2t&sJ{kxFuJY{?mt_mr~!osObG*8jppk)8OgMR=qKMsx<=I zIG*0+B5wDn4=qc)ZH){8k2{f4{x>wAj-oM_uAqYw1fA1gD-cAv-tKGgUNi`c|qVp>IwW%K$H)Fa+j>Y${1 z4ymfbJaqZ#-OXVm66LUi>?gb-X)i+3o@%`hyn+o|LK%AoXFZ^;VhQL8tnzs%+lEgw zjj5@UdHkCfgTL_9wXiEY7u{)g8KCq=ht7{=@>csCFiL{YkHLWiNQVc|fF4fW}A>vyx7 zv{QnT&O2Vy@i#oyEha};;KASEID?pcn(MowyhnT62O#6NsC(J^s#sH{%T}QH6ohDk2_^oF-HOAk|_%&JVChU(a z!xMQyB^4R3njRAXU0DmH$CiO$Mh+|asx$KP`-orDWbJZO0y)$v>J2yc{mkJq7v%##cEkL$MQC4FC?MFwiBSsX#LjRjskZeN%4yiHphSMAe(eB#j7 zKVX9E(zBu3{9=jIHQ_#TZe+y8#c^2l_{_)C=jZ41^T~iq?1LL`X(8wi1X=w#%gWXo z)))6!3{UuAfA!RrIDdoo{wm{kz}C zJM3cYMJ+%r?ffjCJl-S|uwlNb7yl_9bdcKr_bSTzq`vQc6}XssJRkA7MBbIjdw*r3 z@QgrvT9@$b=3u;Z!gudzKvzw%R6Z`}?eY{!HJG=n{&r^kVF$Kqq6yY~{D-Pg<|G>v zUHQx^N*^a|)}mAbD8ImHdNqIz;Rg?_l*M@I%V|ytu7iqjK@f)U*}Es_Ty&K|=k;j^ zaVK)wGWVY%#q3VmFA?Hr7fPLGCTq^-OD6A~hxX3sm_DQ1U${h?{gA&vcJ zO@4_4b--a&V?|A#u$}27XU)2(hw9-^Zw25eDlBIW$z0>vR5zih+4P#xbDcJ&4IWKDAjr&6?E_!;HOODw21qXWY+g!z5*?teq5d| z^uo|hCH8#&-G~`UK9h~w*-7>{w&0T#?Rx7QUUasw&2{ax21gPpSy%;wT$_0tO$f${ zonjl$j)p(ty2oERLVn-X-=-y|z?8b4h-TXn=BPtKGwc0mNsHWtRka9D&FfP_hn+J* zHzFSCaRTmoe{}0Rws5aUkqk-;XM5dydR_7H>G<2_e#9T_7!cz%EhDiRN+Q@DIo-cK z6&4l>F8H$7F?04Py@buA5=GUS+-4ny4|?u9#N^O~%_JPs<3zLWTF*U7?8_weXYuUD zQXdI#szH&$JYsn11gorNlT0GmoVJUVGJWE@BTV3L+qyjldzydj(sW2JluVFbC zB7J?3G0jv)K|Qp}?V2uWRMbkw+;%0MK=gXyA%)T}`GIIW!H?8hB--QXLRI!Ya|3SI zAhPl~+Nq@5e5OP`GCMi*0RQlAVe|8Su1sPbfFl&~u%<&C+Q}v6Z|ztkTfvBL~fSg#A&! zPPj(vh4S%nIqlT;YGPwNP(gth)c1c#YO6@$lK2tmo2_dIhoYoCZV$}SDsB~#!?Tygp+1lGxiTc&|N@Bpy0YSzvj2!a((wT_%_SO&` z+oS{$I$^G1Q92$WeWAwpF^)rClS%chiC;YSYmGbqjR^1O`ToVumAy7R?n=_#Q&!E; zSkjr`m&Dh*6W-GiTukG~{}tEhZ=kw?Mg(lwkR(Sc4x5Lin(yQwD!4hDDe73z^}2E7 z?XS*{IJ@;m{+DEC^03cGd5MPyv&=#4ps-(3a(Ec7rbh;g8nrtYp0<2kV*!rf>+c0! zhGT=wS4RY{q~ri(8eOy9EQ(E%5^)at3+V>Bz&U)18HDLbelh+xCY zOUq?DuI}jomGa4wUsPo=xLxJdx#q-G$TnVH^W1r)Cp@0hg6oPIGs~^v$j6Zj=UGz9 zwZr#pgXo-4@G=WWk@u*SvG@>B)HP84@gpFLRZa|&N1}76Yu`6E4X9xyL3Ab$lk>=G zzX>%Q-|uIYTdN#W1P>J{i%o!=ZpUie(!5T;fN1Pi&*scGQ~!>%Sy>_sbjV|YbuBD2 zWiX@+85po7zbH#6Cy>=on+a3FjI{$M%==J=mPps`N0tn{Ki4Q^xeYbqJDVsaiLo2o z78>qoOJax6MPW|-_1WpQ`{6zE1qA~3Ag;ZeYvQkW-Un*Zf}xb7RM=c3 zG}|o))v&dxeZcY;GZd;zSlSitQEA;>0}2B0Z3% zf`&G-aC#z@2=j+p4it1vY5)dRAhc=@xdXCX3g>jgRPn6VKIEv1D##ex7Q~ z$9`FqP`=Y#X--3r!*G3<6?&0+=Jc>c3}J~1a%W1`jj zHeQRb1DtI~dBg_)9uP2VZ`X^WWPBGr zKc~BJrtQ^CVA6jXr0-`85zGA)WlhFpw!C|U?;D&@Edd^pTG#!Rz)K`8-T31IEkpp< zM8)CT*Zh8p@UP?Z;T}H!4+}85RvoH%M))^VpwS_)2)Me39is)=exyW6Hh$Mzp@&ys z`+>};_9Hw3O`=?ZT|A)Eg2T1F0WV>4|G^T&9KA_^;SOP?0165Z<3xzE@xdO!qKP^L zYPAt=L%S#TaYMKH&{5YabW$-j<^r#R-HV+g-VN|rS?7G1`q&~1 z$1}MZ`S>2!wxaf@@Wb zU3ooMiPB6;x0O533x?PI4a@&z?b<(RIVcN^!x$lCl$VUmt=jYAsY?Hy_EVm5bTmgrR?HCgyPd}?P}htorvt8`bpXd#uw8NNUC< zkgD#`bFP024HW=&2bzjKkekPO{fs_nXl;R2-4s*3aTq4Y3xtLGivm`j<8OcsVBwsp zL&8%Aa^^c{R{bIgbR#*KUT0Z-Z?GxOu{&})nPQdIV>O>{XZ9qN2B%C{MV_yoCkIaJ zpS$AzktYUFfyiW094?nXN7%Y&P;Pnt@wqbLmOojl*{n6{`#*$zoc>0^SJxHS?NFwHtJLT@Rl%rm26LFbTSmsUupTAhfCXOQ*C9dsjaQ?%t_|mr(GB* zK1wmPNuA7smFM4^KR>4bAm%eGWh!6L?#KRA*UyuXNXPSNWggV&8BBTDh z{HcPA(MUxJI^Naw^+^Z`y;m4ID=(l#%at#KzNJ4Y+ofK1z5{XSikZeB)Q&M9*dxCX2HpGoOj2m)*XalqWF*l z!SG)|hzy9NB0hR99=Q*G!yPEDz}>6*BKP5`DNS{CWh?{5t-fDqq#`UB8>#`ava+*t zbF8jFT+a?k#G^Jy?idkapgcbA{_iGYh#LlbzxDzKzIR=G&-HiL_eNzcK)LYK)!BHQG0fqYNwiM zZ^}YQ1dwMP@$-{2L7uUAwYZ^C-6D&G&IPTlkK3uI|I49Ic1&w)>+taKk2c(!?YK0i zF%1ok(faJ%T)#4iF5#u)-i|Z$e+5Za5f7(}s}c;5FbE-LB)ku2I#LIQU1Q3Z`RZTE%RuB5B$eHQf&Qgd4# zqKY0GGVutgUMG2@KzHa(6YZL(C{DgEWi z+|2i}HQd91(ZC+9O13)m2;>*_z! z1*-KyH*+|o&FMQxSy?Tx_%}Ig{;zJ|9tVH?uco$kUj9_x)P=;~XzTjpr^5eV4deFj z5wg-37t-ZUAsIjrWaY;%8$182ylNtrIS-ltI77NE_P_>n)WJtv1Ix~#z=>R8Z%BNF zgh~@h<}r%1UrL-PHSfqHbZVM{0JbQM(DTd^J9B@t)mML~ z2iEy7tifYG4sZSKYGW@;+I!tl{H%INxSTK4h4KuLX!ehBT#Z#A&7ou0Q-qNbatv|U z47Rs{kQ1B-Q_#0BF1lTgW+9+dt2xl0b^8!Io?oiF`1w%b1m)7|s6ms$>MsT@<(%+j zJ;+~5L>1Km%G^#fc99Tfg5a-+f>Ih69RKDMTpa~8zn{*f@EyCxWmeE^L>z0#DcK5y zOMg_;An#$fVc!BZ^=(;8TDVAZ^t5w+&?eUxsv4G7 z4-)`dfj6MSL%6G;b?Q|%C~oYyN`l>M?VE?TDfo3?2de-!*434BH`nt>zx`695 z?GG7;Z%E6wFoTk+sFBkvZ`-eXw(czD-a-guK7ka#Qr5hg{LdQ0PcFZcVHj8VYvqzS z@Jg$#einPJ0k$||(l&;;!i)yvNd3o{Thc)z^}-eR`b_ln%IerqbY^gR{+BmXqS~qI zMj1{X-7+`lI&2ghM#DG)U}1<@{r|`pk&as$cYXYQmGsby3f5@d_s>6`{8TSG-Hj5f zlxfEYzQ$nwJ1w;Zw^3+E<_XoI-QfkyKt%o^eIQeM8qM|#b+U6o{d!GzyW_?FYyU_e z$9cQIP}<#UJ88|b<^`tqXSu@P3c4;As3z7TK1OG`cA+w=Ql@L2hAwb-WZi}zAXQRd z@72ify79-?mm1p5E!i8hAtmc8QwycqxXLTIpJeH54l3L`vecPr+uBrR1AJ;*FNl^i9Fjn7_HS>eLmM_6Ik4swbhSZs=PQ#X1zRDg&@i6zCl-%w1cs; z`xO0aV_cj676t9@;*VYGeTURNCei2&th8|IW={cI)-&Aa$4(MBywQ1fYj1Oil+? z6Sj@4Zr!#RRKH(?=Z1Tg$G9dYO@YPapfW-b1lKK#cqIbw?XN0MM-7=OK?N;l#rh-d zdV%+_rfr})xzAU-w~Gse8V-!qFrPy(KRd_nC;g~*;-P=HQpV(FGIpH;AlAvv)mJLu zBa(xxGC=@3cKNZ!j_bS9x+>~0F|CH@%k$tBX&d^u@I9b1fGNmB#oJ}7_mtALhtqDA zO+Lv@S!ZbT8$FxQJn_0S+spT=0Tm$c#5ps!+#J`8C+{yMYuBo}D%a$cpHCYT-f`;7 z2peolrF_lqbGc3;T;+=5UOLuJvd`fz-Q}sE?Zl2G!Fy7c7Ss--{HpE4TvYE0D(Svl z_YWt7{ROnwn+B5wf#!(D%A#!j+1>ff@W8QF-V>jKWR-Dp#jEorzndjZ5&mha?@ZFx zv8Nl|H{XAd`9BvOh`cS$?hlIugLK`DWlHk1AYUkeTu~AVa`sHgoZSuU0F~+?!Ra zI=w$)q}?tyeOm{O%c_ss2389m!K@m7S9a|64XO4n4Sn`alE|s%m{4$b=Q5q=GLx55 zV_c{byeLr^mMixQn^kKXX1HTrZ?<~}n>du8q?!9pk7k5FLqp}`uze|=7^Pn}D1}4J zw?Z%)KbB}mJ3Zf$ws;O&QEfRRj#|R6K(inf_ImtxUzMflKu-?i?Kik`w9`2}y|2A) z|Mo-&CppN`A*|32aK)OP!_>5n;YL$f%c~$*q+=J2W6HKKSfhDQ;BQe;RfhO?Y6mL#2HI`8-hS_IMNOZWQqWb@(@W(Iyo$Vim(EG z<9Cu(JC3q~h{hHX6=irWk*@uf@Y*VhfdCi)BXYv{?%x44l22t7CFO`>3SRQe&A9Nq z8Zys4wS3F7od3#NOejJ=wJH{=$$bm38`IZEnyR#Yoa`!kR(>-#>tEu#&s|!}_GCur z`|wFGnNvJE)>O-OKFi7WVX|8GAy>HNqu)*Vuw@V_j0#-n-C8&-(-(iU%+3_29~q1{ z4ERuEhxF4$#e#En9h{%ScZd;9OtBH(jOu^94R?u8Ur77CWmZZ6bRtE(cm2?RBtFCR z#thvcn)xSY2}-!cf_JiD5eqt**DcM<^2l^vIKevmKB8x0w4@-bIf&i6zDIn@f;lv1rEXb49wu8-+O1dcv!fmj>vJEBPogP7iOfMcA zey|nLzvHesZSjp2|2XhMO=s80k!Kn41BQwpK7^>@y?E!#!6#s2@TTyRGCt8N?YX0e z7ny{Hx&}|c7P#m=8j7n$a5w=wzb$C00>~GAHJ64MZQzi|j~~h?*n?iIZ}7@QrjThw zN7rcJ2^nAp$y-ibRGxR3^tW+x9S6BVf2D}i?B}+hY@@|9wqewoR^(itpP&%oo;%ao zFpt)CF265L6(cwRzLTSX;`5%Hes<{TYj+!XI&Czy2GBW~%^)GNrHT@}} z&?atHX_gPOc*mf<> zP`$Oa#0au4eRRnP`{d+Z8!!5$0__6S(|1S?k;8yin7kSLY&O zYiS~R))_0lQ*opgw$AH4Pi-yMDKmx9oajf#{d4!@ zAe!U5zOr<;RW`p9f8;x8jg8NKklpXW%riY!I}4mz4|+hcOp}#d%j@aNQqSaF?@Mar zK3dFH@}FPXwZ8Y~&7G4fOM;G1MP1E|BEHs0BBv*l3?e-sQhs}jVSgX_w|2oyLLsMq zeH1CWwhu$F5!#DYHKwN(7#{->jKj(_%QbLAXt)-~Z2SkhpCb#0pn7Dpy6RH(p`LRx zp2<1}#tOvlr-7Jr#*V2ZmhsGhvVRv7IJR((At>tTYhwKRVu^#aYOlsxm;_wc6S(KM zA*u?Cr(t3%M>$e{?$!FFyU1xpfW#nr*+5+_8LdB(XJ#z$2O+?lF(IZd8 z;OcYTykfb{9iADeZ9flqh#vyCENJqm^u`jF9oY z*36ImU3GI)*A0-pLyN7QdN4X{C?-EQ<{wCe&5&`@PVyw|LP5-BRB>7b42ManL`C})=T;!OHcv7Q?0$ICH zWo5kG)+^lXuPI)(ogVgBwY%ni&zJkTXMFLzo-^4?w5I~iXXm+2ZozlGls~B&Y~FEK zn?8ZbnYEK8K~vp!TWbv5)1+96y3V3_R1s%F<3#BtalYsmP`pal z-EG2oA6d#0NhWKIi|ExTsh!{b<>T67nKfCv&);$`M&W;>sJdsG>P}A?z58N46!_Tt z){hV`Y2C^rC!0t&B%AX~@wfuMJRF4@M|84@-A+(jmu`g$B^EI z^w;qrleR9!TA%xwM;ijJ<0*mtSh@Ecvn5&qtm^*r~4wy&z|52?6Hwd z5SobV(Uo25TwJIfvxoU)#sX?QVIk39ktf^a+&9*%Ogqgin;dsF8&i7QYxMUw5QcoC zdSi<`KO^^G<|1WB8MVdC<5xegeNsa$2bxz7r*ogyu7bbR-K7RV*%TW22DA3NGXfh0 z7y40JcpEw2%%8zbE2h{oU_}^l*iqXQQ7jpSMuy=w7(}<#$kj;G6-}=MgZ7uH=U4Xx zCLWtU+`XrV9qcPkx4l)4-HC~8ryrNUz77YUl_IX7;D25XeNraMNc^OVqNnP<6j|H7 zL`34~Q+M|8lVwTyUkm^{w-Rrt+UprAKcXNo3wd}wP@xqTOx+IYMnr_`#Z3ey- zvE!`TMk910`vHrk{AhuEDwAvGR->zf^^S!u^T#@B0UP7s0#BxN{!v>VBm2%q5~7GB zwit*9!#kdyIj8`FZ=&~%$i07&b7{S$?nD?S+_ABmj0S43yMGv-VoA&!iRDD4cSxki zS6vD__x8HWf$s6UO1Lj*6W^>e>H4X~&=0=EQCodeRqe%K`2tn~q)I6;U2Sz|a$|d~ z@(9}XycD__{kbyiv_2lbd0HJnihRLpkhdB({jk?e`s7+|xU_hCs~g$MP-yE~>G!SW zz7-~JN5d=y7mrZO z=Hcnqb0F9+WAx}hnFQF<};^}c6t=~7sisCtT zsr>Zsjd3=vUIJ{HX-gjxpAQcmH4Q>qTox9?IGuNPRJE!s$Nw#Rc ztWJim>pp*XFetd*?tJs+pjJmOcWCaucr2KJR5M7X^K#MEyWB()>&*4eZnPR*f@_;@ zuoev3E{fNTCntvnoOs9|jSipq0>B`enXqu2RJB%?ta6&@GxMs)(*n1R_ejGU3_$Yc zFNs3p;~;Wi?eT}0_FX&>Z;SQxo)jY&9o+z$sp-gk$-DMqp~sDjnag2P7dpN58SF^? z`aR$=cfiK+)!&5eX6)IZK#>Kyuc5GCk;22FtZ+FVTR)4v60mWaIn)I&p=6jHjkT4N zu5``b*Vo&{cA;=4(;wluh;GkxsHi6@ZfQ)D=c8n6F{3)Vb(d^D$y`tP-R0V4Nm9N8 zR=&MD6j}UL_nG<2HJBsRfWiH2wuYo*Ib|a}ny9AAL0cFycYCtdBmC#H`5xj#QPhXS z`-(d^=hSb8ttQz#tbu~9_oqA^oO5`JeFQM~P7f`&%K;KoBdR1EHO_AxoO+YDSTF^H z@!qc|zX}rI{mQ=E>CRBu=j=hvS~AIRL^cvSC;c2aP? z!k5;%JSSgE1~bWCW4%yJZ#Dzb??P~)y0E;WTP2e&r>9(yvnQ$dxk;-nj=7SOWd5)h z4Z2bh)H3VBg~NVVJ(vNDZe$*gb1__?r()_mQnnr9yJ+O5&#$l8$LQ4tFDhQTjgFw+ z*NgRn-v!dXogSbJ$O~+_J%w{F8DVSRfygDJoFCFsxszniQmR}=F~MY@jQ?wh9E0gx0S`` zCf0AYLLtLo3aEP@oAPqE=Eu?NW%#spRKM|x-z$-rP`(S;W5qyeZZ$E#0{=F^j z+wJ=HhglcB_W>jVyi)piv4@MV&HYjhV<*tSkX{6I43DDE1+8ZFT!Y*R-YzS7%r-gN zf`4gcwoZClY+kIC=LyhRj_fTdTxmoC$G6-AImj0ha``$RuD&ZgFG`Sc%YI zzElO#bhkj=oE$7p<{gnb8;8$jF9TUA z$QAgy0;$(c|9#WEUGqoc7#^}Sxx`$8i3&3N{nd1Y4V}9Q6UOj375?1-rqZ|j?{IqK zXZnP?I<+1tI(*d+kPt89Q|9$R1`{jvHhDoRThg(~H`$l);98fZ*An+rd`&`Ocz*TTn;yd9p>bQ9yZhODETO7KyGBq>Prq)KPG|NbRo6o-V<$8xDD8uAsj@@ym zLSWCpP;~Ka8X_NaYFSqbkHLi@jwx{i))Th%v^%||oz{cNRzO6aezRq^FnOJ> zh}d2!J^f@+l2`d_{4Jyt#~lk@h>2rswSCGc+l@_QrYpbJNIUNA zDQ52^p<}mEN`CeAd26!Q#koXQF?Q&A_6?#fv}rwIZ~m6YxdBQ?!&>{=>wbEPozqF? zajk1U_H_#Ab(M^*Tl;nwUr2{2*YL}w8iZE!cp&n&KF7_DhYNBjq4Pzh58|&K&!jlko zNwSkJ{*;}-^TJm(U-QbRnDbYA+xVW2ht%`d%z4DGMK-Xf6_{ekaK&2NZM!u46)mX* zx}Hqto5A(yFHdRI$jnb$iN`Ta29KVrGTHM*>u%CYF7u?jG|(r~8?9Cz-5(FryCQi>ic@yK z=J|`ZR_iHaA*7ON#`nfzec6JdtPSOsSY##7Y*st0k!s|S(LV5R5hxxO!mG4((+E*9 zP!US#nwK#Li=V~}Q3cn*#g`FTq2?3t<|DHAC9-|8|D(n`qSh>A6*!KXVpd%G^c zVks}Dh*n}>#9)IQ;Zzm>;lepNZ51>}6T|{E${?L7qzCUxv=m?BupWBiM<7%ByRY;5 zo>+X`KQl}NN@b4Qda{;!Dge#I0=_=q{?Rgcy{hk#__3q;-3ktNyv32-No_WG8}LGfNwG?ZCchp35O5&92>foc(0lrD;FgEn=_C#@SU{y9CfaoN+ON zHz6TO6L+`)4c#1Sc4qDqDD?c)dWtuFSVn$aN%>9<{9c>(d@IaSYT)#THAi2ZWqU0U z=k;4$ul`Ur$sp>7T%^Ty0M6-av%qdq1Bj8|{xUq<5yJr>gV!+y(nm6G26 z9~MCU)WRA>n&eMIF@{~o6bcIyr7-saz9F#RU9%&O2^<|=G^?hLodhN#k}>*|*7GZc zS;TLj;uM0RP~RsROHzotzL{slqM}@6zEgsv9KXMFNi5FBQg#zv^edD5<3h{<<_K0| zzeSauf}_V6O5>!A+S~s)w)=5FikVIA=S6-hepj!vozst2Tg(R5;x=m|N*qdYHdwVt z%&_K_Q%VpPYXJ_uILU&p^89&)K+WNjTC+b!G?7P z>EF!FPY-F3xqkNj`NO-wn2?B0W~RH6h^Sq~?QI)LL#!Jf!Q1 zDM_IDo*?eJ8mLM#!G;7DW^*9YwVL5O#!5wU=h^^*W{7 zWs>ey2DR`Gq2kiki-}$xP`~GP%FL!SZv3Y7(3@dFw7Yg!BIdb-su$9eVX-WP(HxS7I zHk1|i!#sQ}3xg1X=t%CLPyr~7MeWbu{+TGnN{L3n04I`jtU8GO(9h=%nSoytSxZ3D zH7x7p%md{{h4BUkqaOK(dH7lu%4i7nR0D9l1(VJ`Ou`>PWk1?;`!ddtmCYyedRRDc z0whjl;``DK8&{)>A-;TSZ9gKM2OUf#peV;I6al5w3BkQ^ThB+laO;l(cqBG==H~L! z2JXO1yOgu7z}H|LGrI6IYT;Id9Bu}~8#VxZ0xgH*95n8PA1h09Jl%)y9oh1`XdU$L-SsM3 zIu@y(^gk0)V6A~>OoVCT6Hwfe+lSj$kH_0;Wma8wIJaL@yJ06(txT$HJR!wDi6v;( z7r&Kq{~ZoBN9Ew@xy}5!k;x_^B4)TAr=_b3nLSE*ehfF?xNdv#`Ju?X-m*97pf#$f zHr#UNdkfmokyI2p`w;bb6JjI@wvlm2MGy0+Gk&YWmCp z4!;V0LPyjOCuKf=xm|j@?d<6OM&dN*CT+TCr@hc|7;g9mR&#lBH=Doil>Sg=TUDle z|EzqjLyRKtKB%}sp4f5YcXSOu7jm7j)2uA~5P$gC&to^W#m{h&f~Ur%qD5&ovV64X z_WC&2xtImj;k&a4hllxHBP1oSrlCO#_@cq3K?N`xlit;b_X%ru&;Mu}$WPw4g z&u8edQ{T?uRm1$|vob-c6^yBZH`k4Jo<5`LRHbMNDjLM!;bX9cTPzo9$(nO*Y9RWB zrGC~jZvj6GUEf~XrOlb#W}jW{XN9nzb*M{Z_q|Ei%&j)b`OUq@$EQX3P13PtiUZ;s zLCQtJ0Hc$Gj#}d%Q&St$Z@C`lgRAi}izgnt;U(C@i+RPkyE~|g%?oK;)L9vO!%v^Q zEAY0uL=~hp`e7+jTD^ZLt&vZq4^!J~xaz$3$M7euwfMjW-L-}Nef3y(LCWB|yTKk- zK<<9nrgCFpjYUe})A5AtTpGUHMoV@izyz}=RVCfF!9AW~%k7$~F(!}64!IZ_&^Gba_3O8TBrn%>k zRv=8PsNf!^H;9mr7F)UK|6B3)wAE&r%!3|>Ht&>d`L3m;d|t{QKq3(xT=1?{nQ%$( zZX)(vTQbUYKA!L9Y)q`4=Iw3}DMFCz2hB@)!7%zyFckOFfa;Gy zQ9=AwC*f*;FYb@Fi>%A&UcZt`7I@k<4mNERkh+S9 zM(bn4V5c1Oe>D*z%{6hz@G9XvL(q$X?=e);R?$1+fx!{GK7WmtrubkV4!?BLN zH-^1>NB4fd`!934xgNJOg!0*${$ndMvyVWc{r}yvQGmRgSx}ca;S!Pfm+qM#StkEc=jME_hnkke<5F448nCGkp zq3=}nf`UN4_gkR_^k~G~9{}z-pi(RrHz=w$1`2i9YA`$WQfy5vq=X04NY0q~xPzg z5Q_EcCcSw4{P$P=xt-7Q!E7O5N4>OAagxMsKdKy?z5bL%_6ELsD$c8~j)96UW{hQw zJgeefk%I~Ux{`>ut)a@$75cK3lJgzi6s1T`17^5wCK1yrAEXj9jZBQ-nf&XkA}P-g z_nJH+9LmA)^`=_IbEA)eqg0`0Ox@kiRz>p(5XL_?jvfy|jHL`JvkgLt)2)A7OIKuQ{pQu+;+ zK#8y*B}EQ1ix!Ja97-}2abr(eC01pz7CNr`ca{x74+iTWwd^b88C=@+0QPck5ng9m zRTk-GadElHn)(_ksqyh7Ow|*Ii9ec~35^XNb_C3oNt5Y@jp3c>U#YkB#SHuNV0;39 zmK)6Ie-+N7;82cVdkBA}){p!)y1NhriXvmntD|RRWbSl!TOT~8D-!6yjlB-sj^^Xw8l^#! z3$zrYo3HqheF7^fnJ(4~k0u$$pOugj%)1T6#E^g0_?kxep+s)eXeKj5H!eFfHJNmT zkz6AseF~KEpXE0e(AXjvOX-FFwcRd~xuBE}R_oS-oVih2`h+N)P z6ZEM-OiHK9SCY@U4@LxSY?fZ8X+8o1|L_9x^X}r9=+`a{H;PBMy zda5I-Vr69|o{s@?$i3(Uf_;16Eecj1DVoDQAXMbTya_QgCHYE_+Y7 z?~^_nBtQ;P^<&G`UB?^f{Y`Yyu=H;KY&`EHWv{6Da@ql0ufqbm_Z~N1myTj4MnyWH zM-(RKU9u^Hy&R#m$P4u#W(q6IiXbRWJi>1c4ITz@9A4S8h$OKs8|CZ|1!4>rmxC2( z3A4xK^Qs!Z#_4jS$0_6*nBHxEeIN6^yrW+};^VtbUNl&66Fo(6ek*r}PfG{ckkMgA z%`v!z*M!tE&^9$wM`1_sb) zR8fII;3ODI?_v{L;8e7Qff1-@8xIHi${c}wk();QW0vtq1q(g%1EBuf$vmQ8Ljl(s z%ohenh6WVAjtXt~cL`Nl9Asn&O6qzf%{OUi1>m0nX%(_cEz)u@D;+7_+Jb88~-TnA0 zy~9O8QA5!{6Q@3P2N|Z|3td-1o9A%o6(?mxQKHYYGojM?coOV#$@j7h=8*<(rJuae zM{ZI}ZuiY-WK;0+eNuft!mU0Rb|)$kT~14vJDUnTD94oxJ4L>Yhpu5=g&1<47s7q% zyw3nO`0;Jk+O4*v#m?6UGw0oRIUq9 zrD42@oY=5VDT8zL_-5D;qQ?NuVRAVid{vxd-NWFOs#1$~?!5en=0sAGLdIsC^bsD1 zbvR8zJ=S?}TcwQ;${7LpMbdEdO`1urlT47WTYCO1IcLBBE0t9+Yz$JwU;yTv)Zu4k z>89)hc&CCXGt^xEh9~^7OFcn)*Klvl{$@Ob?aX#n&n|6LZ@cY%I+O zAgghd%zlp6m&vG1OvH_5gA!Flf15w(7)P_ATFk#~TAee13n-K}xlFq@s?d3sRMwPq z>kZ5OcxEZ$&n8uRmtB)MOh5&nf(Hd!29eF6Qhbs9l*?Xt9u^=*GGo!jkc;yqPQ(Ey zXzFAcoaP6isG4X_&nFL*IvactRc1gatl{?6NsKW!b90O4;P1pd6x9Cwi)2}+h{|&@ zN3Gh+AbMyonMAhmJCzKeA}In6tYRw)C~UAJo8>$xm1%3%P0heAnLl#)zSOj z)N{SJG&c9ML~YNi2InsVD*0H#eRY}Vw{W~jaknr6XQ`)tBj-_%$AbX-+W1gaCb^^{ zt=K6SbMtpIVOIz}QI~&LN9ZQCrX5v? zYHh_S%q>4hJnUULZZqNK11;SJphZdTlhs_OU;q9Xf?Fzzc@o%bP~b_;ocioR?L2GR zyKwGWV!EWx9i0!d(pZUP^1;06PAy>;#V6b8n7rd0|M1mNkdNOk<9@n(yv?6ei?MWU z+3V#&Hsd%sC5NAmw=o1J+knAb8T(DE70~4$9^y#5@vwH>|5Zb=w3IVUc_B^t5apmL z5RmWWI=9ZA+bw%Hbu^);#F*+ESa#5K8-tPdDcA5@6b2hAir%ksKOrqc-tji)jGZ-O zKgZLYsq}L{N}mV8_b~oy;MEoO!`JhxXu%eQ^r06vmx(ang<<4&dcB@2VPs0hIdzTh zxFoH!o`ZGlx49+nrC$^iM&7;{1toZkCjQ&jJuJjfpf-{HUtyMEx&Vyj5GAk0PlkG* zj~|v9%@IUM_jRt=2n>lZ_S&oqI}M1^il3dm`L1uw&M-e92K5{1(Q7$*lOsa@AX0wm zJly>@VZTQ)Gb!K`Kj|Kx68MVH(Oa zh@RoUmtV2jch(+@!6z*V2?;3uz8T}S>dN>%dpgFil7%Q1x+GPQ!*;h0M(}u-!=RBj zAw4}kGCXXFr9sa2=kV?m2V{rNwWYE2Z-|fr2?l0*`U7z5c`{yb|NSpx?Gy^+0(ygx z<`v17RH4KJsGi4hM~x+st|5ERPF*rhkb|s4yPk9m36$2bdmcgG)GHjLfr(K-02=rZ z!{{hmW+nJAyt~B?uiC9HFX}oG`tMeT)M*+f&<_wH&)(}dG*^wL|IS(% zE=EqfYd`tF_Zl*SMmgiV9bwk((-tgVPufHw{m)EOv2pf?V3P$?@bYy0=Q8~mP-Tm9Ba*w&jp5i z`}#x;Cc^)GdC%TgMf&dpg8k5y|E^%2LWjEP-?drfP+$K0k5>fWb3K$a(ClA}@k6GK z=BMnSg%czo*@VH5_y6@NQf#%!=ToJJ*s;7Av|{qUO7(wQVXo~D>1dqE*%~;@2 zI?qUSas1cTp?(7CLY16-mfyqKa|UA#RmUAAmiQWi|DMcRg9k4bM}2tpM?Q6&Ms`*b zH|k##qbeg&HY9}h@8spmA-wp?)c{6TW}f z@lvQXXn9iazwTX!sE%Ddhhx*Bkqb{;38DiE=-ItSp^y0>Wrs{j0NLBy8y+66xvEbw zb0Bdu8~W!}L`X#-#VLre^%2z(CI98-xW9tMJ}^!Pl@7R#Tv@8>i$u#1m)pnmseHY0$tBv&~?VgMzUJukQ5 z_V#uS4GsJP2}CM1sKfgR(1|!L9GT-??5EEhS@Hf(g8027Q690%crj#5pWU3TqP^BjHEW%2P9%4hCIVE!UsRks*HU*Pvmsf6!cH!=3)?)h zZEI)Kc@l_0&@l-B45vYOp%vr88@qz=0f0jp<2x$pnPOUzb^=>bMZ{I$o|pVn`pa~(~Qei7!Pa`92M|}41U*?< znK=c05CNRH%+h32(@wFxTkkE=g?I00qC_H{(+qtbWHE~6W0f{X&4k<1MH1wv5}6tSx_e;l>+YUW;6giO$*}n`6m2 z!SEo5xbJD@s zR*FpMX4v0$SsyVL3#px^#^FkyrTcbqkBV=X>c@tIwM&aMN@m(Im#FIdK=3ha=vO&R zN6yK|YdCru3VM6*wFpm*Q9_+evQVjI9_Ky0q3P-VUPaTna3$i)0Y9G~;A1G!i-ny>)^7>vvdGH26=UsN##?xt8Sv!UqIzvz}o zlK5jJ=n;ZhK~BGY=7TVhpgT7baZ!eP=1}a5`Wwcj5Y@r&g$=S3@S7`(x-6lhd=gV1&1m1sDGHwpI0^FVH~ZwO$bwA5VdUHtfR7h9w2Hpr>#d)GM> z(AW3!)C$~KAGyk!#&^Hyubs#yzIIn1IEcWNeAl0_+J5-3l+xSVz??@-^OOZQ-Sh0J z;l2o0_)=ddiS=5_(gjg9?c({m!br^vlsJFNm)w>qvT^=v6dGENJ6DKh7wLvXkfz3 zEHbP<%BoP;)3v4r2$8{%NGvd6n8uC-6iWfuPcOrv)eXlLCAvFT=fVcstWx!9+{RzV z)RZSPc+PjDiI0Jur{Q!45D!981=)A&es4*3z8ssbAm?t7U#jXDqa(NhoqK@`{|yWg zDRY54Kr+zL#D_53GYI!i|J+Lagb>*Pri&o(CkQF2W^Mtdn4 zzfD}4`vp?_rU!F2m`xYhE`eDxUxEfx3tD}BjP&zyz$EfrQ{>7V!bEw%Y+$8fxp=;#J>cFfg5PSgN`j8%uK%^^&ln70Ya8yB%FY zCn_i1YAjnz?mCRN@2cD&$nO@eD?^{%#81UlqIGC?zBjuFwRXaBhAsnDx<62v>~&mg zpL!pzv}_8J3+)~ryMs*Ym|fRH9r*rvK$b#OFlN7d=kD#LuMj><)Yr%eBnmiSPq}Ih zC(?9#KqT|tL^mgp2juzl)@A6Y(-sT=1{$QWIZ#hkMW&C%xJit#S6YK^aZNx=wX0#z z@23xuT1W;A!Xu4xj``5PAaCid?@T=R!1N0@c|3t0swZtXbWQ;P)Fs(3V(NBGdR@43 zrKJep!NZd0MR>b3@BF;(CLfSwT+*=xs-#SVJ@RO`ih`%~dpGJ_%G33?{#>6NyOz3d zyo4ttq{VOWQ1FKG_;q=zd5S?Q&u!u-51T#eF5gl<&mnpPFKU`~TgkQ}t*ihG=d0~8 zTD0hLbtX$~Lq$mu1;_UeE+cVM#J2v-y7VBv;5?^JG8IvhWko&K4_{ygw)8%ut<;wV zAcS`eD%T|a%A!6@b~DWC)nY`xukN0n@T01VEuTjN3Ju~e%zrK0Xm_fX-&_+kgPN4^ zf9#qRmep3|;jk8=k1^!7@jBUipM&J@C4P6}?h#r^NK6MC1U`FPQ|@&rsTYJBq^#E0 z!1<*{w>at{_}*^w9!bT(fpLUcO}>JFFJ2lVmk%9JM&?kMQW$^ORjDaj`E84>ozuk5AU6(Qg9^Ax+kPp`_=N7iF7I#~a*_3yUy~3WC=dC2GtLtb;!9D2%XJaCN zC$RmzPi2m-(-)ri&|iBjHk$0T7N`Es)xoWD*|1`Yci0w{mO@hjpsRg5co`>>OjQ5| zlm(~JLd928vt$~dBkBsI))a8{G_A%UuvA^-0vbH-d26)0yBTzS14_a<+j{O#E*Y*I zruzIwgwmxxHWay+}Bx?q*1S z-^6M7nWoZ(ZC5whMimvmYwsy@q#&2UiT@U?2&l}CIIL;8!}Duu@c{j;3hp_44YU{+ z3kWph!$C&)LISTIE9nu|taA!%88V?jb9}g#K*8&2wK#Uw&bJCh6+=f9BAA#QlPhe& zZE(?wuvy@V`(#%~I}mfgmc>FFTxJ6Ei)5xCd;xtT>vg>V_3f#3GJcG$^#-W({P;JO z8+g|ycG^TKEv2IM=bBx3*7NX(DY&@4Pi8>S1?^2iG4L@|!A8((aB+G00c_RB=RW!PR z-);OR4pGWX>+p~uzOOYKVz1%)XK5_f;SR!dcJHxSTxp&&chw_syZ4qH_42*He}pqc z=q+!so~op%qGHc6+srH+BRbvP*`w+;8#PiBt=-)DO(pp$_vwx+nt^AXt+n@Yz^yE* zeskMh{3OjEHxErYTynK%Q1m^ED^|3>{B~yRXFmJ1sK?uLyU&Zas4eu1@UL#^JH^2`VY-Q)q|(K@xUy3*2!Y{oyP4HPX-Oc6zC6$yxB1 zTLLEf>uZZD@XGJabC*qVfr2*=r@Pm>|CrTDwU?bdEZkSn>L{(#_r{2_$!={KgG-3v zeZ7f2D*0nQlK?Zb!_Cp6$CUxN<0g!rmLMcPx7H!6$*(sOvPw!ym*cSq6sLuTlRf1k zPI-|jgUhU(2Ck0Cz3vV;D(PO_zx_sn!j|!B?<8Hjm*#h(yf5xrld=j`nUpPaUWY?E z79TI%A{q-jU;jRODj35|w=ux&Js*K3G5$2wub7eAT!-iYD2=v42vgrd_n)coQ7TN^ zQ8`K1UWe-*v2ltQ0)DYPUz`k zt#?;eqefW)%D!0F>TompJ_qo>3vBQNWqQBwj)$z1zpKfpnZhd$4yt+rab(J!G9B^BG%-|p&FAg> zb~(;r#zmOoTVe>K^;7l3c)?qjiR{b@Bh4zAe2>fO=3e%+(Cg-pcizL4OMSKLyp~F* z($jC#(8e$pcS_9Gbq+r^TQJdrFB|KVNaM})Jlh1$yBeYNfW8t#$#@|S?cs(oZ9p)0 z4CL|doIL!?=j~)=lF(n@E0slxE0lLj9?HbPHBk`+<1Y)L=YD(mcc^5$w!j(v8YiDK zaZysH;+t}!uLsigUrULqvThd2wGo6dp=#L^b6Xa3lyr@CT?HKlhL%61JSoz&)b-)( ze}u}PFZD9Ta{$4r&yvEm%Z?`(@5+^XBfb}gXzbvYAaOL#K@n)CYSgr5rbWr>OSJx- zH+{l7IzrzM1|4YlWXs=6hXKd>Bc4SY9GpSIwiij4CM65E5Rd%R!7Xgn+}Z!`w1-a} z`R(tep3+;x>`{etQAN)6kx@o%p~tieeJ1Nu$FX&zyGCg)*WOn<_Z&M*%av9;!{^Tx z+uOz?-I@)2*$((%(=?qcub5r3a7sxR6@P<>H`EZ|nq)IE)>~iDprrHlf@`u_Z`gpQ= zSDjT#`MqGe`slG0fJk0_x4hflJxS>2n}_%gG|-b=AhITzZ=ZPp)3h@+IZHShT*0!6XOGHe}a`r-v9Ppi&p?o{!ZsCdC&r0G&o{O(l=p_irdYC@fdl=-9 z6&xI_W$5#ethFvPKIYn~|5;4~73;_%HaKR(ap{F;GDoaj0$F^7T`>X!_9!h_3kQFJ zOOXfp7a$m#9Wj&b!vO>oTXREY$NuF@3=$9&s?+4CC80b?By#=SOas5O?+boHkK;nq zRKy*4kt#D(}aek)G7OJKX+ZfMMb&uLH3 zZOsK5K^*a!5uA2&b+yX4)jqpw0D{l&{NA3(U$*fh`C=Zu`;o}f95#Fo3zoNTXo0KC zm(_NY8=Xs!XKqmNEB>{+XOE%l!X_h2@AIEuO(%M_gI7jitEQfcRZ-elnBuF|<9Dign5dl#c!)^PSY2~g*m~t@RV+IJKzLnyg zjWXw8oqQ|8-fk69eGe!hnxXps9@!yH0{KAl_dafC7QBfmf%7V_%lUfCTIV=4TLXm% ziAjd1m#i-0)s$bp{Y}MoJ8iJ*=2|o0GXmuCQ3EKt%-!h%d0OwaGb(AfD|z z?EBDxj1ac7QF2Ngw9rz;Om45+lexchoXl8}mw|w_4KfJZCrr)Y9bXo z{PLTu`JKnq@QZ>OUFnD*f1uG;sihQsUXq&1$}7+A)Pq-fXv9h_=M4u17UVGXN#WV` zIP%llg3>dUoR`;A=?h{wMnvh`Ng{NM6U0(y>tTq5ndj z1pkFRbF~;489VoLyr^XzN%rOB2YJzvDfO4e$H((KAEzSCw4uRCXvn_zi@@m;335I( z=BZpPfd~hZF-uNP)_^8`@!D%> zKI?@>v2Zv*2;K<#LA_GQfqi@0ABLNu)-`OwR>mhSkr@7o(V9wNBFEsjJs1uRNLptM zpU^<6vOIc%REg}EZFB?sjlAo_JJFk3TA)_)-Ji-vLMJm7z=~=N#ApC^f<(pLj>Dx) saLUdT_;UXR$2G(SnUYE@-^gun6yRIy-2YKV0AwYVzSR7`IVCg!00m#^p#T5? literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.lcn/doc/dyn_text.png b/bundles/org.openhab.binding.lcn/doc/dyn_text.png new file mode 100644 index 0000000000000000000000000000000000000000..64167c7b23ea3da5b7f42b1f3f4c75893fceb8dc GIT binary patch literal 106065 zcmXtfbyyqU^L4P|uEo7jytsRzxVyVUad!z&w73^7PH=a3DK5d?UBk=g`@FwD_Q_`D zW_Rw)IcMfhB9#=RP>~3c0002$4`~S%004UBV-rAt{x~9%koofQf#@Wy?E(NGWBs>5 z&93{u0s!QI9}=Q!o>`~s9$7e&EAW?K)%kU74+|LCaGH={*g7LVUghzlaqWxAESLTY zf~gw?w)GB60l_*TuO%Bf7Mz;%3J*wp{suBrH--f#c{%y)uOTcuA?GnQK6yK*s4gh^ zu;!xJ<^LuyMMX)_Q&qr|Stl2pCLmWHIx4o4#CIAx8^Co-AWs$$bn(7;a23##*<- zqgtZS;C*-8FsHz8T{%!&mugJ#acYo-8AUjlyrwr=(q-fk9NPDe(CwU#vQOq^lm^i4 zTZeeCn~kG%7XncGSpC&|og{4S!+u~=gpS1icXv2wD;=Ys6{U~$SIiVraEu?qe+P{# zNG#BT0RT+xl!h&I5nFi)h6g`(mlJE)lhNmyt55&C=2ZqG!G3k?V!VATq92i>cevoL zs_)86xMa+{rCY{!Ku(3ZiPu6y28Ix0HWBt?IO@ujHV_(zu1b{!enpalkkStv2h?$5 zG)8I)v#~6>%Ct}`|L}^2SwdKPMoK_Mun2ct6OKxhdOK>xs|Yq`nsn8roOo>bECO-+h0=$YRB*lnk^V~WUe zR%^|R9$>#_Bxq}Gc40p^o#GwRKKL37ESy0~H?ZGTT!cZ6YTMshi&bohuU3h~{T9DN z9hczaH0E2qikOT#|IG|{3CU%IAQn@dVrE=sWT@`LDf?9C%@fYoXtb9%wyulBuDgr( zgJbD`wYcytdOyoCrARstHl0-2zqF^YkhN>eH>l{yfG#teNM8@?YW=|25LcN@oIG|# z!N=5xu8Hs3LpxU__kz9$6n;8z#3&eRty1UBF23SyDwkYjKQ|#hD0S()zWBOW-xw5M zPzpLwUQPR^9#u4fpi$XJe9t?8{{CBneR)DPEwT~vWy!!srn<^vC7M@fJCR+;n(uNOMvgnr%1K)vldv7Vn{h~LETA#F zE7YwCVi&e)e^ispRE8MiEZ+liJeO~8U06c3*5!HpW#=A4^ zB$W;O>c5`Z{tIL`6K#!$^YuP*quIhA>uny29|2)^A%SBM)5UQQR}|cFzsy~MRl6wk z@)ft8!#&$HVWDDw901T#<~QL7)T^X0b$Q+{*pg{qkNQ-*{s@E5f0A#!g{{5*R zhTHbwE1>zUDzjw2-2G*Ja+Dzy{-%kabY=gNf0EaoAa>Pufs}va?*8cD-0sV4W|r%U z*Q!S?G^#8|R`aR?KDhC&8Lz-c{}x zIJje;|^_VEpEb$jC$I4eEB_E^260P)FhnsfnXU)gSHQ*F^ zV}~u)>+L)b);*rxh2ooz^D3FYFNZ{=7l~pssD-dWg*4%WQsE)u{dyaDFq-ReGxULU!W5l-9<47;yIEOF#|zmlV&| zF|H=?Pq!{KPygHGo#U^oCH{}6AtTWyXOO2RczCx+Y`isB2(M$idRue5@nh$~hW9hY zfFqc1e-Izuqs-@Vq}N!=E#N)6l#IE#zg+Ncv;dw|V0Rl?$lH62i^Kdi1V87w+T?Re z;i{nL35<9vjb-Os?pSr3hXEVdYq5{4WgDjk+CHy~sE-w8Cm?b`N9O5jh%DE0J90|s zn;X7|AeUvNR+UCEWv>VrrAU3z!kZzyUuM7{Lb>6MVneH4Q8ISf~96FtOpqLqXjk! zHrh$eFgBGrD8SyI*xJt2-#$lIjiPK+Ayaf&4X#cF`0cP?fZclTo3eeEB3j><4h#rh zwxZiy(|!&XH9^Otf;;X``QdYY55}rH+^zzLuPpW9H+yU+-@k!&%6AOkj_*L}+@#G9 zbKzEQPxU_)RTim)9$#n1SUPNW(LxkD?mNpBo?xQnpV}GnyA$ISJFjP4NPQsli(p7s zUwD?=fIYyW^=-T70Q@%Tb7A;U;wOlT9sQqN5SI76dhf?pwCj4i_c?E|#DFF0%(9mK znXnTpZ2XWw4C=Q{c-($J28;9bRTrGuzb!%B}JUvo4wItpn~BJNcN6!YBq+O80JR zj#ex6<7rqx`+8b~GngqO!w~7!;RU?zKU737^c1@8UhCB|)A>KLyYJ=63wVcYu@LdF z1Z&Lk-!D4`EV|`-9n%A-KICNX+kF6J*;1oQs`j4o&>Vz<4T}h<;GpExU(uQpewbk{ zxqzK8Lky-HV3gPz-$4EjZe2XL1cM(!(T>aj@%A1UGkwTuX6AV{Pt1MHd`IaQA=qu& z|2&5LxL3WVo{O-P4#HmY_a;Y+LJSv@{wlcUa8?U7nY%%Equ5wAnJacnxm04JLTz3} zK-N1S6!ro&^fE5XZxZ?&(S`5EV@a4$W#I`ifZxYfG+_dr*L~CC5OZ101hxUAjace? z2vj8MNs&OwVD05X!x1nM2OZIFPjX5PJ2k7K)Ud1I^gPy( zLm{XlE`EsqN5tOi%SFQgKSsRr^76jxWMQsf;^_RnZtFiy4H@O7XiWA$c3kxzru$+a zM=~39Shx+9NxZfekOR1ng!Q^uJNHDV-tT(xbN)6TZjVXnsmiKXWHDRny>IT5f5EFm z2)u+j7vBD5>d}1gJaDHR%5JKjwvPdg(z+Xjud-<1x_iI6Y)-HL>+bn2*W*wb5s)X% zVNsn#TC5*$S1Zpu&+PyP?X6N> zr+x>d??+N#;6~p?*9$bjQa|8e;0wh1#hxruv(zlp{=EI2kV1kH%N-St{3Xuk?51Sy zet)1zFW}$)l_%qH-+9YH!xwozmm%Ys9Q}-M15^rakR*dMh}R99qdY@2aC<{Dm%k8t z+Gfw$<-f4MD(Dr;FzyT2_q`e*a*lGO8}}miIAzmy>`*^RVWswdosklHyRAzZblZ6B z^H_K5FD(f-pH4Egt8iregj6BfUePK*0|bUV56Ipi*_W>_HNt7M=uu!PhBv5x76GLp zs%d`%MFpQ_{D=qtYi_0|Qf#P{US{IPz1BXWCbn&X$Gzq?mZEB8OcvNP>S>ppQCp2l zB7yMdS`V8cK^0WA^Cj2(;hej!i_+TrHejVK)@b93?8TIwX3D8ag?xO#GAxOeUG}x1 zq>zfN<8l{K|J`Y>l>d#@QnZ9Qo9Po(MK<0)7QJ}&NHmhKOi&vBRfk`&u2Sr|8Fe zl0@siwmp1Ln*bD&RN4{gJEZE+4@bG5F<{(R*N~@M;U-|FQ6;Fk&P$2F7A0lNc*qn% z(cuDTkJ*6(nwSd%+mYtz<_nB`uID%@RKLXlI4tGBRQ05s5ETrQ0TkpBiLW4EY)a5$ zEsTkVj+*PK3e=&K4m-eU#tmw@#Z>AmE)$i-f{t4Kg$n4jbakY^7ARw@&~$=vE}Vdt zi)LME-rg?TD23ghPzg+s!tA0VIG$rho>vcoZV5aG*ur%N~sx$wt zd2R0{4+soR8fTiwZ|V0g$Nch756xd46EPvFc5h3CS$C-@$zKa{KE{cKm|xTDGqyRm z08gjEp6|IAf=H%D$RtZGn;90Ls0~w-CCzaRFq-IPrqKWbMJ9ao22}Bbb#lDM6v8l= zq&^SIj2Q_2f6~^~LUyA%_e(xw7@|Ar*I@%_?~!WQXy2R(w?b9$)LAsdFbo;mZY+Mi zsJjl)Ij3TRGU}3bSdv`=q!|8T5y-VHKF)ldwEq$W+7nGXr~_;ipfMVSD0wTv;Y5EcXa z3JKdS)yxz~p@04+LEj*zgjLneYdu@Y()IccFFHN1pY<0QfkNVs62Uh!>37Im-yWbS z>Gu~!|NCXjN?%ob=2QlpYj0f1Rn$H$OEH8>H&s3iWs=SM;Np3+(M2}(YwvNHPLkhq6!B=>eccPQ&$bvd4B%JVMEzz`eD0% zUAa_y8M9}J16U|7A7ovKyEMHosGF}}prw3L#}uoWQ`bJJR{P+vk!7&wUc=sozad1qh zo)$U*!DCvFig#^`PcSn%SO4hfZQKSGrQK3(Zg^`D$>e7T@ zaIk)j5~THV2G7tpVO2)AZh3(^o4^?~trxL?3oxy9k$1Z*RY< zH4pE;H|AaG%6RO{>Ob3-s}ReBxe9WT^eCtBYh1MnwEZbSYFf0_aw*_RYFS$g@;|B% zZ%vnv&HN)*Fw7NYS1``445E9@-@^?vtcOlbf?f(v%%3-wMppSCA=nu@6XpJVpaf*Uo8!z@kMa$NO!;MJ1QCoR|Fl0GT2Qc9dFG-}bs9lv z0SWG|M_E(&kjv5<6S4rFKR)jyQs*8Ls^TH_g-Mj-{T07WKnQssRU&PkT0-MjWjee? zH~f#XTWpa!?=(SGXcGv1kkx;+FsZY;RvR~Rm~|h)hkSHYB~|}^^l(PWrfH0&nut?o zVZr2y!Fi#A2LSlQ5%M{hJnR>byj{Sn_zAbpxdC;Q^?@~APHj!h>iec|}mEe(t% z60j48b1|_S*f)41+=C}cn+l0M!9}52-*F4XyKj7TL}~&F>ATRO@7Tc(eXKg5g$A;f z55fpl`I;;j{qwBo&ff94;1#@Z@ZP-*>sk?&Y z3!mw=C42F&IBKkT*dfXxjr3MhPMCtT7j!1Z_4X3nfG6z?c8|G{xr&vdl!N7^iS390-jiDo^IF@GhUuC^q(w=0>+v1$fw!qpzev@lHx5XKlsBUJe^ z`#Q*!e%pyL+zPf7$L;7>M%BLtWSiDJO~vCi#4Cct9~70r#j)8HJt1GU;d1?PeiUOF z`m>cg%mg2EUBzu=5?-XNx~#r-<>s<`obQXSr&nwaOPW*C1XqcBU*A6Ynam-} z|CFl!S#vf@pknePZ2mg7Yq)M|o8PhT$nqqX&0O4qKpuZ`_tkc z?U3jDBiBtYoLKaj0TB`AlKn}hfQz}ZYOvy3?p+B}VK}iR(q8aVN(R9A=?azxVSH>* z0we53N5)wGF`$UrwkL>MBgY*EuMy5#R3;G?D_$jt0D7GIhmOfIZx@Zz455xFds484 zRT%lEh<1t@1qZo(bl9$wMkPidLID{9B1Wj$mJ*MFgsms(RCdHg=g?AW^o(?qMAm8& zZqso}4P7b|#AlRp3JLl;I4jp);+I>oGABAkypEoR!L-{Ks@pU1g7)Isj~um+>-Je-#xY{``5a#!(@MT2%5&vr3(f$ zLA&U|@cblLfIpzJXVV08fD#D~Cz=i?VeF^r@8l__XNRCOyu?nMj5fNB`hSz0J?QlZ zh*L367$UXiJ+%$-8IeU8)PP_NCt?Xo3}~G(^6s^j(_fODSlQb%zQ5L(gBCfR_2J3Vsy>7Ke{Nu;Wqxeu~>TgDgi39XPWkf(Nl6y3+55Rcgis+{0>UNcmPEg<{$P8m`E)B+s+w{H+Tk?b*~-sW0S!!&cWb509|7Df;G#Ox8be(}7iCr2lG+v% z@!v!sR)(VhSzg24PetVtZIi%*UPR8Ks&v*fEC_IxMGl9G&9--(VCEji2M6EGxg~3o zW8&Fc6yG6=o5S~I@AbTWT89I@?tuE{t`SLs`NB(x7mb+c8}araxe3J<0%ik@#cbB7 z_49WllTcIl;`ZYg1x~Q*VJ%0>;Y^Vwn%MV(OFvd|AlYZ?p$dC`P7j5}2XKt0#wVfQ zI3RDf(_dtb{MHJMlRq@^x*j!O937PXR2*weq9pE;=$Fh>4IdqQS^dSwUA_7q-N>QY zE$d>M)@ln>da^w_nI--`=yyjAn5Y(?z(a7`Z%E~HgTg-{1XC{iwrL;PxnW7z^ zWY<0}y1ZEmTVrkdZq}^t!B}WO3(gKDWiAeH0DYa>UkrXAzz6LOoEmgMJ8_- z2?n2ydNKPRagB?x#M-fAI-&Z5Y^?8qMbGW;*2`_VpYHB(BFV8ty?6mu9d`bmD&G)> zP4}c@bsSc|mN@b)b5phsn9Hja(&R(QDw-p=gnL!{(spGqpc9O)+{{b^WK$A96BdRd zsm<&KN4t~oiKTFJ;l6e=;6;at&7i;P30>eudri6crD}(y#Hi_8>wKC-Mw%%VUV8Ux0ukUGUk?lhldjc0&oRS9z44KGzT*8YPkj=Y%8ou_ z_dMp1%dRR>C`6SEbS9HQ7a>$Hw2Zr#l3+ROFSv+JPwj#6U0`bSj? zgCbW-%h~>^f|VV6T8VC4r?sBxkCKvlX`Ua^dqe8u4f={7_Ym*q^dMDiI=fGT$IF@h zNMym5C{EZJHq7(gF zq33{y!Y+V6KxrqXk+i6(ek?RnR$1N&;w$tihQGBp%Pt@n0d)@y8d+~l?3=~E_@+F( zp0EcrEvC1f`+0ILQd3UWnvw@f3y6gEzR#;{+GPrtXd>e?9i@)+h(7yza`4@|$yTHa@d;W@)VZvg z>Hm5Gp2G69E|GYrCh(6_6f)^crut%mlEe&|Te!}!5=t`o(ODB^gU{WX-q)Ir%;`;+ zYqHh6r_+64|AS)S~~*5Oy6fZ^S-j^B}Wz@K!9a#ClhMqy19~9Ii&H<#&v3^lmRYrqQ`4Pk}>K8u-Zp4dgdIS-%7MTn;+* z)sC@V$N5B^&vdchlg&jDOv246lA!}>WFKb&ZUS<8X#K2r9ZYr+ZcmU!hxh*ANVq78 z1qNRK+)tN3$O3bCyB@PNm6VkyS=83YYOT;0j3yy*XeTbKNqvy^Xy`}Inh>fkzRkA8 z)tOs46x)Zbh;K1j`*kHA1CAs#c@P$1Tw)i)S{Q*6&Ay8SbscZjLH_MIuT~{wq;}wT zWVxkkue|C?PuCLB(nh<*0EB_#V({k>FUr68Ds$jE{VL{#*m)lmAXHsd<&ex!X;tKx zyPiLaj}&GC@*Bz^6@!-CUT@YL+TtR(b*PoOnGKaZ zFV5_^+GclEej(BID!{fC{g^MAa`LJ13>mXZ-+<6HpR-tQr<%6xg|sl+Jx?4h|Ey6| z`?E8zXOTd}!Ov7Q$WS#u%Pr8rNmSLXh4e*j4di(tjB~%-b@{q86FU!S|1_3Ji}T@? zk((yCj7JLqMl!UBq^Z-U& z-`!(6{$r7*RW!wI1PrSQPuW)}Gos8jlk@VC9(+E}YxT}2i!2WVhkjCCXg>Errl!r; zn+{LA$djJNGZRpn?h_{m%1+6*EugWTi1Byo^uXEIo}t%(xXVn(f>J~f!EEX5bi9+( z-;;c$fA;0SuMV4p$GuBIRoj~(5gP)n9}BRXt!jpbt(iE+NTSl^@e={;4e?X=!)9eJ zUfjROxXqE|wYh&Y?IKm-U-pLGC5nsLFXqHdwHd+vA7W@1O%lh?(k{3)5;2(P@0|yWV5dF$GfjeHLU4(%Otvd% z*~EC1qvw|daf*7zL#BeLEo7G+a)^CK5fDj~+R~y?`d^6qZ|1(<`_SRU%jM@2%cMeM7R67A%iNw( zeUHVU^1ndas94NZB;<8u27BvHxsEd6(GzRVFbXI>kkz~8%ELoV%b`HqR%+VX^+%(|Xw$5x7TZ(&qlD#b1A!bT5K_7ys^HtF8? zWJZLYacp$Y*)rR0!8}!8!_d$%80({~H4Z<*>TkSNk*avDjuOfHG~?Q7m~SgRu6)jw|!e>xtz|5-l|{Cu5dNaG^0ik0N}$YDTQbN`NB z=sQ%wq66aSi4nx1T**lz$K5Q|WzbHyGZ6?BI<0Z_3#DpbS;rPWM40J+B8(<%&O_Ba z?VEg@S75FmR65G&Xnbta{y&5uF{mmjeSXtj2iIx;dhGx$u4}J<+#(3j!Jb(_35?5Q zMgI!TWJRrH&Ro%k?R|i8OM|cGGRqCS;$V5e?J@T%-y}4^YC`A5b_F?b@H@sIM@Kd% z0K}8H>x%3B;4|b=6$eS0+DLmkgAx;8+PrSh103NYj1j<>bHAENnUDv^87pqDp8sp{e1eWhMqmE>oq%dLFFqh^A8yMt=3_)nWm=Z zOX8nTuQ}~~Jw~R;@3nsIz;xOyy&=7En{i5aY5)goSocWl5M>7Ef z9~g6U4lDA)(1S|n$Jmec_!x6-*%Zd7`&CDIo{!g-_smckkDyU05UkI<>mZAa-%0vL z`SCi4`ux##iuj9qqJ7~h`!fJqjAevg&Imzt$y7eKJVE!8Kda=aL-meZt-$XXXakW$ zGtY(8iflhByN@G?D(n>>%+SO)&6 z$+#S57#vgxavA-s{zBNX1cTn=$ilL?!yg^8R4jmk*p348VBIbwgQMh2nQ7 z(o$X9Qp1LdsA2=2cYaQ3(Tf1jZH%|jU|5Y*B3&B>|ZpimNj_A}#t6ze?a?1p4UQ z_ojh$wy=_5!PdWb*59QZx6V9H z)j8Q|;RS!_ku>?}&#YH6HYh?LK(=zWyBaS`c^z*^UxW%tVYv=;C< z08C1v=2p;sncX^~3rrL>Q{wOS+X?A5rmz5m6SbdMmjqfrP>hB4~A#8G#Kv=?g2WSQ)VPK;1?x|9i`iF2f5O6?BV;R9iP^TdE7 zIX-GstG_3A4`UPfy)~R$#OoN6HV5g##a`Mg1O0c4YeyYZb|? zc%MllBfJt)v%w>Uz5vqx%$!lS0END~-wgMK-0-X_6JOmho!?axEh!nmPq^ z-WK&~7>j}L@aK|sz8;1+q@o$$@~5XKVU_&G6nC)tnh!qK)oiP`%%8GELw|ikKBO5f zdc5Vs2pj+Lsp>OJPPq@r6{cjQnbpui*J!B5s6}YZxS@QXf`aJ# zFal*CtC)n@2M=puLla!z}tjL7W>7t3(;zuHpLU>b8?%2#dP_muRhn& z?t4Sg$JdA{Jf6Mrx8Tmj@NI~`?QGTOw}TI{`%G8YFmEXhi=p@7=P%&fA{iR|PXCD+ zT|57O^NvhEic&h=2DZfSd2Xso9Yz;Ae~m3WzVDS04G9PmIi>ZL7*zx?VU8F&d{QpO z%2=S2Jxmya+>*kur5?&b3(Dl{C+7;GL`u{ z`R}Ukz?W<1?WoRT!t*S;DY)#|p1Gs1F{>{$_ z`9x(_g&jbNXjz9#e@ruYQbfSPnK~RqJ1z!%e)j&4m27^9!0=1$929*$X+V4b{3WA2 z82bc|i>v8XP6tk*)_3*lwG7_FhH*0UaewZe;9=yS*2|Nx>w%IBmPchTi_TjY~Cbo2FbLEz9f~WUs9p zL>>qnwn8xw%3N_>JTKcX7?SFEyUQZ=G4!5<5>0KY>Uhy_dyn-YYjty@z()`ruc1VXfAt;xG6_K(2&n+8ROPoZ@ zY}mhKn3K4V7KSy0O3&|1g!Jkx-cEse4Y4USvz#y=4T(wekL7I+-Zln{TRwKR_3p2x z)r$BgD<^TW)s<&mW8GYIt9D>{de4*0ru~PDroK*ivK4dpopJOMAk)KAmEk`o8zfbV z3fvIhS5NkFldoN@lvbbWn(t+9&m+a4#6+yAe?OnUc+Q&5nVS;W4o-kI!;Klgpa?a( z=Lg6Y6E)O|AW8ow&>W6&C@Z*|k(vwsET*I41cKsl+6ApP>fSCCn06|V`42F`qZ;x& zAC@(SNGVqG?rx7g9Pd~}>BFBJ%yr;zuRl9*=gF2F%b!MB)hiAehxmMtB2{%6Lct7y z{V|p(miDKDC=i1VXQs2G`PdmF=<|4u{lA5e@?7w6e`q+!q8KH^xI&D;UN56cCeg}_ z2>@TOR*p=V;^b0OD-;i?HE2s%6zq&{+!>KmZ8Ry5zVN@3voA4cYh4DFy!mxe5k(tt zXe%qH$66Jw%Pq&NI(nS}Pw0{r%)dRp{f(eCkN~;L9KhI2x+m5wjY&Lb-z;h>h}H`v zUM*qm+dazGQDq%qu_i`=qEKkfTwOFJKak7ieX!i3Tp^E7Xxu1A7KlP)gfOTvZ^s^-OWR86sA z3Dxk&fXzUht{MGW&Hx)M0*5qnef(c-*z@FD#jrxtkrEUMtg$H8PU@Er&GPg3$8${6 zQ`-e*3^afp{DA(vu0V^&_KTA>n=>4g&$_}xrOqshVnzPrHX-xfwixQ9A+xw;l)wJs z$v;n&+-b9|>VO?PuR)V>|K=%w_3O%J_XCn7%d}iS-(}R+1%pFdJq@^@k_ZD+0R61& zD2rVNSE~L)=8^gJR(Hml^(IlU{hDW6{_tXjwAUrhj3o-JDQD}i)qwS!(ucG~OPj)m zeNi$A*nAEA8E0J0e`D)G3r-^)Ya}rmIxGy*Cmyu*<`ln+1U-iC#qWH`$o0Pd;uOC9 zyX2=C3I{>mX)-=ryucAl`>23fHrr41t*##z@#DF7X%e1 zOZQG$-J${kqwtMr$)VO5w@u!!{ z>S{yvVthcioJ5P>CU3EX+QwaCW_8OM85w`X zBR)E8=KURFa^-DF;)cLJ7ad}M0C~m577--w!+XxQUZ_otNn(64!57(H%t776O9~N3 zbvBI~;j`ATK}WT0kS6O&?8yF?i>}=)E{u2 z4qXRZYS`>A#X)F~C?OZSi;vY=BZfd}vY)c(D;P(ffHja$+R5JP))x#{W53j>)g0gX z7tnL8dyAXmVVi^S-}G3^5`KUL6hx$KXp zvs?1Tng%D{MFaH~Nmz{nG4k(hPhKE|&$hi6bLw6A67|>e#)UhrYU|

    -DZ!fI-O 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

    *11(UQ%p?#Wx0Gz&4{CLV%U5fRiKB8 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 literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.lcn/doc/ir.png b/bundles/org.openhab.binding.lcn/doc/ir.png new file mode 100644 index 0000000000000000000000000000000000000000..c287edb0eee96f4fb5902f7a3e393a8dc2ff0501 GIT binary patch literal 110156 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 P1K>|j+X#l%d=UOWj3EB3 literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.lcn/doc/overview.jpg b/bundles/org.openhab.binding.lcn/doc/overview.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f77334bdb23863c40acda4e5dbba54589dbe63e5 GIT binary patch literal 72598 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 #0R0L8;j;mzP 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 literal 0 HcmV?d00001 diff --git a/bundles/org.openhab.binding.lcn/doc/pck_discovery.png b/bundles/org.openhab.binding.lcn/doc/pck_discovery.png new file mode 100644 index 0000000000000000000000000000000000000000..058a96d19c10922c45f8dacd8d0869e3dea6147c GIT binary patch literal 66933 zcmdpe1zX%uxAh>!i@UoNx8lX!-K{tjcc&D0*TG#1h2rk+THM{WIA8kSd*2`No#z=c zOeP^Y*=NgIYZI=dAo&pi9{~gceUz3GQvrdXp+O*sWH?CRlYQ8_M_>c*Af@dL0wJRR zdqK>s`MrQZq#$WAVKoo%X_h-!qrd%Ka3ftcr?wpmQdY9C0xm2NQ#^WrSpKWD+_YhW zw`yDX-{RFen~`>E)-_7%X|aOvd~$i^h%j=Qi(g$#-!2sSCh6Ea1t)%wYD8op;!Hdz zFG@^Iq^GkxXZv<+Jbq44At&8@$5)XdU;4(*|M=LJ96R(&q(@*a5drRh#)BXX-x|jM z{_=0bEdV_t^xx6NBa@2+{oesD`2Tiigi{NRgNhWLw;^Tz`)m4tZy6fQ37#GasD?sS z_IL8P`0+k(_2Iue!C3$HG4HijWh#{DFJg&E(S?Vx7Sl!)Ta}k*pYy={_nR+

    S_ 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%0De2eEvB-rEeh9aHpzqNxqpp@pLHk$~Tl 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 + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.5-SNAPSHOT + + + org.openhab.binding.lcn + + openHAB Add-ons :: Bundles :: LCN Binding + + diff --git a/bundles/org.openhab.binding.lcn/src/main/feature/feature.xml b/bundles/org.openhab.binding.lcn/src/main/feature/feature.xml new file mode 100644 index 0000000000000..4b803e077eff1 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.lcn/${project.version} + + diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/DimmerOutputProfile.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/DimmerOutputProfile.java new file mode 100644 index 0000000000000..938611f30f6f5 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/DimmerOutputProfile.java @@ -0,0 +1,115 @@ +/** + * 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; + +import java.math.BigDecimal; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.profiles.ProfileCallback; +import org.eclipse.smarthome.core.thing.profiles.ProfileContext; +import org.eclipse.smarthome.core.thing.profiles.ProfileTypeUID; +import org.eclipse.smarthome.core.thing.profiles.StateProfile; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.lcn.internal.common.DimmerOutputCommand; +import org.openhab.binding.lcn.internal.common.LcnDefs; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A profile to control multiple dimmer outputs simultaneously with ramp. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class DimmerOutputProfile implements StateProfile { + private final Logger logger = LoggerFactory.getLogger(DimmerOutputProfile.class); + /** The Profile's UID */ + static final ProfileTypeUID UID = new ProfileTypeUID(LcnBindingConstants.BINDING_ID, "output"); + private ProfileCallback callback; + private int rampMs; + private boolean controlAllOutputs; + private boolean controlOutputs12; + + public DimmerOutputProfile(ProfileCallback callback, ProfileContext profileContext) { + this.callback = callback; + + Optional ramp = Optional.ofNullable(profileContext.getConfiguration().get("ramp")); + Optional allOutputs = Optional.ofNullable(profileContext.getConfiguration().get("controlAllOutputs")); + Optional outputs12 = Optional.ofNullable(profileContext.getConfiguration().get("controlOutputs12")); + + ramp.ifPresent(b -> { + if (b instanceof BigDecimal) { + rampMs = (int) (((BigDecimal) b).doubleValue() * 1000); + } else { + logger.warn("Could not parse 'ramp', unexpected type, should be float: {}", ramp); + } + }); + + allOutputs.ifPresent(b -> { + if (b instanceof Boolean) { + controlAllOutputs = true; + } else { + logger.warn("Could not parse 'controlAllOutputs', unexpected type, should be true/false: {}", b); + } + }); + + outputs12.ifPresent(b -> { + if (b instanceof Boolean) { + controlOutputs12 = true; + } else { + logger.warn("Could not parse 'controlOutputs12', unexpected type, should be true/false: {}", b); + } + }); + } + + @Override + public void onCommandFromItem(Command command) { + if (rampMs != 0 && rampMs != LcnDefs.FIXED_RAMP_MS && controlOutputs12) { + logger.warn("Unsupported 'ramp' setting. Will be forced to 250ms: {}", rampMs); + } + BigDecimal value; + if (command instanceof DecimalType) { + value = ((DecimalType) command).toBigDecimal(); + } else if (command instanceof OnOffType) { + value = ((OnOffType) command) == OnOffType.ON ? BigDecimal.valueOf(100) : BigDecimal.ZERO; + } else { + logger.warn("Unsupported type: {}", command.toFullString()); + return; + } + callback.sendUpdate(new DimmerOutputCommand(value, controlAllOutputs, controlOutputs12, rampMs)); + } + + @Override + public void onStateUpdateFromHandler(State state) { + callback.sendUpdate(state); + } + + @Override + public ProfileTypeUID getProfileTypeUID() { + return UID; + } + + @Override + public void onCommandFromHandler(Command command) { + // nothing + } + + @Override + public void onStateUpdateFromItem(State state) { + // nothing + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnBindingConstants.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnBindingConstants.java new file mode 100644 index 0000000000000..fefe862475a3d --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnBindingConstants.java @@ -0,0 +1,43 @@ +/** + * 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; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link LcnBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnBindingConstants { + /** The scope name of this binding */ + public static final String BINDING_ID = "lcn"; + /** + * Firmware version of the measurement processing since 2013. It has more variables and thresholds and event-based + * variable updates. + */ + public static final int FIRMWARE_2013 = 0x170206; + /** Firmware version which supports controling all 4 outputs simultaneously */ + public static final int FIRMWARE_2014 = 0x180501; + /** List of all Thing Type UIDs */ + public static final ThingTypeUID THING_TYPE_PCK_GATEWAY = new ThingTypeUID(BINDING_ID, "pckGateway"); + public static final ThingTypeUID THING_TYPE_MODULE = new ThingTypeUID(BINDING_ID, "module"); + public static final ThingTypeUID THING_TYPE_GROUP = new ThingTypeUID(BINDING_ID, "group"); + /** Regex for address in PCK protocol */ + public static final String ADDRESS_REGEX = "[:=%]M(?\\d{3})(?\\d{3})"; + /** LCN coding for ACK */ + public static final int CODE_ACK = -1; +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnChannelVariableConfiguration.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnChannelVariableConfiguration.java new file mode 100644 index 0000000000000..6c3713cbcfe21 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnChannelVariableConfiguration.java @@ -0,0 +1,26 @@ +/** + * 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; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link LcnChannelVariableConfiguration} class contains configuration field mapping for Channels of type + * 'variable'. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnChannelVariableConfiguration { + public String unit = "native"; +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnGroupConfiguration.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnGroupConfiguration.java new file mode 100644 index 0000000000000..165966cdc85a0 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnGroupConfiguration.java @@ -0,0 +1,25 @@ +/** + * 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; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link LcnModuleConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnGroupConfiguration extends LcnModuleConfiguration { + public int groupId; +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnGroupHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnGroupHandler.java new file mode 100644 index 0000000000000..f86c4b51a64e3 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnGroupHandler.java @@ -0,0 +1,55 @@ +/** + * 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; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.Thing; +import org.openhab.binding.lcn.internal.common.LcnAddr; +import org.openhab.binding.lcn.internal.common.LcnAddrGrp; +import org.openhab.binding.lcn.internal.common.LcnException; + +/** + * The {@link LcnGroupHandler} is responsible for handling commands, which are + * sent to one of the channels and their destination is an LCN group address. + * + * The module in the attribute moduleAddress is used for state updates of the group as representative for all modules in + * the group. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnGroupHandler extends LcnModuleHandler { + private @Nullable LcnAddrGrp groupAddress; + + public LcnGroupHandler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + LcnGroupConfiguration localConfig = getConfigAs(LcnGroupConfiguration.class); + groupAddress = new LcnAddrGrp(localConfig.segmentId, localConfig.groupId); + + super.initialize(); + } + + @Override + protected LcnAddr getCommandAddress() throws LcnException { + LcnAddrGrp localAddress = groupAddress; + if (localAddress == null) { + throw new LcnException("LCN group address not set"); + } + return localAddress; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnHandlerFactory.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnHandlerFactory.java new file mode 100644 index 0000000000000..67702f1fd6c36 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnHandlerFactory.java @@ -0,0 +1,75 @@ +/** + * 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; + +import static org.openhab.binding.lcn.internal.LcnBindingConstants.*; + +import java.util.Collections; +import java.util.Hashtable; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link LcnHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.lcn", service = ThingHandlerFactory.class) +public class LcnHandlerFactory extends BaseThingHandlerFactory { + private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet( + Stream.of(THING_TYPE_PCK_GATEWAY, THING_TYPE_MODULE, THING_TYPE_GROUP).collect(Collectors.toSet())); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (THING_TYPE_GROUP.equals(thingTypeUID)) { + return new LcnGroupHandler(thing); + } + + if (THING_TYPE_MODULE.equals(thingTypeUID)) { + return new LcnModuleHandler(thing); + } + + if (THING_TYPE_PCK_GATEWAY.equals(thingTypeUID)) { + PckGatewayHandler handler = new PckGatewayHandler((Bridge) thing); + + LcnModuleDiscoveryService discoveryService = new LcnModuleDiscoveryService(handler); + + bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, + new Hashtable<@Nullable String, @Nullable Object>()); + return handler; + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java new file mode 100644 index 0000000000000..82ed4fbe20cc1 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java @@ -0,0 +1,192 @@ +/** + * 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; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.Arrays; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.binding.ThingActions; +import org.eclipse.smarthome.core.thing.binding.ThingActionsScope; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnDefs; +import org.openhab.binding.lcn.internal.common.LcnDefs.KeyTable; +import org.openhab.binding.lcn.internal.common.LcnDefs.SendKeyCommand; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.PckGenerator; +import org.openhab.core.automation.annotation.ActionInput; +import org.openhab.core.automation.annotation.RuleAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handles actions requested to be sent to an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@ThingActionsScope(name = "lcn") +@NonNullByDefault +public class LcnModuleActions implements ThingActions { + private final Logger logger = LoggerFactory.getLogger(LcnModuleActions.class); + private static final int DYN_TEXT_CHUNK_COUNT = 5; + private static final int DYN_TEXT_HEADER_LENGTH = 6; + private static final int DYN_TEXT_CHUNK_LENGTH = 12; + private @Nullable LcnModuleHandler moduleHandler; + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + this.moduleHandler = (LcnModuleHandler) handler; + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return moduleHandler; + } + + @RuleAction(label = "LCN Hit Key", description = "Sends a \"hit key\" command to an LCN module") + public void hitKey( + @ActionInput(name = "table", required = true, type = "java.lang.String", label = "Table", description = "The key table (A-D)") @Nullable String table, + @ActionInput(name = "key", required = true, type = "java.lang.Integer", label = "Key", description = "The key number (1-8)") int key, + @ActionInput(name = "action", required = true, type = "java.lang.String", label = "Action", description = "The action (HIT, MAKE, BREAK)") @Nullable String action) { + try { + if (table == null) { + throw new LcnException("Table is not set"); + } + + if (action == null) { + throw new LcnException("Action is not set"); + } + + KeyTable keyTable; + try { + keyTable = LcnDefs.KeyTable.valueOf(table.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new LcnException("Unknown key table: " + table); + } + + SendKeyCommand sendKeyCommand; + try { + sendKeyCommand = SendKeyCommand.valueOf(action.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new LcnException("Unknown action: " + action); + } + + if (!LcnChannelGroup.KEYLOCKTABLEA.isValidId(key - 1)) { + throw new LcnException("Key number is out of range: " + key); + } + + SendKeyCommand[] cmds = new SendKeyCommand[LcnDefs.KEY_TABLE_COUNT]; + Arrays.fill(cmds, SendKeyCommand.DONTSEND); + boolean[] keys = new boolean[LcnChannelGroup.KEYLOCKTABLEA.getCount()]; + + int keyTableNumber = keyTable.name().charAt(0) - LcnDefs.KeyTable.A.name().charAt(0); + cmds[keyTableNumber] = sendKeyCommand; + keys[key - 1] = true; + + getHandler().sendPck(PckGenerator.sendKeys(cmds, keys)); + } catch (LcnException e) { + logger.warn("Could not execute hit key command: {}", e.getMessage()); + } + } + + @RuleAction(label = "LCN Flicker Output", description = "Let a dimmer output flicker for a given count of flashes") + public void flickerOutput( + @ActionInput(name = "output", type = "java.lang.Integer", required = true, label = "Output", description = "The output number (1-4)") int output, + @ActionInput(name = "depth", type = "java.lang.Integer", label = "Depth", description = "0=25% 1=50% 2=100%") int depth, + @ActionInput(name = "ramp", type = "java.lang.Integer", label = "Ramp", description = "0=2sec 1=1sec 2=0.5sec") int ramp, + @ActionInput(name = "count", type = "java.lang.Integer", label = "Count", description = "Number of flashes (1-15)") int count) { + try { + getHandler().sendPck(PckGenerator.flickerOutput(output - 1, depth, ramp, count)); + } catch (LcnException e) { + logger.warn("Could not send output flicker command: {}", e.getMessage()); + } + } + + @RuleAction(label = "LCN Dynamic Text", description = "Send custom text to an LCN-GTxD display") + public void sendDynamicText( + @ActionInput(name = "row", type = "java.lang.Integer", required = true, label = "Row", description = "Display the text on the LCN-GTxD in the given row number (1-4)") int row, + @ActionInput(name = "text", type = "java.lang.String", label = "Text", description = "The text to display (max. 60 chars/bytes)") @Nullable String textInput) { + try { + String text = textInput; + + if (text == null) { + text = new String(); + } + + // convert String to bytes to split the data every 12 bytes, because a unicode character can take more than + // one byte + ByteBuffer bb = ByteBuffer.wrap(text.getBytes(LcnDefs.LCN_ENCODING)); + + if (bb.capacity() > DYN_TEXT_CHUNK_LENGTH * DYN_TEXT_CHUNK_COUNT) { + logger.warn("Dynamic text truncated. Has {} bytes: '{}'", bb.capacity(), text); + } + + bb.limit(Math.min(DYN_TEXT_CHUNK_LENGTH * DYN_TEXT_CHUNK_COUNT, bb.capacity())); + + int part = 0; + while (bb.hasRemaining()) { + byte[] chunk = new byte[DYN_TEXT_CHUNK_LENGTH]; + bb.get(chunk, 0, Math.min(bb.remaining(), DYN_TEXT_CHUNK_LENGTH)); + + ByteBuffer command = ByteBuffer.allocate(DYN_TEXT_HEADER_LENGTH + DYN_TEXT_CHUNK_LENGTH); + command.put(PckGenerator.dynTextHeader(row - 1, part++).getBytes(LcnDefs.LCN_ENCODING)); + command.put(chunk); + + getHandler().sendPck(command); + } + } catch (UnsupportedEncodingException | IllegalArgumentException | LcnException e) { + logger.warn("Could not send dynamic text: {}", e.getMessage()); + } + } + + /** Static alias to support the old DSL rules engine and make the action available there. */ + public static void hitKey(@Nullable ThingActions actions, @Nullable String table, int key, + @Nullable String action) { + if (actions instanceof LcnModuleActions) { + ((LcnModuleActions) actions).hitKey(table, key, action); + } else { + throw new IllegalArgumentException( + "Instance is not a " + LcnModuleActions.class.getSimpleName() + " class."); + } + } + + public static void flickerOutput(@Nullable ThingActions actions, int output, int depth, int ramp, int count) { + if (actions instanceof LcnModuleActions) { + ((LcnModuleActions) actions).flickerOutput(output, depth, ramp, count); + } else { + throw new IllegalArgumentException( + "Instance is not a " + LcnModuleActions.class.getSimpleName() + " class."); + } + } + + public static void sendDynamicText(@Nullable ThingActions actions, int row, @Nullable String text) { + if (actions instanceof LcnModuleActions) { + ((LcnModuleActions) actions).sendDynamicText(row, text); + } else { + throw new IllegalArgumentException( + "Instance is not a " + LcnModuleActions.class.getSimpleName() + " class."); + } + } + + private LcnModuleHandler getHandler() throws LcnException { + LcnModuleHandler localModuleHandler = moduleHandler; + if (localModuleHandler != null) { + return localModuleHandler; + } else { + throw new LcnException("Handler not set"); + } + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleConfiguration.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleConfiguration.java new file mode 100644 index 0000000000000..aa845f77027ab --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleConfiguration.java @@ -0,0 +1,26 @@ +/** + * 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; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link LcnModuleConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleConfiguration { + public int segmentId; + public int moduleId; +} 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 new file mode 100644 index 0000000000000..de9a8d4b1fffa --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleDiscoveryService.java @@ -0,0 +1,199 @@ +/** + * 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; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.openhab.binding.lcn.internal.common.LcnAddrMod; +import org.openhab.binding.lcn.internal.common.NullScheduledFuture; +import org.openhab.binding.lcn.internal.connection.Connection; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaAckSubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaFirmwareSubHandler; + +/** + * Scans all LCN segments for LCN modules. + * + * Scan approach: + * 1. Send Leerkomando to the broadcast address with request for Ack set + * 2. For every received Ack, send to the module: + * - serial number request (SN) + * - module's name first part request (NM1) + * - module's name second part request (NM2) + * 3. When all three messages have been received, fire thingDiscovered() + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class LcnModuleDiscoveryService extends AbstractDiscoveryService { + private static final Pattern NAME_PATTERN = Pattern + .compile("=M(?\\d{3})(?\\d{3}).N(?[1-2]{1})(?.*)"); + private static final int MODULE_NAME_PART_COUNT = 2; + private static final int DISCOVERY_TIMEOUT_SEC = 90; + private static final int ACK_TIMEOUT_MS = 1000; + private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet( + Stream.of(LcnBindingConstants.THING_TYPE_PCK_GATEWAY, LcnBindingConstants.THING_TYPE_MODULE) + .collect(Collectors.toSet())); + private PckGatewayHandler bridgeHandler; + private Map> moduleNames = new HashMap<>(); + private Map discoveryResultBuilders = new HashMap<>(); + private List successfullyDiscovered = new LinkedList<>(); + private LinkedList<@Nullable LcnAddrMod> serialNumberRequestQueue = new LinkedList<>(); + private LinkedList<@Nullable LcnAddrMod> moduleNameRequestQueue = new LinkedList<>(); + private volatile ScheduledFuture queueProcessor = NullScheduledFuture.getInstance(); + private ScheduledFuture builderTask = NullScheduledFuture.getInstance(); + + public LcnModuleDiscoveryService(PckGatewayHandler bridgeHandler) throws IllegalArgumentException { + super(SUPPORTED_THING_TYPES_UIDS, DISCOVERY_TIMEOUT_SEC, false); + this.bridgeHandler = bridgeHandler; + } + + @SuppressWarnings({ "unused", "null" }) + @Override + protected void startScan() { + synchronized (this) { + if (bridgeHandler.getConnection() == null) { + builderTask.cancel(true); + } + + bridgeHandler.registerPckListener(data -> { + Matcher matcher; + + if ((matcher = LcnModuleMetaAckSubHandler.PATTERN_POS.matcher(data)).matches() + || (matcher = LcnModuleMetaFirmwareSubHandler.PATTERN.matcher(data)).matches() + || (matcher = NAME_PATTERN.matcher(data)).matches()) { + synchronized (LcnModuleDiscoveryService.this) { + Connection connection = bridgeHandler.getConnection(); + + if (connection == null) { + return; + } + + LcnAddrMod addr = new LcnAddrMod( + bridgeHandler.toLogicalSegmentId(Integer.parseInt(matcher.group("segId"))), + Integer.parseInt(matcher.group("modId"))); + + if (matcher.pattern() == LcnModuleMetaAckSubHandler.PATTERN_POS) { + // the module could send an Ack with a response to another command. So, ignore the Ack, when + // we received our data already. + if (!discoveryResultBuilders.containsKey(addr)) { + serialNumberRequestQueue.add(addr); + rescheduleQueueProcessor(); // delay request of serial until all modules finished ACKing + } + + if (!moduleNames.containsKey(addr) + || moduleNames.get(addr).size() != MODULE_NAME_PART_COUNT) { + moduleNameRequestQueue.add(addr); + rescheduleQueueProcessor(); // delay request of names until all modules finished ACKing + } + } else if (matcher.pattern() == LcnModuleMetaFirmwareSubHandler.PATTERN) { + Map properties = new HashMap<>(5); + properties.put("segmentId", addr.getSegmentId()); + properties.put("moduleId", addr.getModuleId()); + + ThingUID bridgeUid = bridgeHandler.getThing().getUID(); + String thingId = matcher.group("sn"); + ThingUID thingUid = new ThingUID(LcnBindingConstants.THING_TYPE_MODULE, bridgeUid, thingId); + + DiscoveryResultBuilder discoveryResult = DiscoveryResultBuilder.create(thingUid) + .withProperties(properties).withBridge(bridgeUid); + + discoveryResultBuilders.put(addr, discoveryResult); + } else if (matcher.pattern() == NAME_PATTERN) { + final int part = Integer.parseInt(matcher.group("part")) - 1; + final String name = matcher.group("name"); + + moduleNames.compute(addr, (partNumber, namePart) -> { + Map namePartMapping = namePart; + if (namePartMapping == null) { + namePartMapping = new HashMap<>(); + } + + namePartMapping.put(part, name); + + return namePartMapping; + }); + } + } + } + }); + + builderTask = scheduler.scheduleWithFixedDelay(() -> { + synchronized (LcnModuleDiscoveryService.this) { + discoveryResultBuilders.entrySet().stream().filter(e -> moduleNames.containsKey(e.getKey())) + .filter(e -> moduleNames.get(e.getKey()).size() == MODULE_NAME_PART_COUNT) + .filter(e -> !successfullyDiscovered.contains(e.getKey())).forEach(e -> { + // collect() to remove while iterating + StringBuilder thingName = new StringBuilder(); + if (e.getKey().getSegmentId() != 0) { + thingName.append("Segment " + e.getKey().getSegmentId() + " "); + } + + thingName.append("Module " + e.getKey().getModuleId() + ": "); + thingName.append(moduleNames.get(e.getKey()).get(0)); + thingName.append(moduleNames.get(e.getKey()).get(1)); + + thingDiscovered(e.getValue().withLabel(thingName.toString()).build()); + successfullyDiscovered.add(e.getKey()); + }); + } + }, 500, 500, TimeUnit.MILLISECONDS); + + bridgeHandler.sendModuleDiscoveryCommand(); + } + } + + private synchronized void rescheduleQueueProcessor() { + // delay serial number and module name requests to not clog the bus + queueProcessor.cancel(true); + queueProcessor = scheduler.scheduleWithFixedDelay(() -> { + synchronized (serialNumberRequestQueue) { + LcnAddrMod serial = serialNumberRequestQueue.poll(); + if (serial != null) { + bridgeHandler.sendSerialNumberRequest(serial); + } + } + + synchronized (moduleNameRequestQueue) { + LcnAddrMod name = moduleNameRequestQueue.poll(); + if (name != null) { + bridgeHandler.sendModuleNameRequest(name); + } + } + }, ACK_TIMEOUT_MS, ACK_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + @Override + public synchronized void stopScan() { + builderTask.cancel(true); + queueProcessor.cancel(true); + bridgeHandler.removeAllPckListeners(); + super.stopScan(); + } +} 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 new file mode 100644 index 0000000000000..592c5e4a0f79e --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleHandler.java @@ -0,0 +1,421 @@ +/** + * 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; + +import java.lang.reflect.InvocationTargetException; +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.library.types.StopMoveType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.lcn.internal.common.DimmerOutputCommand; +import org.openhab.binding.lcn.internal.common.LcnAddr; +import org.openhab.binding.lcn.internal.common.LcnAddrMod; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.connection.Connection; +import org.openhab.binding.lcn.internal.connection.ModInfo; +import org.openhab.binding.lcn.internal.converter.AbstractVariableValueConverter; +import org.openhab.binding.lcn.internal.converter.AngleConverter; +import org.openhab.binding.lcn.internal.converter.Co2Converter; +import org.openhab.binding.lcn.internal.converter.CurrentConverter; +import org.openhab.binding.lcn.internal.converter.EnergyConverter; +import org.openhab.binding.lcn.internal.converter.IdentityConverter; +import org.openhab.binding.lcn.internal.converter.LightConverter; +import org.openhab.binding.lcn.internal.converter.PowerConverter; +import org.openhab.binding.lcn.internal.converter.TemperatureConverter; +import org.openhab.binding.lcn.internal.converter.VoltageConverter; +import org.openhab.binding.lcn.internal.converter.WindspeedConverter; +import org.openhab.binding.lcn.internal.subhandler.AbstractLcnModuleSubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaAckSubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaFirmwareSubHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link LcnModuleHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleHandler extends BaseThingHandler { + private final Logger logger = LoggerFactory.getLogger(LcnModuleHandler.class); + private @Nullable LcnAddrMod moduleAddress; + private Map subHandlers; + private List metadataSubHandlers; + private Map converters; + + public LcnModuleHandler(Thing thing) { + super(thing); + + subHandlers = Collections.synchronizedMap(new HashMap<>()); + metadataSubHandlers = Collections.synchronizedList(new LinkedList<>()); + converters = Collections.synchronizedMap(new HashMap<>()); + } + + @Override + public void initialize() { + updateStatus(ThingStatus.UNKNOWN); + LcnModuleConfiguration localConfig = getConfigAs(LcnModuleConfiguration.class); + LcnAddrMod localModuleAddress = moduleAddress = new LcnAddrMod(localConfig.segmentId, localConfig.moduleId); + + try { + // create sub handlers + ModInfo info = getPckGatewayHandler().getModInfo(localModuleAddress); + for (LcnChannelGroup type : LcnChannelGroup.values()) { + + AbstractLcnModuleSubHandler newHandler = type.getSubHandlerClass() + .getDeclaredConstructor(LcnModuleHandler.class, ModInfo.class).newInstance(this, info); + + subHandlers.put(type, newHandler); + } + + // meta sub handlers, which are not assigned to a channel group + // initialize() can be invoked multiple times on the same handler, when changing a Channel's config + metadataSubHandlers.clear(); + metadataSubHandlers.add(new LcnModuleMetaAckSubHandler(this, info)); + metadataSubHandlers.add(new LcnModuleMetaFirmwareSubHandler(this, info)); + + // initialize variable value converters + for (Channel channel : thing.getChannels()) { + Object unitObject = channel.getConfiguration().get("unit"); + Object parameterObject = channel.getConfiguration().get("parameter"); + + if (unitObject instanceof String) { + switch ((String) unitObject) { + case "temperature": + converters.put(channel.getUID(), new TemperatureConverter()); + break; + case "light": + converters.put(channel.getUID(), new LightConverter()); + break; + case "co2": + converters.put(channel.getUID(), new Co2Converter()); + break; + case "power": + converters.put(channel.getUID(), new PowerConverter(parameterObject)); + break; + case "energy": + converters.put(channel.getUID(), new EnergyConverter(parameterObject)); + break; + case "current": + converters.put(channel.getUID(), new CurrentConverter()); + break; + case "voltage": + converters.put(channel.getUID(), new VoltageConverter()); + break; + case "angle": + converters.put(channel.getUID(), new AngleConverter()); + break; + case "windspeed": + converters.put(channel.getUID(), new WindspeedConverter()); + break; + } + } + } + + // module is assumed as online, when the corresponding Bridge (PckGatewayHandler) is online. + updateStatus(ThingStatus.ONLINE); + } catch (LcnException | InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | NoSuchMethodException | SecurityException e) { + logger.warn("Failed to initialize handler: {}: {}: {}", localModuleAddress, e.getClass().getSimpleName(), + e.getMessage()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); + } + } + + @Override + public void handleCommand(ChannelUID channelUid, Command command) { + // this method can be invoked, when initialize() has not finished, yet. + if (thing.getStatus() != ThingStatus.ONLINE) { + return; + } + + try { + // refresh command can be received, when Bridge is initializing, so synchronize + synchronized (getPckGatewayHandler()) { + String groupId = channelUid.getGroupId(); + + if (!channelUid.isInGroup()) { + return; + } + + if (groupId == null) { + throw new LcnException("Group ID is null"); + } + + LcnChannelGroup channelGroup = LcnChannelGroup.valueOf(groupId.toUpperCase()); + AbstractLcnModuleSubHandler subHandler = subHandlers.get(channelGroup); + + if (subHandler == null) { + throw new LcnException("Sub Handler not found for: " + channelGroup); + } + + Optional number = channelUidToChannelNumber(channelUid, channelGroup); + + if (command instanceof RefreshType) { + number.ifPresent(n -> subHandler.handleRefresh(channelGroup, n)); + subHandler.handleRefresh(channelUid.getIdWithoutGroup()); + } else if (command instanceof OnOffType) { + subHandler.handleCommandOnOff(castCommand(command), channelGroup, number.get()); + } else if (command instanceof DimmerOutputCommand) { + subHandler.handleCommandDimmerOutput(castCommand(command), number.get()); + } else if (command instanceof PercentType && number.isPresent()) { + subHandler.handleCommandPercent(castCommand(command), channelGroup, number.get()); + } else if (command instanceof HSBType) { + subHandler.handleCommandHsb(castCommand(command), channelUid.getIdWithoutGroup()); + } else if (command instanceof PercentType) { + subHandler.handleCommandPercent(castCommand(command), channelGroup, channelUid.getIdWithoutGroup()); + } else if (command instanceof StringType) { + subHandler.handleCommandString(castCommand(command), number.get()); + } else if (command instanceof DecimalType) { + DecimalType decimalType = castCommand(command); + DecimalType nativeValue = getConverter(channelUid).onCommandFromItem(decimalType.doubleValue()); + subHandler.handleCommandDecimal(nativeValue, channelGroup, number.get()); + } else if (command instanceof QuantityType) { + QuantityType quantityType = castCommand(command); + DecimalType nativeValue = getConverter(channelUid).onCommandFromItem(quantityType); + subHandler.handleCommandDecimal(nativeValue, channelGroup, number.get()); + } else if (command instanceof UpDownType) { + subHandler.handleCommandUpDown(castCommand(command), channelGroup, number.get()); + } else if (command instanceof StopMoveType) { + subHandler.handleCommandStopMove(castCommand(command), channelGroup, number.get()); + } else { + throw new LcnException("Unsupported command type"); + } + } + } catch (IllegalArgumentException | NoSuchElementException | LcnException e) { + logger.warn("{}: Failed to handle command {}: {}", channelUid, command.getClass().getSimpleName(), + e.getMessage()); + } + } + + @NonNullByDefault({}) // getOrDefault() + private AbstractVariableValueConverter getConverter(ChannelUID channelUid) throws LcnException { + return converters.getOrDefault(channelUid, IdentityConverter.getInstance()); + } + + /** + * Convenience method to cast a command. + * + * @param the concrete type to be casted to + * @param command the command to be casted + * @return the concrete command + * @throws LcnException when the command cannot be casted + */ + @SuppressWarnings("unchecked") + private T castCommand(Command command) throws LcnException { + try { + return (T) command; + } catch (ClassCastException e) { + throw new LcnException("Unexpected command type"); + } + } + + /** + * Invoked when a PCK messages arrives from the PCK gateway + * + * @param pck the message without line termination + */ + @SuppressWarnings("null") + public void handleStatusMessage(String pck) { + // this method can be invoked, when initialize() has not finished, yet. + if (thing.getStatus() != ThingStatus.ONLINE) { + return; + } + + synchronized (subHandlers) { + for (AbstractLcnModuleSubHandler handler : subHandlers.values()) { + if (handler.tryParse(pck)) { + break; + } + } + } + + synchronized (metadataSubHandlers) { + metadataSubHandlers.forEach(h -> h.tryParse(pck)); + } + } + + private Optional channelUidToChannelNumber(ChannelUID channelUid, LcnChannelGroup channelGroup) + throws LcnException { + try { + int number = Integer.parseInt(channelUid.getIdWithoutGroup()) - 1; + + if (!channelGroup.isValidId(number)) { + throw new LcnException("Out of range: " + number); + } + return Optional.of(number); + } catch (NumberFormatException e) { + return Optional.empty(); + } + } + + private PckGatewayHandler getPckGatewayHandler() throws LcnException { + Bridge bridge = getBridge(); + if (bridge == null) { + throw new LcnException("No LCN-PCK gateway configured for this module"); + } + + PckGatewayHandler handler = (PckGatewayHandler) bridge.getHandler(); + if (handler == null) { + throw new LcnException("Could not get PckGatewayHandler"); + } + return handler; + } + + /** + * Queues a PCK string for sending. + * + * @param command without the address part + * @throws LcnException when the module address is unknown + */ + public void sendPck(String command) throws LcnException { + getPckGatewayHandler().queue(getCommandAddress(), true, command); + } + + /** + * Queues a PCK byte buffer for sending. + * + * @param command without the address part + * @throws LcnException when the module address is unknown + */ + public void sendPck(ByteBuffer command) throws LcnException { + getPckGatewayHandler().queue(getCommandAddress(), true, command); + } + + /** + * Gets the address, which shall be used when sending commands into the LCN bus. This can also be a group address. + * + * @return the address to send to + * @throws LcnException when the address is unknown + */ + protected LcnAddr getCommandAddress() throws LcnException, LcnException { + LcnAddr localAddress = moduleAddress; + if (localAddress == null) { + throw new LcnException("Module address not set"); + } + return localAddress; + } + + /** + * Invoked when an update for this LCN module should be fired to openHAB. + * + * @param channelGroup the Channel to update + * @param channelId the ID within the Channel to update + * @param state the new state + */ + public void updateChannel(LcnChannelGroup channelGroup, String channelId, State state) { + ChannelUID channelUid = createChannelUid(channelGroup, channelId); + AbstractVariableValueConverter converter = converters.get(channelUid); + + State convertedState = state; + if (converter != null) { + convertedState = converter.onStateUpdateFromHandler(state); + } + updateState(channelUid, convertedState); + } + + /** + * Invoked when an trigger for this LCN module should be fired to openHAB. + * + * @param channelGroup the Channel to update + * @param channelId the ID within the Channel to update + * @param event the event used to trigger + */ + public void triggerChannel(LcnChannelGroup channelGroup, String channelId, String event) { + triggerChannel(createChannelUid(channelGroup, channelId), event); + } + + private ChannelUID createChannelUid(LcnChannelGroup channelGroup, String channelId) { + return new ChannelUID(thing.getUID(), channelGroup.name().toLowerCase() + "#" + channelId); + } + + /** + * Checks the LCN module address against the own. + * + * @param physicalSegmentId which is 0 if it is the local segment + * @param moduleId + * @return true, if the given address matches the own address + */ + public boolean isMyAddress(String physicalSegmentId, String moduleId) { + try { + return new LcnAddrMod(getPckGatewayHandler().toLogicalSegmentId(Integer.parseInt(physicalSegmentId)), + Integer.parseInt(moduleId)).equals(getStatusMessageAddress()); + } catch (LcnException e) { + return false; + } + } + + @Override + public Collection> getServices() { + return Collections.singleton(LcnModuleActions.class); + } + + /** + * Invoked when an Ack from this module has been received. + */ + public void onAckRceived() { + try { + Connection connection = getPckGatewayHandler().getConnection(); + LcnAddrMod localModuleAddress = moduleAddress; + if (connection != null && localModuleAddress != null) { + getPckGatewayHandler().getModInfo(localModuleAddress).onAck(LcnBindingConstants.CODE_ACK, connection, + getPckGatewayHandler().getTimeoutMs(), System.nanoTime()); + } + } catch (LcnException e) { + logger.warn("Connection or module address not set"); + } + } + + /** + * Gets the address the handler shall react to, when a status message from this address is processed. + * + * @return the address for status messages + */ + public LcnAddrMod getStatusMessageAddress() { + LcnAddrMod localmoduleAddress = moduleAddress; + if (localmoduleAddress != null) { + return localmoduleAddress; + } else { + return new LcnAddrMod(0, 0); + } + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnProfileFactory.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnProfileFactory.java new file mode 100644 index 0000000000000..a63c73e0c74cf --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnProfileFactory.java @@ -0,0 +1,71 @@ +/** + * 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; + +import java.util.Collection; +import java.util.Collections; +import java.util.Locale; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.CoreItemFactory; +import org.eclipse.smarthome.core.thing.profiles.Profile; +import org.eclipse.smarthome.core.thing.profiles.ProfileCallback; +import org.eclipse.smarthome.core.thing.profiles.ProfileContext; +import org.eclipse.smarthome.core.thing.profiles.ProfileFactory; +import org.eclipse.smarthome.core.thing.profiles.ProfileType; +import org.eclipse.smarthome.core.thing.profiles.ProfileTypeBuilder; +import org.eclipse.smarthome.core.thing.profiles.ProfileTypeProvider; +import org.eclipse.smarthome.core.thing.profiles.ProfileTypeUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Factory to create Profile instances. Also provides the available ProfileTypes and gives advise which profile to use + * by a given link. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +@Component(service = { ProfileFactory.class, ProfileTypeProvider.class }) +public class LcnProfileFactory implements ProfileFactory, ProfileTypeProvider { + private final Logger logger = LoggerFactory.getLogger(LcnProfileFactory.class); + + @Override + public Collection getSupportedProfileTypeUIDs() { + return Collections.singleton(DimmerOutputProfile.UID); + } + + @Override + public Collection getProfileTypes(@Nullable Locale locale) { + return Collections.singleton(ProfileTypeBuilder.newState(DimmerOutputProfile.UID, "Dimmer Output (%)") + .withSupportedItemTypes(CoreItemFactory.DIMMER, CoreItemFactory.COLOR) + .withSupportedChannelTypeUIDs( + new ChannelTypeUID(LcnBindingConstants.BINDING_ID, LcnChannelGroup.OUTPUT.name().toLowerCase())) + .build()); + } + + @Override + public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback, + ProfileContext profileContext) { + if (profileTypeUID.equals(DimmerOutputProfile.UID)) { + return new DimmerOutputProfile(callback, profileContext); + } else { + logger.warn("Could not create {}", profileTypeUID); + return null; + } + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayConfiguration.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayConfiguration.java new file mode 100644 index 0000000000000..593396a463c2a --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayConfiguration.java @@ -0,0 +1,54 @@ +/** + * 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; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link PckGatewayConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class PckGatewayConfiguration { + private @NonNullByDefault({}) String hostname; + private int port; + private @NonNullByDefault({}) String username; + private @NonNullByDefault({}) String password; + private @NonNullByDefault({}) String mode; + private @NonNullByDefault({}) int timeoutMs; + + public String getHostname() { + return hostname; + } + + public int getPort() { + return port; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getMode() { + return mode; + } + + public int getTimeoutMs() { + return timeoutMs; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java new file mode 100644 index 0000000000000..5d947ec6af1f0 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java @@ -0,0 +1,297 @@ +/** + * 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; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Optional; +import java.util.function.Consumer; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.lcn.internal.common.LcnAddr; +import org.openhab.binding.lcn.internal.common.LcnAddrMod; +import org.openhab.binding.lcn.internal.common.LcnDefs; +import org.openhab.binding.lcn.internal.common.LcnDefs.OutputPortDimMode; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.connection.Connection; +import org.openhab.binding.lcn.internal.connection.ConnectionCallback; +import org.openhab.binding.lcn.internal.connection.ConnectionSettings; +import org.openhab.binding.lcn.internal.connection.ModInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link PckGatewayHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class PckGatewayHandler extends BaseBridgeHandler { + private final Logger logger = LoggerFactory.getLogger(PckGatewayHandler.class); + private @Nullable Connection connection; + private Optional> pckListener = Optional.empty(); + + public PckGatewayHandler(Bridge bridge) { + super(bridge); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + // nothing + } + + @Override + public synchronized void initialize() { + PckGatewayConfiguration config = getConfigAs(PckGatewayConfiguration.class); + + String errorMessage = "Could not connect to LCN-PCHK/PKE: " + config.getHostname() + ": "; + + try { + OutputPortDimMode dimMode; + if (LcnDefs.OutputPortDimMode.NATIVE50.name().equalsIgnoreCase(config.getMode())) { + dimMode = LcnDefs.OutputPortDimMode.NATIVE50; + } else if (LcnDefs.OutputPortDimMode.NATIVE200.name().equalsIgnoreCase(config.getMode())) { + dimMode = LcnDefs.OutputPortDimMode.NATIVE200; + } else { + throw new LcnException("DimMode " + config.getMode() + " is not supported"); + } + + ConnectionSettings settings = new ConnectionSettings("0", config.getHostname(), config.getPort(), + config.getUsername(), config.getPassword(), dimMode, LcnDefs.OutputPortStatusMode.PERCENT, + config.getTimeoutMs()); + + connection = new Connection(settings, scheduler, new ConnectionCallback() { + @Override + public void onOnline() { + updateStatus(ThingStatus.ONLINE); + } + + @Override + public void onOffline(@Nullable String errorMessage) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorMessage + "."); + } + + @Override + public void onPckMessageReceived(String message) { + pckListener.ifPresent(l -> l.accept(message)); + getThing().getThings().stream().filter(t -> t.getStatus() == ThingStatus.ONLINE).map(t -> { + LcnModuleHandler handler = (LcnModuleHandler) t.getHandler(); + if (handler == null) { + logger.warn("Failed to process PCK message: Handler not set"); + } + return handler; + }).filter(h -> h != null).forEach(h -> h.handleStatusMessage(message)); + } + }); + + updateStatus(ThingStatus.UNKNOWN); + } catch (IOException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorMessage + e.getMessage()); + } catch (LcnException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMessage + e.getMessage()); + } + } + + @Override + public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) { + if (childThing.getThingTypeUID().equals(LcnBindingConstants.THING_TYPE_MODULE) + || childThing.getThingTypeUID().equals(LcnBindingConstants.THING_TYPE_GROUP)) { + try { + LcnAddr addr = getLcnAddrFromThing(childThing); + Connection localConnection = connection; + if (localConnection != null) { + localConnection.removeLcnModule(addr); + } + } catch (LcnException e) { + logger.warn("Failed to read configuration: {}", e.getMessage()); + } + } + } + + private LcnAddr getLcnAddrFromThing(Thing childThing) throws LcnException { + LcnModuleHandler lcnModuleHandler = (LcnModuleHandler) childThing.getHandler(); + if (lcnModuleHandler != null) { + return lcnModuleHandler.getCommandAddress(); + } else { + throw new LcnException("Could not get module handler"); + } + } + + /** + * Enqueues a PCK command to be sent to an LCN module. + * + * @param addr the modules address + * @param wantsAck true, if the module shall send an ACK upon successful processing + * @param pck the command to send + */ + public void queue(LcnAddr addr, boolean wantsAck, String pck) { + Connection localConnection = connection; + if (localConnection != null) { + localConnection.queue(addr, wantsAck, pck); + } else { + logger.warn("Dropped PCK command: {}", pck); + } + } + + /** + * Enqueues a PCK command to be sent to an LCN module. + * + * @param addr the modules address + * @param wantsAck true, if the module shall send an ACK upon successful processing + * @param pck the command to send + */ + public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer pck) { + Connection localConnection = connection; + if (localConnection != null) { + localConnection.queue(addr, wantsAck, pck); + } else { + logger.warn("Dropped PCK command of length: {}", pck.capacity()); + } + } + + /** + * Sends a broadcast message to all LCN modules. All LCN modules are requested to answer with an Ack. + */ + void sendModuleDiscoveryCommand() { + Connection localConnection = connection; + if (localConnection != null) { + localConnection.sendModuleDiscoveryCommand(); + } + } + + /** + * Send a request to an LCN module to respond with its serial number and firmware version. + * + * @param addr the module's address + */ + void sendSerialNumberRequest(LcnAddrMod addr) { + Connection localConnection = connection; + if (localConnection != null) { + localConnection.sendSerialNumberRequest(addr); + } + } + + /** + * Send a request to an LCN module to respond with its configured name. + * + * @param addr the module's address + */ + void sendModuleNameRequest(LcnAddrMod addr) { + Connection localConnection = connection; + if (localConnection != null) { + localConnection.sendModuleNameRequest(addr); + } + } + + /** + * Returns the ModInfo to a given module. Will be created if it doesn't exist,yet. + * + * @param addr the module's address + * @return the ModInfo + * @throws LcnException when this handler is not initialized, yet + */ + ModInfo getModInfo(LcnAddrMod addr) throws LcnException { + Connection localConnection = connection; + if (localConnection != null) { + return localConnection.updateModuleData(addr); + } else { + throw new LcnException("Connection is null"); + } + } + + /** + * Registers a listener to receive all PCK messages from this PCK gateway. + * + * @param listener the listener to add + */ + void registerPckListener(Consumer listener) { + this.pckListener = Optional.of(listener); + } + + /** + * Removes all listeners for PCK messages from this PCK gateway. + */ + void removeAllPckListeners() { + this.pckListener = Optional.empty(); + } + + /** + * Gets the Connection for this handler. + * + * @return the Connection + */ + @Nullable + public Connection getConnection() { + return connection; + } + + /** + * Gets the local segment ID. When no segments are used, the value is 0. + * + * @return the local segment ID + */ + public int getLocalSegmentId() { + Connection localConnection = connection; + if (localConnection != null) { + return localConnection.getLocalSegId(); + } else { + return 0; + } + } + + /** + * Translates the given physical segment ID (0 or 4 if local segment) to the logical segment ID (local segment ID). + * + * @param physicalSegmentId the segment ID to convert + * @return the converted segment ID + */ + public int toLogicalSegmentId(int physicalSegmentId) { + int localSegmentId = getLocalSegmentId(); + if ((physicalSegmentId == 0 || physicalSegmentId == 4) && localSegmentId != -1) { + // PCK message came from local segment + // physicalSegmentId == 0 => Module is programmed to send status messages to local segment only + // physicalSegmentId == 4 => Module is programmed to send status messages globally (to all segments) + // or segment coupler scan did not finish, yet (-1). Assume local segment, then. + return localSegmentId; + } else { + return physicalSegmentId; + } + } + + @Override + public void dispose() { + Connection localConnection = connection; + if (localConnection != null) { + localConnection.shutdown(); + } + } + + /** + * Gets the configured connection timeout for the PCK gateway. + * + * @return the timeout in ms + */ + public long getTimeoutMs() { + return getConfigAs(PckGatewayConfiguration.class).getTimeoutMs(); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/DimmerOutputCommand.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/DimmerOutputCommand.java new file mode 100644 index 0000000000000..cd2a74b79f319 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/DimmerOutputCommand.java @@ -0,0 +1,65 @@ +/** + * 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.common; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.PercentType; + +/** + * Holds the information to control dimmer outputs of an LCN module. Used when the user configured an "output" profile. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class DimmerOutputCommand extends PercentType { + private static final long serialVersionUID = 8147502412107723798L; + private boolean controlAllOutputs; + private boolean controlOutputs12; + private int rampMs; + + public DimmerOutputCommand(BigDecimal value, boolean controlAllOutputs, boolean controlOutputs12, int rampMs) { + super(value); + this.controlAllOutputs = controlAllOutputs; + this.controlOutputs12 = controlOutputs12; + this.rampMs = rampMs; + } + + /** + * Gets the ramp. + * + * @return ramp in milliseconds + */ + public int getRampMs() { + return rampMs; + } + + /** + * Returns if all dimmer outputs shall be controlled. + * + * @return true, if all dimmer outputs shall be controlled + */ + public boolean isControlAllOutputs() { + return controlAllOutputs; + } + + /** + * Returns if dimmer outputs 1+2 shall be controlled. + * + * @return true, if dimmer outputs 1+2 shall be controlled + */ + public boolean isControlOutputs12() { + return controlOutputs12; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnAddr.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnAddr.java new file mode 100644 index 0000000000000..c5630ecc69d9c --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnAddr.java @@ -0,0 +1,79 @@ +/** + * 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.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Represents an LCN address (module or group). + * + * @author Tobias Jüttner - Initial Contribution + */ +@NonNullByDefault +public abstract class LcnAddr { + /** + * The logical segment ID. When no segments are used, the ID is always 0. When segments are used and the module is + * in the local segment, the ID is the local's segment ID. + */ + protected final int segmentId; + + /** + * Constructs an address with a (logical) segment id. + * + * @param segId the segment id + */ + public LcnAddr(int segId) { + this.segmentId = segId; + } + + /** + * Gets the (logical) segment id. + * + * @return the segment id + */ + public int getSegmentId() { + return this.segmentId; + } + + /** + * Gets the physical segment id ("local" segment replaced with 0). + * Can be used to send data into the LCN bus. + * + * @param localSegegmentId the segment id of the local segment (managed by {@link Connection}) + * @return the physical segment id + */ + public int getPhysicalSegmentId(int localSegegmentId) { + return this.segmentId == localSegegmentId ? 0 : this.segmentId; + } + + /** + * Checks the address against the LCN specification for valid addresses. + * + * @return true if address is valid + */ + public abstract boolean isValid(); + + /** + * Queries the concrete address type. + * + * @return true if address is a group address (module address otherwise) + */ + public abstract boolean isGroup(); + + /** + * Gets the address' module or group id (discarding the concrete type). + * + * @return the module or group id + */ + public abstract int getId(); +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnAddrGrp.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnAddrGrp.java new file mode 100644 index 0000000000000..0873dfd0edd85 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnAddrGrp.java @@ -0,0 +1,102 @@ +/** + * 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.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Represents an LCN group address. + * Can be used as a key in maps. + * Hash codes are guaranteed to be unique as long as {@link #isValid()} is true. + * + * @author Tobias Jüttner - Initial Contribution + */ +@NonNullByDefault +public class LcnAddrGrp extends LcnAddr implements Comparable { + private final Logger logger = LoggerFactory.getLogger(LcnAddrGrp.class); + private final int groupId; + + /** + * Constructs a group address with (logical) segment id and group id. + * + * @param segId the segment id + * @param grpId the group id + */ + public LcnAddrGrp(int segId, int grpId) { + super(segId); + this.groupId = grpId; + } + + /** + * Gets the group id. + * + * @return the group id + */ + public int getGroupId() { + return this.groupId; + } + + @Override + public boolean isValid() { + // segId: + // 0 = Local, 1..2 = Not allowed (but "seen in the wild") + // 3 = Broadcast, 4 = Status messages, 5..127, 128 = Segment-bus disabled (valid value) + // grpId: + // 3 = Broadcast, 4 = Status messages, 5..254 + return this.segmentId >= 0 && this.segmentId <= 128 && this.groupId >= 3 && this.groupId <= 254; + } + + @Override + public boolean isGroup() { + return true; + } + + @Override + public int getId() { + return this.groupId; + } + + @Override + public int hashCode() { + // Reversing the bits helps to generate better balanced trees as ids tend to be "user-sorted" + try { + if (this.isValid()) { + return ReverseNumber.reverseUInt8(this.groupId) << 8 + ReverseNumber.reverseUInt8(this.segmentId); + } + } catch (LcnException ex) { + logger.warn("Could not calculate hash code"); + } + return -1; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof LcnAddrGrp)) { + return false; + } + return this.segmentId == ((LcnAddrGrp) obj).segmentId && this.groupId == ((LcnAddrGrp) obj).groupId; + } + + @Override + public int compareTo(LcnAddrMod other) { + return this.hashCode() - other.hashCode(); + } + + @Override + public String toString() { + return this.isValid() ? String.format("S%03dG%03d", this.segmentId, this.groupId) : "Invalid"; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnAddrMod.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnAddrMod.java new file mode 100644 index 0000000000000..81fe842230c38 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnAddrMod.java @@ -0,0 +1,102 @@ +/** + * 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.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Represents an LCN module address. + * Can be used as a key in maps. + * Hash codes are guaranteed to be unique as long as {@link #isValid()} is true. + * + * @author Tobias Jüttner - Initial Contribution + */ +@NonNullByDefault +public class LcnAddrMod extends LcnAddr implements Comparable { + private final Logger logger = LoggerFactory.getLogger(LcnAddrMod.class); + private final int moduleId; + + /** + * Constructs a module address with (logical) segment id and module id. + * + * @param segId the segment id + * @param modId the module id + */ + public LcnAddrMod(int segId, int modId) { + super(segId); + this.moduleId = modId; + } + + /** + * Gets the module id. + * + * @return the module id + */ + public int getModuleId() { + return this.moduleId; + } + + @Override + public boolean isValid() { + // segId: + // 0 = Local, 1..2 = Not allowed (but "seen in the wild") + // 3 = Broadcast, 4 = Status messages, 5..127, 128 = Segment-bus disabled (valid value) + // modId: + // 1 = LCN-PRO, 2 = LCN-GVS/LCN-W, 4 = PCHK, 5..254, 255 = Unprog. (valid, but irrelevant here) + return this.segmentId >= 0 && this.segmentId <= 128 && this.moduleId >= 1 && this.moduleId <= 254; + } + + @Override + public boolean isGroup() { + return false; + } + + @Override + public int getId() { + return this.moduleId; + } + + @Override + public int hashCode() { + // Reversing the bits helps to generate better balanced trees as ids tend to be "user-sorted" + try { + if (this.isValid()) { + return ReverseNumber.reverseUInt8(this.moduleId) << 8 + ReverseNumber.reverseUInt8(this.segmentId); + } + } catch (LcnException ex) { + logger.warn("Could not calculate hash code"); + } + return -1; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof LcnAddrMod)) { + return false; + } + return this.segmentId == ((LcnAddrMod) obj).segmentId && this.moduleId == ((LcnAddrMod) obj).moduleId; + } + + @Override + public int compareTo(LcnAddrMod other) { + return this.hashCode() - other.hashCode(); + } + + @Override + public String toString() { + return this.isValid() ? String.format("S%03dM%03d", this.segmentId, this.moduleId) : "Invalid"; + } +} 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 new file mode 100644 index 0000000000000..dfff4af1b56e4 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnChannelGroup.java @@ -0,0 +1,115 @@ +/** + * 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.common; + +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.LcnModuleKeyLockTableSubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleLedSubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleLogicSubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleOutputSubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleRelaySubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleRollershutterOutputSubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleRollershutterRelaySubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleRvarLockSubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleRvarSetpointSubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleS0CounterSubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleThresholdSubHandler; +import org.openhab.binding.lcn.internal.subhandler.LcnModuleVariableSubHandler; + +/** + * Defines the supported channels of an LCN module handler. + * + * @author Fabian Wolter - Initial contribution + */ +public enum LcnChannelGroup { + OUTPUT(4, LcnModuleOutputSubHandler.class), + ROLLERSHUTTEROUTPUT(1, LcnModuleRollershutterOutputSubHandler.class), + RELAY(8, LcnModuleRelaySubHandler.class), + ROLLERSHUTTERRELAY(4, LcnModuleRollershutterRelaySubHandler.class), + LED(12, LcnModuleLedSubHandler.class), + LOGIC(4, LcnModuleLogicSubHandler.class), + BINARYSENSOR(8, LcnModuleBinarySensorSubHandler.class), + VARIABLE(12, LcnModuleVariableSubHandler.class), + RVARSETPOINT(2, LcnModuleRvarSetpointSubHandler.class), + RVARLOCK(2, LcnModuleRvarLockSubHandler.class), + THRESHOLDREGISTER1(5, LcnModuleThresholdSubHandler.class), + THRESHOLDREGISTER2(4, LcnModuleThresholdSubHandler.class), + THRESHOLDREGISTER3(4, LcnModuleThresholdSubHandler.class), + THRESHOLDREGISTER4(4, LcnModuleThresholdSubHandler.class), + S0INPUT(4, LcnModuleS0CounterSubHandler.class), + KEYLOCKTABLEA(8, LcnModuleKeyLockTableSubHandler.class), + KEYLOCKTABLEB(8, LcnModuleKeyLockTableSubHandler.class), + KEYLOCKTABLEC(8, LcnModuleKeyLockTableSubHandler.class), + KEYLOCKTABLED(8, LcnModuleKeyLockTableSubHandler.class), + CODE(0, LcnModuleCodeSubHandler.class); + + private int count; + private Class subHandlerClass; + + private LcnChannelGroup(int count, Class subHandlerClass) { + this.count = count; + this.subHandlerClass = subHandlerClass; + } + + /** + * Gets the number of Channels within the channel group. + * + * @return the Channel count + */ + public int getCount() { + return count; + } + + /** + * Checks the given Channel id against the max. Channel count in this Channel group. + * + * @param number the number to check + * @return true, if the number is in the range + */ + public boolean isValidId(int number) { + return number >= 0 && number < count; + } + + /** + * Gets the sub handler class to handle this Channel group. + * + * @return the sub handler class + */ + public Class getSubHandlerClass() { + return subHandlerClass; + } + + /** + * Converts a given table ID into the corresponding Channel group. + * + * @param tableId to convert + * @return the channel group + * @throws LcnException when the ID is out of range + */ + public static LcnChannelGroup fromTableId(int tableId) throws LcnException { + switch (tableId) { + case 0: + return KEYLOCKTABLEA; + case 1: + return KEYLOCKTABLEB; + case 2: + return KEYLOCKTABLEC; + case 3: + return KEYLOCKTABLED; + default: + throw new LcnException("Unknown key table ID: " + tableId); + } + } +} 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 new file mode 100644 index 0000000000000..b78b02190b875 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnDefs.java @@ -0,0 +1,185 @@ +/** + * 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.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common definitions and helpers for the PCK protocol. + * + * @author Tobias Jüttner - Initial Contribution + * @author Fabian Wolter - Migration to OH2 + */ +@NonNullByDefault +public final class LcnDefs { + /** Text encoding used by LCN-PCHK. */ + public static final String LCN_ENCODING = "UTF-8"; + /** Number of thresholds registers of an LCN module */ + 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 thresholds before LCN module firmware version 2013 */ + public static final int THRESHOLD_COUNT_BEFORE_2013 = 5; + /** + * Default dimmer output ramp when used with roller shutters. Results in a switching delay of 600ms. Value copied + * from the LCN-PRO motor/shutter command dialog. + */ + public static final int ROLLER_SHUTTER_RAMP_MS = 4000; + /** Max. value of a variable, threshold or regulator setpoint */ + public static final int MAX_VARIABLE_VALUE = 32768; + /** The fixed ramp when output 1+2 are controlled */ + public static final int FIXED_RAMP_MS = 250; + /** Authentication at LCN-PCHK: Request user name. */ + public static final String AUTH_USERNAME = "Username:"; + /** Authentication at LCN-PCHK: Request password. */ + public static final String AUTH_PASSWORD = "Password:"; + /** LCN-PK/PKU is connected. */ + public static final String LCNCONNSTATE_CONNECTED = "$io:#LCN:connected"; + /** LCN-PK/PKU is disconnected. */ + public static final String LCNCONNSTATE_DISCONNECTED = "$io:#LCN:disconnected"; + /** LCN-PCHK/PKE has not enough licenses to handle this connection. */ + public static final String INSUFFICIENT_LICENSES = "$err:(license?)"; + + /** + * LCN dimming mode. + * If solely modules with firmware 170206 or newer are present, LCN-PRO automatically programs {@link #NATIVE200}. + * Otherwise the default is {@link #NATIVE50}. + * Since LCN-PCHK doesn't know the current mode, it must explicitly be set. + */ + public enum OutputPortDimMode { + NATIVE50, // 0..50 dimming steps (all LCN module generations) + NATIVE200 // 0..200 dimming steps (since 170206) + } + + /** + * Tells LCN-PCHK how to format output-port status-messages. + * {@link #NATIVE} allows to show the status in half-percent steps (e.g. "10.5"). + * {@link #NATIVE} is completely backward compatible and there are no restrictions + * concerning the LCN module generations. It requires LCN-PCHK 2.3 or higher though. + */ + public enum OutputPortStatusMode { + PERCENT, // Default (compatible with all versions of LCN-PCHK) + NATIVE // 0..200 steps (since LCN-PCHK 2.3) + } + + /** Possible states for LCN LEDs. */ + public enum LedStatus { + OFF, + ON, + BLINK, + FLICKER; + } + + /** Possible states for LCN logic-operations. */ + public enum LogicOpStatus { + NOT, + OR, // Note: Actually not correct since AND won't be OR also + AND; + } + + /** Time units used for several LCN commands. */ + public enum TimeUnit { + SECONDS, + MINUTES, + HOURS, + DAYS; + + /** + * Parses the given input into a time unit. + * It supports several alternative terms. + * + * @param input the text to parse + * @return the parsed {@link TimeUnit} + * @throws LcnException if input could not be parsed + */ + public static TimeUnit parse(String input) throws LcnException { + switch (input.toUpperCase()) { + case "SECONDS": + case "SECOND": // Allow singular too + case "SEC": + case "S": + return SECONDS; + case "MINUTES": + case "MINUTE": // Allow singular too + case "MIN": + case "M": + return MINUTES; + case "HOURS": + case "HOUR": // Allow singular too + case "H": + return HOURS; + case "DAYS": + case "DAY": // Allow singular too + case "D": + return DAYS; + } + throw new LcnException(); + } + } + + /** Relay-state modifiers used in LCN commands. */ + public enum RelayStateModifier { + ON, + OFF, + TOGGLE, + NOCHANGE + } + + /** Value-reference for relative LCN variable commands. */ + public enum RelVarRef { + CURRENT, + PROG // Programmed value (LCN-PRO). Relevant for set-points and thresholds. + } + + /** Command types used when sending LCN keys. */ + public enum SendKeyCommand { + HIT, + MAKE, + BREAK, + DONTSEND + } + + /** Key-lock modifiers used in LCN commands. */ + public enum KeyLockStateModifier { + ON, + OFF, + TOGGLE, + NOCHANGE + } + + /** List of key tables of an LCN module */ + public enum KeyTable { + A, + B, + C, + D + } + + /** + * Generates an array of booleans from an input integer (actually a byte). + * + * @param input the input byte (0..255) + * @return the array of 8 booleans + * @throws IllegalArgumentException if input is out of range (not a byte) + */ + public static boolean[] getBooleanValue(int inputByte) throws IllegalArgumentException { + if (inputByte < 0 || inputByte > 255) { + throw new IllegalArgumentException(); + } + boolean[] result = new boolean[8]; + for (int i = 0; i < 8; ++i) { + result[i] = (inputByte & (1 << i)) != 0; + } + return result; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnException.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnException.java new file mode 100644 index 0000000000000..d630469b4cef7 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnException.java @@ -0,0 +1,37 @@ +/** + * 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.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Default checked exception. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnException extends Exception { + private static final long serialVersionUID = -4341882774124288028L; + + public LcnException() { + super(); + } + + public LcnException(String message) { + super(message); + } + + public LcnException(NumberFormatException e) { + super(e); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/NullScheduledFuture.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/NullScheduledFuture.java new file mode 100644 index 0000000000000..2f18ac19c359e --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/NullScheduledFuture.java @@ -0,0 +1,80 @@ +/** + * 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.common; + +import java.util.concurrent.Delayed; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Empty ScheduledFuture, used for initialization. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class NullScheduledFuture implements ScheduledFuture { + @NonNullByDefault({}) + private static class LazyHolder { + static final NullScheduledFuture INSTANCE = new NullScheduledFuture(); + } + + private NullScheduledFuture() { + // nothing + } + + /** Gets the instance of this singleton. */ + public static NullScheduledFuture getInstance() { + return LazyHolder.INSTANCE; + } + + @Override + public long getDelay(@Nullable TimeUnit unit) { + return 0; + } + + @Override + public int compareTo(@Nullable Delayed o) { + return 0; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return true; + } + + @Override + public boolean isCancelled() { + return true; + } + + @Override + public boolean isDone() { + return true; + } + + @Override + public Object get() throws InterruptedException, ExecutionException { + return new Object(); + } + + @Override + public Object get(long timeout, @Nullable TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return new Object(); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/PckGenerator.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/PckGenerator.java new file mode 100644 index 0000000000000..47b791e43ae06 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/PckGenerator.java @@ -0,0 +1,779 @@ +/** + * 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.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Helpers to generate LCN-PCK commands. + *

    + * LCN-PCK is the command-syntax used by LCN-PCHK to send and receive LCN commands. + * + * @author Tobias Jüttner - Initial Contribution + * @author Fabian Wolter - Migration to OH2 + */ +@NonNullByDefault +public final class PckGenerator { + private static final Logger LOGGER = LoggerFactory.getLogger(PckGenerator.class); + /** Termination character after a PCK message */ + public static final String TERMINATION = "\n"; + + /** + * Generates a keep-alive. + * LCN-PCHK will close the connection if it does not receive any commands from + * an open {@link Connection} for a specific period (10 minutes by default). + * + * @param counter the current ping's id (optional, but "best practice"). Should start with 1 + * @return the PCK command as text + */ + public static String ping(int counter) { + return String.format("^ping%d", counter); + } + + /** + * Generates a PCK command that will set the LCN-PCHK connection's operation mode. + * This influences how output-port commands and status are interpreted and must be + * in sync with the LCN bus. + * + * @param dimMode see {@link LcnDefs.OutputPortDimMode} + * @param statusMode see {@link LcnDefs.OutputPortStatusMode} + * @return the PCK command as text + */ + public static String setOperationMode(LcnDefs.OutputPortDimMode dimMode, LcnDefs.OutputPortStatusMode statusMode) { + return "!OM" + (dimMode == LcnDefs.OutputPortDimMode.NATIVE200 ? "1" : "0") + + (statusMode == LcnDefs.OutputPortStatusMode.PERCENT ? "P" : "N"); + } + + /** + * Generates a PCK address header. + * Used for commands to LCN modules and groups. + * + * @param addr the target's address (module or group) + * @param localSegId the local segment id where the physical bus connection is located + * @param wantsAck true to claim an acknowledge / receipt from the target + * @return the PCK address header as text + */ + public static String generateAddressHeader(LcnAddr addr, int localSegId, boolean wantsAck) { + return String.format(">%s%03d%03d%s", addr.isGroup() ? "G" : "M", addr.getPhysicalSegmentId(localSegId), + addr.getId(), wantsAck ? "!" : "."); + } + + /** + * Generates a scan-command for LCN segment-couplers. + * Used to detect the local segment (where the physical bus connection is located). + * + * @return the PCK command (without address header) as text + */ + public static String segmentCouplerScan() { + return "SK"; + } + + /** + * Generates a firmware/serial-number request. + * + * @return the PCK command (without address header) as text + */ + public static String requestSn() { + return "SN"; + } + + /** + * Generates a command to request a part of a name of a module. + * + * @param partNumber 0..1 + * @return the PCK command (without address header) as text + */ + public static String requestModuleName(int partNumber) { + return "NMN" + (partNumber + 1); + } + + /** + * Generates an output-port status request. + * + * @param outputId 0..3 + * @return the PCK command (without address header) as text + * @throws LcnException if out of range + */ + public static String requestOutputStatus(int outputId) throws LcnException { + if (outputId < 0 || outputId > 3) { + throw new LcnException(); + } + return String.format("SMA%d", outputId + 1); + } + + /** + * Generates a dim command for a single output-port. + * + * @param outputId 0..3 + * @param percent 0..100 + * @param rampMs ramp in milliseconds + * @return the PCK command (without address header) as text + * @throws LcnException if out of range + */ + public static String dimOutput(int outputId, double percent, int rampMs) throws LcnException { + if (outputId < 0 || outputId > 3) { + throw new LcnException(); + } + int rampNative = PckGenerator.timeToRampValue(rampMs); + int n = (int) Math.round(percent * 2); + if ((n % 2) == 0) { // Use the percent command (supported by all LCN-PCHK versions) + return String.format("A%dDI%03d%03d", outputId + 1, n / 2, rampNative); + } else { // We have a ".5" value. Use the native command (supported since LCN-PCHK 2.3) + return String.format("O%dDI%03d%03d", outputId + 1, n, rampNative); + } + } + + /** + * Generates a dim command for all output-ports. + * + * Attention: This command is supported since module firmware version 180501 AND LCN-PCHK 2.61 + * + * @param firstPercent dimmer value of the first output 0..100 + * @param secondPercent dimmer value of the first output 0..100 + * @param thirdPercent dimmer value of the first output 0..100 + * @param fourthPercent dimmer value of the first output 0..100 + * @param rampMs ramp in milliseconds + * @return the PCK command (without address header) as text + */ + public static String dimAllOutputs(double firstPercent, double secondPercent, double thirdPercent, + double fourthPercent, int rampMs) { + long n1 = Math.round(firstPercent * 2); + long n2 = Math.round(secondPercent * 2); + long n3 = Math.round(thirdPercent * 2); + long n4 = Math.round(fourthPercent * 2); + + return String.format("OY%03d%03d%03d%03d%03d", n1, n2, n3, n4, timeToRampValue(rampMs)); + } + + /** + * Generates a control command for switching all outputs ON or OFF with a fixed ramp of 0.5s. + * + * @param percent 0..100 + * @returnthe PCK command (without address header) as text + */ + public static String controlAllOutputs(double percent) { + return String.format("AH%03d", Math.round(percent)); + } + + /** + * Generates a control command for switching dimmer output 1 and 2 both ON or OFF with a fixed ramp of 0.5s or + * without ramp. + * + * @param on true, if outputs shall be switched on + * @param ramp true, if the ramp shall be 0.5s, else 0s + * @return the PCK command (without address header) as text + */ + public static String controlOutputs12(boolean on, boolean ramp) { + int commandByte; + if (on) { + commandByte = ramp ? 0xC8 : 0xFD; + } else { + commandByte = ramp ? 0x00 : 0xFC; + } + return String.format("X2%03d%03d%03d", 1, commandByte, commandByte); + } + + /** + * Generates a dim command for setting the brightness of dimmer output 1 and 2 with a fixed ramp of 0.5s. + * + * @param percent brightness of both outputs 0..100 + * @return the PCK command (without address header) as text + */ + public static String dimOutputs12(double percent) { + long localPercent = Math.round(percent); + return String.format("AY%03d%03d", localPercent, localPercent); + } + + /** + * Let an output flicker. + * + * @param outputId output id 0..3 + * @param depth flicker depth, the higher the deeper 0..2 + * @param ramp the flicker speed 0..2 + * @param count number of flashes 1..15 + * @return the PCK command (without address header) as text + * @throws LcnException when the input values are out of range + */ + public static String flickerOutput(int outputId, int depth, int ramp, int count) throws LcnException { + if (outputId < 0 || outputId > 3) { + throw new LcnException("Output number out of range"); + } + if (count < 1 || count > 15) { + throw new LcnException("Number of flashes out of range"); + } + String depthString; + switch (depth) { + case 0: + depthString = "G"; + break; + case 1: + depthString = "M"; + break; + case 2: + depthString = "S"; + break; + default: + throw new LcnException("Depth out of range"); + } + String rampString; + switch (ramp) { + case 0: + rampString = "L"; + break; + case 1: + rampString = "M"; + break; + case 2: + rampString = "S"; + break; + default: + throw new LcnException("Ramp out of range"); + } + return String.format("A%dFL%s%s%02d", outputId + 1, depthString, rampString, count); + } + + /** + * Generates a command to change the value of an output-port. + * + * @param outputId 0..3 + * @param percent -100..100 + * @return the PCK command (without address header) as text + * @throws LcnException if out of range + */ + public static String relOutput(int outputId, double percent) throws LcnException { + if (outputId < 0 || outputId > 3) { + throw new LcnException(); + } + int n = (int) Math.round(percent * 2); + if ((n % 2) == 0) { // Use the percent command (supported by all LCN-PCHK versions) + return String.format("A%d%s%03d", outputId + 1, percent >= 0 ? "AD" : "SB", Math.abs(n / 2)); + } else { // We have a ".5" value. Use the native command (supported since LCN-PCHK 2.3) + return String.format("O%d%s%03d", outputId + 1, percent >= 0 ? "AD" : "SB", Math.abs(n)); + } + } + + /** + * Generates a command that toggles a single output-port (on->off, off->on). + * + * @param outputId 0..3 + * @param ramp see {@link PckGenerator#timeToRampValue(int)} + * @return the PCK command (without address header) as text + * @throws LcnException if out of range + */ + public static String toggleOutput(int outputId, int ramp) throws LcnException { + if (outputId < 0 || outputId > 3) { + throw new LcnException(); + } + return String.format("A%dTA%03d", outputId + 1, ramp); + } + + /** + * Generates a command that toggles all output-ports (on->off, off->on). + * + * @param ramp see {@link PckGenerator#timeToRampValue(int)} + * @return the PCK command (without address header) as text + */ + public static String toggleAllOutputs(int ramp) { + return String.format("AU%03d", ramp); + } + + /** + * Generates a relays-status request. + * + * @return the PCK command (without address header) as text + */ + public static String requestRelaysStatus() { + return "SMR"; + } + + /** + * Generates a command to control relays. + * + * @param states the 8 modifiers for the relay states + * @return the PCK command (without address header) as text + * @throws LcnException if out of range + */ + public static String controlRelays(LcnDefs.RelayStateModifier[] states) throws LcnException { + if (states.length != 8) { + throw new LcnException(); + } + String ret = "R8"; + for (int i = 0; i < 8; ++i) { + switch (states[i]) { + case ON: + ret += "1"; + break; + case OFF: + ret += "0"; + break; + case TOGGLE: + ret += "U"; + break; + case NOCHANGE: + ret += "-"; + break; + default: + throw new LcnException(); + } + } + return ret; + } + + /** + * Generates a binary-sensors status request. + * + * @return the PCK command (without address header) as text + */ + public static String requestBinSensorsStatus() { + return "SMB"; + } + + /** + * Generates a command that sets a variable absolute. + * + * @param number regulator number 0..1 + * @param value the absolute value to set + * @return the PCK command (without address header) as text + * @throws LcnException + */ + public static String setSetpointAbsolute(int number, int value) throws LcnException { + int internalValue = value; + // Set absolute (not in PCK yet) + int b1 = number << 6; // 01000000 + b1 |= 0x20; // xx10xxxx (set absolute) + if (value < 1000) { + internalValue = 1000 - internalValue; + b1 |= 8; + } else { + internalValue -= 1000; + } + b1 |= (internalValue >> 8) & 0x0f; // xxxx1111 + int b2 = internalValue & 0xff; + return String.format("X2%03d%03d%03d", 30, b1, b2); + } + + /** + * Generates a command to change the value of a variable. + * + * @param variable the target variable to change + * @param type the reference-point + * @param value the native LCN value to add/subtract (can be negative) + * @return the PCK command (without address header) as text + * @throws LcnException if command is not supported + */ + public static String setVariableRelative(Variable variable, LcnDefs.RelVarRef type, int value) throws LcnException { + if (variable.getNumber() == 0) { + // Old command for variable 1 / T-var (compatible with all modules) + return String.format("Z%s%d", value >= 0 ? "A" : "S", Math.abs(value)); + } else { // New command for variable 1-12 (compatible with all modules, since LCN-PCHK 2.8) + return String.format("Z%s%03d%d", value >= 0 ? "+" : "-", variable.getNumber() + 1, Math.abs(value)); + } + } + + /** + * Generates a command the change the value of a regulator setpoint relative. + * + * @param number 0..1 + * @param type relative to the current or to the programmed value + * @param value the relative value -4000..+4000 + * @return the PCK command (without address header) as text + */ + public static String setSetpointRelative(int number, LcnDefs.RelVarRef type, int value) { + return String.format("RE%sS%s%s%d", number == 0 ? "A" : "B", type == LcnDefs.RelVarRef.CURRENT ? "A" : "P", + value >= 0 ? "+" : "-", Math.abs(value)); + } + + /** + * Generates a command the change the value of a threshold relative. + * + * @param variable the threshold to change + * @param type relative to the current or to the programmed value + * @param value the relative value -4000..+4000 + * @param is2013 true, if the LCN module's firmware is equal to or newer than 2013 + * @return the PCK command (without address header) as text + */ + public static String setThresholdRelative(Variable variable, LcnDefs.RelVarRef type, int value, boolean is2013) + throws LcnException { + if (is2013) { // New command for registers 1-4 (since 170206, LCN-PCHK 2.8) + return String.format("SS%s%04d%sR%d%d", type == LcnDefs.RelVarRef.CURRENT ? "R" : "E", Math.abs(value), + value >= 0 ? "A" : "S", variable.getNumber() + 1, variable.getThresholdNumber().get() + 1); + } else if (variable.getNumber() == 0) { // Old command for register 1 (before 170206) + return String.format("SS%s%04d%s%s%s%s%s%s", type == LcnDefs.RelVarRef.CURRENT ? "R" : "E", Math.abs(value), + value >= 0 ? "A" : "S", variable.getThresholdNumber().get() == 0 ? "1" : "0", + variable.getThresholdNumber().get() == 1 ? "1" : "0", + variable.getThresholdNumber().get() == 2 ? "1" : "0", + variable.getThresholdNumber().get() == 3 ? "1" : "0", + variable.getThresholdNumber().get() == 4 ? "1" : "0"); + } else { + throw new LcnException( + "Module does not have threshold register " + (variable.getThresholdNumber().get() + 1)); + } + } + + /** + * Generates a variable value request. + * + * @param variable the variable to request + * @param firmwareVersion the target module's firmware version + * @return the PCK command (without address header) as text + * @throws LcnException if command is not supported + */ + public static String requestVarStatus(Variable variable, int firmwareVersion) throws LcnException { + if (firmwareVersion >= LcnBindingConstants.FIRMWARE_2013) { + int id = variable.getNumber(); + switch (variable.getType()) { + case UNKNOWN: + throw new LcnException("Variable unknown"); + case VARIABLE: + return String.format("MWT%03d", id + 1); + case REGULATOR: + return String.format("MWS%03d", id + 1); + case THRESHOLD: + return String.format("SE%03d", id + 1); // Whole register + case S0INPUT: + return String.format("MWC%03d", id + 1); + } + throw new LcnException("Unsupported variable type: " + variable); + } else { + switch (variable) { + case VARIABLE1: + return "MWV"; + case VARIABLE2: + return "MWTA"; + case VARIABLE3: + return "MWTB"; + case RVARSETPOINT1: + return "MWSA"; + case RVARSETPOINT2: + return "MWSB"; + case THRESHOLDREGISTER11: + case THRESHOLDREGISTER12: + case THRESHOLDREGISTER13: + case THRESHOLDREGISTER14: + case THRESHOLDREGISTER15: + return "SL1"; // Whole register + default: + throw new LcnException("Unsupported variable type: " + variable); + } + } + } + + /** + * Generates a request for LED and logic-operations states. + * + * @return the PCK command (without address header) as text + */ + public static String requestLedsAndLogicOpsStatus() { + return "SMT"; + } + + /** + * Generates a command to the set the state of a single LED. + * + * @param ledId 0..11 + * @param state the state to set + * @return the PCK command (without address header) as text + * @throws LcnException if out of range + */ + public static String controlLed(int ledId, LcnDefs.LedStatus state) throws LcnException { + if (ledId < 0 || ledId > 11) { + throw new LcnException(); + } + return String.format("LA%03d%s", ledId + 1, state == LcnDefs.LedStatus.OFF ? "A" + : state == LcnDefs.LedStatus.ON ? "E" : state == LcnDefs.LedStatus.BLINK ? "B" : "F"); + } + + /** + * Generates a command to send LCN keys. + * + * @param cmds the 4 concrete commands to send for the tables (A-D) + * @param keys the tables' 8 key-states (true means "send") + * @return the PCK command (without address header) as text + * @throws IllegalArgumentException if out of range + */ + public static String sendKeys(LcnDefs.SendKeyCommand[] cmds, boolean[] keys) throws LcnException { + if (cmds.length != 4 || keys.length != 8) { + throw new LcnException(); + } + String ret = "TS"; + for (int i = 0; i < 4; ++i) { + switch (cmds[i]) { + case HIT: + ret += "K"; + break; + case MAKE: + ret += "L"; + break; + case BREAK: + ret += "O"; + break; + case DONTSEND: + // By skipping table D (if it is not used), we use the old command + // for table A-C which is compatible with older LCN modules + if (i < 3) { + ret += "-"; + } + break; + default: + throw new LcnException(); + } + } + for (int i = 0; i < 8; ++i) { + ret += keys[i] ? "1" : "0"; + } + return ret; + } + + /** + * Generates a command to send LCN keys deferred / delayed. + * + * @param tableId 0(A)..3(D) + * @param time the delay time + * @param timeUnit the time unit + * @param keys the key-states (true means "send") + * @return the PCK command (without address header) as text + * @throws LcnException if out of range + */ + public static String sendKeysHitDefered(int tableId, int time, LcnDefs.TimeUnit timeUnit, boolean[] keys) + throws LcnException { + if (tableId < 0 || tableId > 3 || keys.length != 8) { + throw new IllegalArgumentException(); + } + String ret = "TV"; + switch (tableId) { + case 0: + ret += "A"; + break; + case 1: + ret += "B"; + break; + case 2: + ret += "C"; + break; + case 3: + ret += "D"; + break; + default: + throw new LcnException(); + } + ret += String.format("%03d", time); + switch (timeUnit) { + case SECONDS: + if (time < 1 || time > 60) { + throw new LcnException(); + } + ret += "S"; + break; + case MINUTES: + if (time < 1 || time > 90) { + throw new LcnException(); + } + ret += "M"; + break; + case HOURS: + if (time < 1 || time > 50) { + throw new LcnException(); + } + ret += "H"; + break; + case DAYS: + if (time < 1 || time > 45) { + throw new LcnException(); + } + ret += "D"; + break; + default: + throw new LcnException(); + } + for (int i = 0; i < 8; ++i) { + ret += keys[i] ? "1" : "0"; + } + return ret; + } + + /** + * Generates a request for key-lock states. + * Always requests table A-D. Supported since LCN-PCHK 2.8. + * + * @return the PCK command (without address header) as text + */ + public static String requestKeyLocksStatus() { + return "STX"; + } + + /** + * Generates a command to lock keys. + * + * @param tableId 0(A)..3(D) + * @param states the 8 key-lock modifiers + * @return the PCK command (without address header) as text + * @throws LcnException if out of range + */ + public static String lockKeys(int tableId, LcnDefs.KeyLockStateModifier[] states) throws LcnException { + if (tableId < 0 || tableId > 3 || states.length != 8) { + throw new LcnException(); + } + String ret = String.format("TX%s", tableId == 0 ? "A" : tableId == 1 ? "B" : tableId == 2 ? "C" : "D"); + for (int i = 0; i < 8; ++i) { + switch (states[i]) { + case ON: + ret += "1"; + break; + case OFF: + ret += "0"; + break; + case TOGGLE: + ret += "U"; + break; + case NOCHANGE: + ret += "-"; + break; + default: + throw new LcnException(); + } + } + return ret; + } + + /** + * Generates a command to lock keys for table A temporary. + * There is no hardware-support for locking tables B-D. + * + * @param time the lock time + * @param timeUnit the time unit + * @param keys the 8 key-lock states (true means lock) + * @return the PCK command (without address header) as text + * @throws LcnException if out of range + */ + public static String lockKeyTabATemporary(int time, LcnDefs.TimeUnit timeUnit, boolean[] keys) throws LcnException { + if (keys.length != 8) { + throw new IllegalArgumentException(); + } + String ret = String.format("TXZA%03d", time); + switch (timeUnit) { + case SECONDS: + if (time < 1 || time > 60) { + throw new LcnException(); + } + ret += "S"; + break; + case MINUTES: + if (time < 1 || time > 90) { + throw new LcnException(); + } + ret += "M"; + break; + case HOURS: + if (time < 1 || time > 50) { + throw new LcnException(); + } + ret += "H"; + break; + case DAYS: + if (time < 1 || time > 45) { + throw new LcnException(); + } + ret += "D"; + break; + default: + throw new LcnException(); + } + for (int i = 0; i < 8; ++i) { + ret += keys[i] ? "1" : "0"; + } + return ret; + } + + /** + * Generates the command header / start for sending dynamic texts. + * Used by LCN-GTxD periphery (supports 4 text rows). + * To complete the command, the text to send must be appended (UTF-8 encoding). + * Texts are split up into up to 5 parts with 12 "UTF-8 bytes" each. + * + * @param row 0..3 + * @param part 0..4 + * @return the PCK command (without address header) as text + * @throws LcnException if out of range + */ + public static String dynTextHeader(int row, int part) throws LcnException { + if (row < 0 || row > 3 || part < 0 || part > 4) { + throw new LcnException("Row number is out of range: " + (row + 1)); + } + return String.format("GTDT%d%d", row + 1, part + 1); + } + + /** + * Generates a command to lock a regulator. + * + * @param regId 0..1 + * @param state the lock state + * @return the PCK command (without address header) as text + * @throws IllegalArgumentException if out of range + */ + public static String lockRegulator(int regId, boolean state) throws LcnException { + if (regId < 0 || regId > 1) { + throw new LcnException(); + } + return String.format("RE%sX%s", regId == 0 ? "A" : "B", state ? "S" : "A"); + } + + /** + * Generates a null command, used for broadcast messages. + * + * @return the PCK command (without address header) as text + */ + public static String nullCommand() { + return "LEER"; + } + + /** + * Converts the given time into an LCN ramp value. + * + * @param timeMSec the time in milliseconds + * @return the (LCN-internal) ramp value (0..250) + */ + private static int timeToRampValue(int timeMSec) { + int ret; + if (timeMSec < 250) { + ret = 0; + } else if (timeMSec < 500) { + ret = 1; + } else if (timeMSec < 660) { + ret = 2; + } else if (timeMSec < 1000) { + ret = 3; + } else if (timeMSec < 1400) { + ret = 4; + } else if (timeMSec < 2000) { + ret = 5; + } else if (timeMSec < 3000) { + ret = 6; + } else if (timeMSec < 4000) { + ret = 7; + } else if (timeMSec < 5000) { + ret = 8; + } else if (timeMSec < 6000) { + ret = 9; + } else { + ret = (timeMSec / 1000 - 6) / 2 + 10; + if (ret >= 250) { + ret = 250; + LOGGER.warn("Ramp value is too high. Limiting value to 486s."); + } + } + return ret; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/ReverseNumber.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/ReverseNumber.java new file mode 100644 index 0000000000000..70a6c1fc82bd8 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/ReverseNumber.java @@ -0,0 +1,53 @@ +/** + * 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.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Helper to bitwise reverse numbers. + * + * @author Tobias Jüttner - Initial Contribution + */ +@NonNullByDefault +final class ReverseNumber { + /** Cache with all reversed 8 bit values. */ + private static final int[] REVERSED_UINT8 = new int[256]; + + /** Initializes static data once this class is first used. */ + static { + for (int i = 0; i < 256; ++i) { + int reversed = 0; + for (int j = 0; j < 8; ++j) { + if ((i & (1 << j)) != 0) { + reversed |= (0x80 >> j); + } + } + REVERSED_UINT8[i] = reversed; + } + } + + /** + * Reverses the given 8 bit value bitwise. + * + * @param value the value to reverse bitwise (treated as unsigned 8 bit value) + * @return the reversed value + * @throws LcnException if value is out of range (not unsigned 8 bit) + */ + static int reverseUInt8(int value) throws LcnException { + if (value < 0 || value > 255) { + throw new LcnException(); + } + return REVERSED_UINT8[value]; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java new file mode 100644 index 0000000000000..ce8be45e0f374 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java @@ -0,0 +1,272 @@ +/** + * 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.common; + +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.openhab.binding.lcn.internal.LcnBindingConstants; + +/** + * LCN variable types. + * + * @author Tobias Jüttner - Initial Contribution + * @author Fabian Wolter - Migration to OH2 + */ +public enum Variable { + UNKNOWN(0, Type.UNKNOWN, null), // Used if the real type is not known (yet) + VARIABLE1(0, Type.VARIABLE, LcnChannelGroup.VARIABLE), // or TVar + VARIABLE2(1, Type.VARIABLE, LcnChannelGroup.VARIABLE), + VARIABLE3(2, Type.VARIABLE, LcnChannelGroup.VARIABLE), + VARIABLE4(3, Type.VARIABLE, LcnChannelGroup.VARIABLE), + VARIABLE5(4, Type.VARIABLE, LcnChannelGroup.VARIABLE), + VARIABLE6(5, Type.VARIABLE, LcnChannelGroup.VARIABLE), + VARIABLE7(6, Type.VARIABLE, LcnChannelGroup.VARIABLE), + VARIABLE8(7, Type.VARIABLE, LcnChannelGroup.VARIABLE), + VARIABLE9(8, Type.VARIABLE, LcnChannelGroup.VARIABLE), + VARIABLE10(9, Type.VARIABLE, LcnChannelGroup.VARIABLE), + VARIABLE11(10, Type.VARIABLE, LcnChannelGroup.VARIABLE), + VARIABLE12(11, Type.VARIABLE, LcnChannelGroup.VARIABLE), // Since 170206 + RVARSETPOINT1(0, Type.REGULATOR, LcnChannelGroup.RVARSETPOINT), + RVARSETPOINT2(1, Type.REGULATOR, LcnChannelGroup.RVARSETPOINT), // Set-points for regulators + THRESHOLDREGISTER11(0, 0, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER1), + THRESHOLDREGISTER12(0, 1, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER1), + THRESHOLDREGISTER13(0, 2, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER1), + THRESHOLDREGISTER14(0, 3, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER1), + // Register 1 (THRESHOLDREGISTER15 only before 170206) + THRESHOLDREGISTER15(0, 4, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER1), + THRESHOLDREGISTER21(1, 0, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER2), + THRESHOLDREGISTER22(1, 1, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER2), + THRESHOLDREGISTER23(1, 2, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER2), + THRESHOLDREGISTER24(1, 3, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER2), // Register 2 (since 2012) + THRESHOLDREGISTER31(2, 0, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER3), + THRESHOLDREGISTER32(2, 1, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER3), + THRESHOLDREGISTER33(2, 2, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER3), + THRESHOLDREGISTER34(2, 3, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER3), // Register 3 (since 2012) + THRESHOLDREGISTER41(3, 0, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER4), + THRESHOLDREGISTER42(3, 1, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER4), + THRESHOLDREGISTER43(3, 2, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER4), + THRESHOLDREGISTER44(3, 3, Type.THRESHOLD, LcnChannelGroup.THRESHOLDREGISTER4), // Register 4 (since 2012) + S0INPUT1(0, Type.S0INPUT, LcnChannelGroup.S0INPUT), + S0INPUT2(1, Type.S0INPUT, LcnChannelGroup.S0INPUT), + S0INPUT3(2, Type.S0INPUT, LcnChannelGroup.S0INPUT), + S0INPUT4(3, Type.S0INPUT, LcnChannelGroup.S0INPUT); // LCN-BU4L + + private int number; + private Optional thresholdNumber = Optional.empty(); + private Type type; + private LcnChannelGroup channelGroup; + + /** + * Defines the origin of an LCN variable. + */ + public enum Type { + UNKNOWN, + VARIABLE, + REGULATOR, + THRESHOLD, + S0INPUT + } + + Variable(int number, Type type, LcnChannelGroup channelGroup) { + this.number = number; + this.type = type; + this.channelGroup = channelGroup; + } + + Variable(int number, int thresholdNumber, Type type, LcnChannelGroup channelGroup) { + this(number, type, channelGroup); + this.thresholdNumber = Optional.of(thresholdNumber); + } + + /** + * Gets the type of the variable's origin. + * + * @return the type + */ + public Type getType() { + return type; + } + + /** + * Gets the channel type of the variable. + * + * @return the channel type + */ + public LcnChannelGroup getChannelType() { + return channelGroup; + } + + /** + * Gets the threshold number within a threshold register. + * + * @return the threshold number + */ + public Optional getThresholdNumber() { + return thresholdNumber; + } + + /** + * Gets the threshold register number. + * + * @return the threshold register number + */ + public int getNumber() { + return number; + } + + /** + * Translates a given id into a variable type. + * + * @param number 0..11 + * @return the translated {@link Variable} + * @throws LcnException if out of range + */ + public static Variable varIdToVar(int number) throws LcnException { + if (number < 0 || number >= LcnChannelGroup.VARIABLE.getCount()) { + throw new LcnException("Invalid variable number: " + (number + 1)); + } + return getVariableFromNumberAndType(number, Type.VARIABLE, v -> true); + } + + /** + * Translates a given id into a LCN set-point variable type. + * + * @param number 0..1 + * @return the translated {@link Variable} + * @throws LcnException if out of range + */ + public static Variable setPointIdToVar(int number) throws LcnException { + if (number < 0 || number >= LcnChannelGroup.RVARSETPOINT.getCount()) { + throw new LcnException(); + } + + return getVariableFromNumberAndType(number, Type.REGULATOR, v -> true); + } + + /** + * Translates given ids into a LCN threshold variable type. + * + * @param registerNumber 0..3 + * @param thresholdNumber 0..4 for register 0, 0..3 for registers 1..3 + * @return the translated {@link Variable} + * @throws LcnException if out of range + */ + public static Variable thrsIdToVar(int registerNumber, int thresholdNumber) throws LcnException { + if (registerNumber < 0 || registerNumber >= LcnDefs.THRESHOLD_REGISTER_COUNT) { + throw new LcnException("Threshold register number out of range: " + (registerNumber + 1)); + } + if (thresholdNumber < 0 || thresholdNumber >= (registerNumber == 0 ? 5 : 4)) { + throw new LcnException("Threshold number out of range: " + (thresholdNumber + 1)); + } + return getVariableFromNumberAndType(registerNumber, Type.THRESHOLD, + v -> v.thresholdNumber.get() == thresholdNumber); + } + + /** + * Translates a given id into a LCN S0-input variable type. + * + * @param number 0..3 + * @return the translated {@link Variable} + * @throws LcnException if out of range + */ + public static Variable s0IdToVar(int number) throws LcnException { + if (number < 0 || number >= LcnChannelGroup.S0INPUT.getCount()) { + throw new LcnException(); + } + return getVariableFromNumberAndType(number, Type.S0INPUT, v -> true); + } + + private static Variable getVariableFromNumberAndType(int varId, Type type, Predicate filter) + throws LcnException { + return Stream.of(values()).filter(v -> v.type == type).filter(v -> v.number == varId).filter(filter).findAny() + .orElseThrow(() -> new LcnException()); + } + + /** + * Checks if this variable type uses special values. + * Examples for special values: "No value yet", "sensor defective" etc. + * + * @return true if special values are in use + */ + public boolean useLcnSpecialValues() { + return type != Type.S0INPUT; + } + + /** + * Module-generation check. + * Checks if the given variable type would receive a typed response if + * its status was requested. + * + * @param firmwareVersion the target LCN-modules firmware version + * @return true if a response would contain the variable's type + */ + public boolean hasTypeInResponse(int firmwareVersion) { + return (firmwareVersion >= LcnBindingConstants.FIRMWARE_2013 + || (type != Type.VARIABLE && type != Type.REGULATOR)); + } + + /** + * Module-generation check. + * Checks if the given variable type automatically sends status-updates on + * value-change. It must be polled otherwise. + * + * @param firmwareVersion the target LCN-module's firmware version + * @return true if the LCN module supports automatic status-messages for this {@link Variable} + */ + public boolean isEventBased(int firmwareVersion) { + return type == Type.REGULATOR || type == Type.S0INPUT || firmwareVersion >= LcnBindingConstants.FIRMWARE_2013; + } + + /** + * Module-generation check. + * Checks if the target LCN module would automatically send status-updates if + * the given variable type was changed by command. + * + * @param variable the variable type to check + * @param is2013 the target module's-generation + * @return true if a poll is required to get the new status-value + */ + public boolean shouldPollStatusAfterCommand(int firmwareVersion) { + // Regulator set-points will send status-messages on every change (all firmware versions) + if (type == Type.REGULATOR) { + return false; + } + // Thresholds since 170206 will send status-messages on every change + if (firmwareVersion >= LcnBindingConstants.FIRMWARE_2013 && type == Type.THRESHOLD) { + return false; + } + // Others: + // - Variables before 170206 will never send any status-messages + // - Variables since 170206 only send status-messages on "big" changes + // - Thresholds before 170206 will never send any status-messages + // - S0-inputs only send status-messages on "big" changes + // (all "big changes" cases force us to poll the status to get faster updates) + return true; + } + + /** + * Module-generation check. + * Checks if the target LCN module would automatically send status-updates if + * the given regulator's lock-state was changed by command. + * + * @param firmwareVersion the target LCN-module's firmware version + * @param lockState the lock-state sent via command + * @return true if a poll is required to get the new status-value + */ + public boolean shouldPollStatusAfterRegulatorLock(int firmwareVersion, boolean lockState) { + // LCN modules before 170206 will send an automatic status-message for "lock", but not for "unlock" + return !lockState && firmwareVersion < LcnBindingConstants.FIRMWARE_2013; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/VariableValue.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/VariableValue.java new file mode 100644 index 0000000000000..ecced343c134b --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/VariableValue.java @@ -0,0 +1,99 @@ +/** + * 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.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.types.State; + +/** + * A value of an LCN variable. + *

    + * It internally stores the native LCN value and allows to convert from/into other units. + * Some conversions allow to specify whether the source value is absolute or relative. + * Relative values are used to create {@link VariableValue}s that can be added/subtracted from + * other (absolute) {@link VariableValue}s. + * + * @author Tobias Jüttner - Initial Contribution + * @author Fabian Wolter - Migration to OH2 + */ +@NonNullByDefault +public class VariableValue { + private static final String SENSOR_DEFECTIVE_STATE = "DEFECTIVE"; + + /** The absolute, native LCN value. */ + private long nativeValue; + + /** + * Constructor with native LCN value. + * + * @param nativeValue the native value + */ + public VariableValue(long nativeValue) { + this.nativeValue = nativeValue; + } + + /** + * Converts to native value. Mask locked bit. + * + * @return the converted value + */ + public long toNative(boolean useSpecialValues) { + if (useSpecialValues) { + return nativeValue & 0x7fff; + } else { + return nativeValue; + } + } + + /** + * Returns the lock state if value comes from a regulator set-point. + * If the variable type is not a regulator, the result is undefined. + * + * @return true if the regulator is locked + */ + public boolean isRegulatorLocked() { + return (this.nativeValue & 0x8000) != 0; + } + + /** + * Returns the defective state of the originating sensor for this variable. + * + * @return true if the sensor is defective + */ + public boolean isSensorDefective() { + return nativeValue == 0x7f00; + } + + /** + * Returns the configuration state of the variable. + * + * @return true if the variable is configured via LCN-PRO + */ + public boolean isConfigured() { + return this.nativeValue != 0xFFFF; + } + + public State getState(Variable variable) { + State stateValue; + if (variable.useLcnSpecialValues() && isSensorDefective()) { + stateValue = new StringType(SENSOR_DEFECTIVE_STATE); + } else if (variable.useLcnSpecialValues() && !isConfigured()) { + stateValue = new StringType("Not configured in LCN-PRO"); + } else { + stateValue = new DecimalType(toNative(variable.useLcnSpecialValues())); + } + return stateValue; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java new file mode 100644 index 0000000000000..2df88475bf583 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java @@ -0,0 +1,93 @@ +/** + * 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.connection; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.Channel; +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnAddr; +import org.openhab.binding.lcn.internal.common.LcnDefs; + +/** + * Base class for representing LCN-PCK gateway connection states + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public abstract class AbstractConnectionState extends AbstractState { + /** The PCk gateway's Connection */ + protected Connection connection; + /** An openHAB scheduler */ + protected ScheduledExecutorService scheduler; + + public AbstractConnectionState(StateContext context, ScheduledExecutorService scheduler) { + super(context); + this.connection = context.getConnection(); + this.scheduler = scheduler; + } + + /** + * Callback method when a PCK message has been received. + * + * @param data the received PCK message without line termination character + */ + public abstract void onPckMessageReceived(String data); + + /** + * Enqueues a PCK message to be sent. When the connection is offline, the message will be buffered and sent when the + * connection is established. When the enqueued PCK message is too old, it will be discarded before a new connection + * is established. + * + * @param addr the module's address to which is message shall be sent + * @param wantsAck true, if the module shall respond with an Ack upon successful processing + * @param data the PCK message to be sent + */ + public abstract void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data); + + /** + * Shuts the Connection down finally. A shut-down connection cannot re-used. + */ + public void shutdownFinally() { + nextState(ConnectionStateShutdown.class); + } + + /** + * Checks if the given PCK message is an LCN bus disconnect message. If so, openHAB will be informed and the + * Connection's State Machine waits for a re-connect. + * + * @param pck the PCK message to check + */ + protected void parseLcnBusDiconnectMessage(String pck) { + if (pck.equals(LcnDefs.LCNCONNSTATE_DISCONNECTED)) { + connection.getCallback().onOffline("LCN bus not connected to LCN-PCHK/PKE"); + nextState(ConnectionStateWaitForLcnBusConnectedAfterDisconnected.class); + } + } + + /** + * Closes the Connection SocketChannel. + */ + protected void closeSocketChannel() { + try { + Channel socketChannel = connection.getSocketChannel(); + if (socketChannel != null) { + socketChannel.close(); + } + } catch (IOException e) { + // ignore + } + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java new file mode 100644 index 0000000000000..595662e4f262e --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java @@ -0,0 +1,56 @@ +/** + * 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.connection; + +import java.nio.ByteBuffer; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnAddr; +import org.openhab.binding.lcn.internal.common.LcnException; + +/** + * Base class for sends username or password. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public abstract class AbstractConnectionStateSendCredentials extends AbstractConnectionState { + private static final int AUTH_TIMEOUT_SEC = 10; + + public AbstractConnectionStateSendCredentials(StateContext context, ScheduledExecutorService scheduler) { + super(context, scheduler); + } + + @Override + public void startWorking() { + addTimer(scheduler.schedule(() -> nextState(ConnectionStateConnecting.class), AUTH_TIMEOUT_SEC, + TimeUnit.SECONDS)); + } + + /** + * Starts a timeout when the PCK gateway does not answer to the credentials. + */ + protected void startTimeoutTimer() { + addTimer(scheduler.schedule( + () -> context.handleConnectionFailed( + new LcnException("Network timeout in state " + getClass().getSimpleName())), + connection.getSettings().getTimeout(), TimeUnit.MILLISECONDS)); + } + + @Override + public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + connection.queueOffline(addr, wantsAck, data); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java new file mode 100644 index 0000000000000..bc58217ae5e33 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java @@ -0,0 +1,69 @@ +/** + * 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.connection; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ScheduledFuture; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Base class for usage for states with {@link StateMachine}. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public abstract class AbstractState { + private List> usedTimers = new ArrayList<>(); + protected StateContext context; + + public AbstractState(StateContext context) { + this.context = context; + } + + /** + * Must be invoked when the State shall start its actions. + */ + abstract void startWorking(); + + /** + * Stops all timers, the State has been started. + */ + void cancelAllTimers() { + List> copy = new ArrayList<>(usedTimers); + + copy.forEach(t -> t.cancel(true)); + } + + /** + * When a state starts a timer, its ScheduledFuture must be added by this method. All timers added by this method, + * are canceled when the StateMachine leaves this State. + * + * @param timer the new timer + */ + void addTimer(ScheduledFuture timer) { + usedTimers.add(timer); + } + + /** + * Sets a new State. The current state is torn down gracefully. + * + * @param newStateClass the class of the new State + */ + synchronized void nextState(Class newStateClass) { + if (context.isStateActive(this)) { + context.setState(newStateClass); + } + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java new file mode 100644 index 0000000000000..65547810a63f7 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java @@ -0,0 +1,505 @@ +/** + * 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.connection; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.Channel; +import java.nio.channels.CompletionHandler; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.lcn.internal.common.LcnAddr; +import org.openhab.binding.lcn.internal.common.LcnAddrGrp; +import org.openhab.binding.lcn.internal.common.LcnAddrMod; +import org.openhab.binding.lcn.internal.common.LcnDefs; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.PckGenerator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents a configured connection to one LCN-PCHK. + * It uses a {@link AsynchronousSocketChannel} to connect to LCN-PCHK. + * Included logic: + *

      + *
    • Reconnection on connection loss + *
    • Segment scan (to detect the local segment ID) + *
    • Acknowledge handling + *
    • Periodic value requests + *
    • Caching of runtime data about the underlying LCN bus + *
    + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class Connection { + private final Logger logger = LoggerFactory.getLogger(Connection.class); + /** Max. lengths of a PCK string including address and line feed. Currently dynamic text (GTDT) */ + private static final int MAX_PCK_STRING_LENGTH = 34; + private static final int BROADCAST_MODULE_ID = 3; + private static final int BROADCAST_SEGMENT_ID = 3; + private final ConnectionSettings settings; + private final ConnectionCallback callback; + @Nullable + private AsynchronousSocketChannel channel; + /** The local segment id. -1 means "unknown". */ + private int localSegId; + private final ByteBuffer readBuffer = ByteBuffer.allocate(1024); + private final ByteBuffer sendBuffer = ByteBuffer.allocate(MAX_PCK_STRING_LENGTH); + private final LinkedBlockingQueue<@Nullable SendData> sendQueue = new LinkedBlockingQueue<>(); + private final LinkedBlockingQueue<@Nullable PckQueueItem> offlineSendQueue = new LinkedBlockingQueue<>(); + private final Map modData = Collections.synchronizedMap(new HashMap<>()); + private boolean writeInProgress; + private ScheduledExecutorService scheduler; + private StateMachine stateMachine; + + /** + * Constructs a clean (disconnected) connection with the given settings. + * This does not start the actual connection process. + * + * @param sets the settings to use for the new connection + * @param callback the callback to the owner + * @throws IOException + */ + public Connection(ConnectionSettings sets, ScheduledExecutorService scheduler, ConnectionCallback callback) + throws IOException { + this.settings = sets; + this.callback = callback; + this.scheduler = scheduler; + this.clearRuntimeData(); + + stateMachine = new StateMachine(this, scheduler); + stateMachine.startWorking(); + } + + /** Clears all runtime data. */ + void clearRuntimeData() { + this.channel = null; + this.localSegId = -1; + this.readBuffer.clear(); + this.sendQueue.clear(); + this.sendBuffer.clear(); + } + + /** + * Retrieves the settings for this connection (never changed). + * + * @return the settings + */ + public ConnectionSettings getSettings() { + return this.settings; + } + + private boolean isSocketConnected() { + try { + AsynchronousSocketChannel localChannel = channel; + return localChannel != null && localChannel.getRemoteAddress() != null; + } catch (IOException e) { + return false; + } + } + + /** + * Sets the local segment id. + * + * @param localSegId the new local segment id + */ + public void setLocalSegId(int localSegId) { + this.localSegId = localSegId; + } + + /** + * Called whenever an acknowledge is received. + * + * @param addr the source LCN module + * @param code the LCN internal code (-1 = "positive") + */ + public void onAck(LcnAddrMod addr, int code) { + ModInfo info = this.modData.get(addr); + if (info != null) { + info.onAck(code, this, this.settings.getTimeout(), System.nanoTime()); + } + } + + /** + * Creates and/or returns cached data for the given LCN module. + * + * @param addr the module's address + * @return the data (never null) + */ + public ModInfo updateModuleData(LcnAddrMod addr) { + synchronized (modData) { + ModInfo data = this.modData.get(addr); + if (data == null) { + data = new ModInfo(addr); + this.modData.put(addr, data); + } + return data; + } + } + + /** + * Reads and processes input from the underlying channel. + * Fragmented input is kept in {@link #readBuffer} and will be processed with the next call. + * + * @throws IOException if connection was closed or a generic channel error occurred + */ + void readAndProcess() { + AsynchronousSocketChannel localChannel = channel; + if (localChannel != null && isSocketConnected()) { + localChannel.read(readBuffer, null, new CompletionHandler<@Nullable Integer, @Nullable Void>() { + @Override + public void completed(@Nullable Integer transmittedByteCount, @Nullable Void attachment) { + synchronized (Connection.this) { + if (transmittedByteCount == null || transmittedByteCount == -1) { + String msg = "Connection was closed by foreign host."; + logger.debug(msg); + stateMachine.handleConnectionFailed(new LcnException(msg)); + } else { + try { + readBuffer.flip(); + int aPos = readBuffer.position(); // 0 + String s = new String(readBuffer.array(), aPos, transmittedByteCount, + LcnDefs.LCN_ENCODING); + int pos1 = 0, pos2 = s.indexOf(PckGenerator.TERMINATION, pos1); + while (pos2 != -1) { + String data = s.substring(pos1, pos2); + if (logger.isTraceEnabled()) { + logger.trace("Received: '{}'", data); + } + scheduler.submit(() -> { + stateMachine.onInputReceived(data); + callback.onPckMessageReceived(data); + }); + // Seek position in input array + aPos += s.substring(pos1, pos2 + 1).getBytes(LcnDefs.LCN_ENCODING).length; + // Next input + pos1 = pos2 + 1; + pos2 = s.indexOf(PckGenerator.TERMINATION, pos1); + } + readBuffer.limit(readBuffer.capacity()); + readBuffer.position(transmittedByteCount - aPos); // Keeps fragments for the next call + } catch (UnsupportedEncodingException ex) { + logger.warn("Unable to decode input from channel \"{}\": {}", settings.getId(), + ex.getMessage()); + } + + if (isSocketConnected()) { + readAndProcess(); + } + } + } + } + + @Override + public void failed(@Nullable Throwable e, @Nullable Void attachment) { + logger.debug("Lost connection"); + stateMachine.handleConnectionFailed(e); + } + }); + } else { + stateMachine.handleConnectionFailed(new LcnException("Socket not open")); + } + } + + /** + * Writes all queued data. + * Will try to write all data at once to reduce overhead. + */ + public synchronized void triggerWriteToSocket() { + AsynchronousSocketChannel localChannel = channel; + if (localChannel == null || !isSocketConnected() || writeInProgress) { + return; + } + sendBuffer.clear(); + SendData item = sendQueue.poll(); + + if (item != null) { + try { + if (!item.write(sendBuffer, localSegId)) { + logger.warn("Data loss: Could not write packet into send buffer"); + } + + writeInProgress = true; + sendBuffer.flip(); + localChannel.write(sendBuffer, null, new CompletionHandler<@Nullable Integer, @Nullable Void>() { + @Override + public void completed(@Nullable Integer result, @Nullable Void attachment) { + synchronized (Connection.this) { + if (result != sendBuffer.limit()) { + logger.warn("Data loss while writing to channel: {}", settings.getAddress()); + } else { + if (logger.isTraceEnabled()) { + logger.trace("Sent: {}", new String(sendBuffer.array(), 0, sendBuffer.limit())); + } + } + + writeInProgress = false; + + if (sendQueue.size() > 0) { + /** + * This could lead to stack overflows, since the CompletionHandler may run in the same + * Thread as triggerWriteToSocket() is invoked (see + * {@link AsynchronousChannelGroup}/Threading), but we do not expect as much data + * in one chunk here, that the stack can be filled in a critical way. + */ + triggerWriteToSocket(); + } + } + } + + @Override + public void failed(@Nullable Throwable exc, @Nullable Void attachment) { + synchronized (Connection.this) { + if (exc != null) { + logger.warn("Writing to channel \"{}\" failed: {}", settings.getAddress(), + exc.getMessage()); + } + writeInProgress = false; + stateMachine.handleConnectionFailed(new LcnException("write() failed")); + } + } + }); + } catch (UnsupportedEncodingException | BufferOverflowException e) { + logger.warn("Sending failed: {}: {}: {}", item, e.getClass().getSimpleName(), e.getMessage()); + } + } + } + + /** + * Queues plain text to be sent to LCN-PCHK. + * Sending will be done the next time {@link #triggerWriteToSocket()} is called. + * + * @param plainText the text + */ + public void queueDirectlyPlainText(String plainText) { + this.queueAndSend(new SendDataPlainText(plainText)); + } + + /** + * Queues a PCK command to be sent. + * + * @param addr the target LCN address + * @param wantsAck true to wait for acknowledge on receipt (should be false for group addresses) + * @param pck the pure PCK command (without address header) + */ + void queueDirectly(LcnAddr addr, boolean wantsAck, String pck) { + try { + this.queueDirectly(addr, wantsAck, ByteBuffer.wrap(pck.getBytes(LcnDefs.LCN_ENCODING))); + } catch (UnsupportedEncodingException ex) { + logger.error("Failed to encode PCK command: {}", pck); + } + } + + /** + * Queues a PCK command for immediate sending, regardless of the Connection state. The PCK command is automatically + * re-sent if the destination is not a group, an Ack is requested and the module did not answer within the expected + * time. + * + * @param addr the target LCN address + * @param wantsAck true to wait for acknowledge on receipt (should be false for group addresses) + * @param data the pure PCK command (without address header) + */ + void queueDirectly(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + if (!addr.isGroup() && wantsAck) { + this.updateModuleData((LcnAddrMod) addr).queuePckCommandWithAck(data, this, this.settings.getTimeout(), + System.nanoTime()); + } else { + this.queueAndSend(new SendDataPck(addr, false, data)); + } + } + + /** + * Enqueues a raw PCK command and triggers the socket to start sending, if it does not already. Does not take care + * of any Acks. + * + * @param data raw PCK command + */ + synchronized void queueAndSend(SendData data) { + this.sendQueue.add(data); + + triggerWriteToSocket(); + } + + /** + * Enqueues a PCK command to the offline queue. Data will be sent when the Connection state will enter + * {@link ConnectionStateConnected}. + * + * @param addr LCN module address + * @param wantsAck true, if the LCN module shall respond with an Ack on successful processing + * @param data the pure PCK command (without address header) + */ + void queueOffline(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + offlineSendQueue.add(new PckQueueItem(addr, wantsAck, data)); + } + + /** + * Enqueues a PCK command for sending. Takes care of the Connection state and buffers the command for a specific + * time if the Connection is not ready. If an Ack is requested, the PCK command is automatically + * re-sent, if the module did not answer in the expected time. + * + * @param addr LCN module address + * @param wantsAck true, if the LCN module shall respond with an Ack on successful processing + * @param pck the pure PCK command (without address header) + */ + public void queue(LcnAddr addr, boolean wantsAck, String pck) { + try { + this.queue(addr, wantsAck, ByteBuffer.wrap(pck.getBytes(LcnDefs.LCN_ENCODING))); + } catch (UnsupportedEncodingException ex) { + logger.warn("Failed to encode PCK command: {}", pck); + } + } + + /** + * Enqueues a PCK command for sending. Takes care of the Connection state and buffers the command for a specific + * time if the Connection is not ready. If an Ack is requested, the PCK command is automatically + * re-sent, if the module did not answer in the expected time. + * + * @param addr LCN module address + * @param wantsAck true, if the LCN module shall respond with an Ack on successful processing + * @param pck the pure PCK command (without address header) + */ + public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer pck) { + stateMachine.queue(addr, wantsAck, pck); + } + + /** + * Process the offline PCK command queue. Does only send recently enqueued PCK commands, the rest is discarded. + */ + void sendOfflineQueue() { + // don't use forEach(), because elements can be added during iteration + while (!offlineSendQueue.isEmpty()) { + PckQueueItem item = offlineSendQueue.poll(); + + if (item == null) { + break; + } + + // only send messages that were enqueued recently, discard older messages + long timeout = settings.getTimeout(); + if (item.getEnqueued().isAfter(Instant.now().minus(timeout * 4, ChronoUnit.MILLIS))) { + queueDirectly(item.getAddr(), item.isWantsAck(), item.getData()); + } + } + } + + /** + * Gets the Connection's callback. + * + * @return the callback + */ + public ConnectionCallback getCallback() { + return callback; + } + + /** + * Sets the SocketChannel of this Connection + * + * @param channel the new Channel + */ + public void setSocketChannel(AsynchronousSocketChannel channel) { + this.channel = channel; + } + + /** + * Gets the SocketChannel of the Connection. + * + * @returnthe socket channel + */ + @Nullable + public Channel getSocketChannel() { + return channel; + } + + /** + * Gets the local segment ID. When no segments are used, the local segment ID is 0. + * + * @return the local segment ID + */ + public int getLocalSegId() { + return localSegId; + } + + /** + * Runs the periodic updates on all ModInfos. + */ + public void updateModInfos() { + synchronized (modData) { + for (ModInfo info : modData.values()) { + if (info != null) { + info.update(this, settings.getTimeout(), System.nanoTime()); + } + } + } + } + + /** + * Removes an LCN module from the ModData list. + * + * @param addr the module's address to be removed + */ + public void removeLcnModule(LcnAddr addr) { + modData.remove(addr); + } + + /** + * Invoked when this Connection shall be shut-down finally. + */ + public void shutdown() { + stateMachine.shutdownFinally(); + } + + /** + * Sends a broadcast to all LCN modules with a reuqest to respond with an Ack. + */ + public void sendModuleDiscoveryCommand() { + try { + queueAndSend(new SendDataPck(new LcnAddrGrp(BROADCAST_SEGMENT_ID, BROADCAST_MODULE_ID), true, + ByteBuffer.wrap(PckGenerator.nullCommand().getBytes(LcnDefs.LCN_ENCODING)))); + queueAndSend(new SendDataPck(new LcnAddrGrp(0, BROADCAST_MODULE_ID), true, + ByteBuffer.wrap(PckGenerator.nullCommand().getBytes(LcnDefs.LCN_ENCODING)))); + } catch (UnsupportedEncodingException e) { + logger.warn("Could not send discovery request: {}", e.getMessage()); + } + } + + /** + * Requests the serial number and the firmware version of the given LCN module. + * + * @param addr module's address + */ + public void sendSerialNumberRequest(LcnAddrMod addr) { + queueDirectly(addr, false, PckGenerator.requestSn()); + } + + /** + * Requests theprogrammed name of the given LCN module. + * + * @param addr module's address + */ + public void sendModuleNameRequest(LcnAddrMod addr) { + queueDirectly(addr, false, PckGenerator.requestModuleName(0)); + queueDirectly(addr, false, PckGenerator.requestModuleName(1)); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionCallback.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionCallback.java new file mode 100644 index 0000000000000..0a2b116250453 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionCallback.java @@ -0,0 +1,44 @@ +/** + * 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.connection; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Handles events from the connection to the LCN-PCK gateway. + * + * @author Tobias Jüttner - Initial Contribution + * @author Fabian Wolter - Migration to OH2 + */ +@NonNullByDefault +public interface ConnectionCallback { + /** + * Invoked when the Connection to the PCK gateway is established and the LCN bus is connected to the PCK gateway. + */ + void onOnline(); + + /** + * Invoked when the Connection to the PCK gateway has been closed or when the LCN bus is disconnected from the PCK + * gateway. + * + * @param errorMessage the reason + */ + void onOffline(String errorMessage); + + /** + * Invoked when a PCK message has been reived from the PCK gateway. + * + * @param message the received message + */ + void onPckMessageReceived(String message); +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionSettings.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionSettings.java new file mode 100644 index 0000000000000..117c42539556a --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionSettings.java @@ -0,0 +1,159 @@ +/** + * 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.connection; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.lcn.internal.common.LcnDefs; + +/** + * Settings for a connection to LCN-PCHK. + * + * @author Tobias Jüttner - Initial Contribution + */ +@NonNullByDefault +public class ConnectionSettings { + + /** Unique identifier for this connection. */ + private final String id; + + /** The user name for authentication. */ + private final String username; + + /** The password for authentication. */ + private final String password; + + /** The TCP/IP address or IP of the connection. */ + private final String address; + + /** The TCP/IP port of the connection. */ + private final int port; + + /** The dimming mode to use. */ + private final LcnDefs.OutputPortDimMode dimMode; + + /** The status-messages mode to use. */ + private final LcnDefs.OutputPortStatusMode statusMode; + + /** Timeout for requests. */ + private final long timeoutMSec; + + /** + * Constructor. + * + * @param id the connnection's unique identifier + * @param address the connection's TCP/IP address or IP + * @param port the connection's TCP/IP port + * @param username the user name for authentication + * @param password the password for authentication + * @param dimMode the dimming mode + * @param statusMode the status-messages mode + * @param timeout the request timeout + */ + public ConnectionSettings(String id, String address, int port, String username, String password, + LcnDefs.OutputPortDimMode dimMode, LcnDefs.OutputPortStatusMode statusMode, int timeout) { + this.id = id; + this.address = address; + this.port = port; + this.username = username; + this.password = password; + this.dimMode = dimMode; + this.statusMode = statusMode; + this.timeoutMSec = timeout; + } + + /** + * Gets the unique identifier for the connection. + * + * @return the unique identifier + */ + public String getId() { + return this.id; + } + + /** + * Gets the user name used for authentication. + * + * @return the user name + */ + public String getUsername() { + return this.username; + } + + /** + * Gets the password used for authentication. + * + * @return the password + */ + public String getPassword() { + return this.password; + } + + /** + * Gets the TCP/IP address or IP of the connection. + * + * @return the address or IP + */ + public String getAddress() { + return this.address; + } + + /** + * Gets the TCP/IP port of the connection. + * + * @return the port + */ + public int getPort() { + return this.port; + } + + /** + * Gets the dimming mode to use for the connection. + * + * @return the dimming mode + */ + public LcnDefs.OutputPortDimMode getDimMode() { + return this.dimMode; + } + + /** + * Gets the status-messages mode to use for the connection. + * + * @return the status-messages mode + */ + public LcnDefs.OutputPortStatusMode getStatusMode() { + return this.statusMode; + } + + /** + * Gets the request timeout. + * + * @return the timeout in milliseconds + */ + public long getTimeout() { + return this.timeoutMSec; + } + + @Override + public boolean equals(@Nullable Object o) { + if (!(o instanceof ConnectionSettings)) { + return false; + } + ConnectionSettings other = (ConnectionSettings) o; + return this.id.equals(other.id) && this.address.equals(other.address) && this.port == other.port + && this.username.equals(other.username) && this.password.equals(other.password) + && this.dimMode == other.dimMode && this.statusMode == other.statusMode + && this.timeoutMSec == other.timeoutMSec; + } + +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnected.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnected.java new file mode 100644 index 0000000000000..aa0072e7840fa --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnected.java @@ -0,0 +1,60 @@ +/** + * 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.connection; + +import java.nio.ByteBuffer; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnAddr; +import org.openhab.binding.lcn.internal.common.PckGenerator; + +/** + * This state is active when the connection to the LCN bus has been established successfully and data can be sent and + * retrieved. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class ConnectionStateConnected extends AbstractConnectionState { + private static final int PING_INTERVAL_SEC = 60; + private int pingCounter; + + public ConnectionStateConnected(StateContext context, ScheduledExecutorService scheduler) { + super(context, scheduler); + } + + @Override + public void startWorking() { + // send periodic keep-alives to keep the connection open + addTimer(scheduler.scheduleWithFixedDelay( + () -> connection.queueDirectlyPlainText(PckGenerator.ping(++pingCounter)), PING_INTERVAL_SEC, + PING_INTERVAL_SEC, TimeUnit.SECONDS)); + + // run ModInfo.update() for every LCN module + addTimer(scheduler.scheduleWithFixedDelay(connection::updateModInfos, 0, 1, TimeUnit.SECONDS)); + + connection.sendOfflineQueue(); + } + + @Override + public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + connection.queueDirectly(addr, wantsAck, data); + } + + @Override + public void onPckMessageReceived(String data) { + parseLcnBusDiconnectMessage(data); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java new file mode 100644 index 0000000000000..2ae808988ea18 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java @@ -0,0 +1,105 @@ +/** + * 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.connection; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.StandardSocketOptions; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.lcn.internal.common.LcnAddr; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This state is active during the socket creation, host name resolving and waiting for the TCP connection to become + * established. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class ConnectionStateConnecting extends AbstractConnectionState { + private final Logger logger = LoggerFactory.getLogger(ConnectionStateConnecting.class); + + public ConnectionStateConnecting(StateContext context, ScheduledExecutorService scheduler) { + super(context, scheduler); + } + + @Override + public void startWorking() { + connection.clearRuntimeData(); + + logger.debug("Connecting to {}:{} ...", connection.getSettings().getAddress(), + connection.getSettings().getPort()); + + try { + // Open Channel by using the system-wide default AynchronousChannelGroup. + // So, Threads are used or re-used on demand. + AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(); + // Do not wait until some buffer is filled, send PCK commands immediately + channel.setOption(StandardSocketOptions.TCP_NODELAY, true); + connection.setSocketChannel(channel); + + InetSocketAddress address = new InetSocketAddress(connection.getSettings().getAddress(), + connection.getSettings().getPort()); + + if (address.isUnresolved()) { + throw new LcnException("Could not resolve hostname"); + } + + channel.connect(address, null, new CompletionHandler<@Nullable Void, @Nullable Void>() { + @Override + public void completed(@Nullable Void result, @Nullable Void attachment) { + connection.readAndProcess(); + nextState(ConnectionStateSendUsername.class); + } + + @Override + public void failed(@Nullable Throwable e, @Nullable Void attachment) { + handleConnectionFailure(e); + } + }); + } catch (IOException | LcnException e) { + handleConnectionFailure(e); + } + } + + private void handleConnectionFailure(@Nullable Throwable e) { + String message; + if (e != null) { + logger.warn("Could not connect to {}:{}: {}", connection.getSettings().getAddress(), + connection.getSettings().getPort(), e.getMessage()); + message = e.getMessage(); + } else { + message = ""; + } + connection.getCallback().onOffline(message); + context.handleConnectionFailed(e); + } + + @Override + public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + connection.queueOffline(addr, wantsAck, data); + } + + @Override + public void onPckMessageReceived(String data) { + // nothing + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java new file mode 100644 index 0000000000000..b8189be5a2d68 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java @@ -0,0 +1,53 @@ +/** + * 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.connection; + +import java.nio.ByteBuffer; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnAddr; + +/** + * This state is active when the connection failed. A grace period is enforced to prevent fast cycling through the + * states. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class ConnectionStateGracePeriodBeforeReconnect extends AbstractConnectionState { + private static final int RECONNECT_GRACE_PERIOD_SEC = 5; + + public ConnectionStateGracePeriodBeforeReconnect(StateContext context, ScheduledExecutorService scheduler) { + super(context, scheduler); + } + + @Override + public void startWorking() { + closeSocketChannel(); + + addTimer(scheduler.schedule(() -> nextState(ConnectionStateConnecting.class), RECONNECT_GRACE_PERIOD_SEC, + TimeUnit.SECONDS)); + } + + @Override + public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + connection.queueOffline(addr, wantsAck, data); + } + + @Override + public void onPckMessageReceived(String data) { + // nothing + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java new file mode 100644 index 0000000000000..93ce70b951fbe --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java @@ -0,0 +1,46 @@ +/** + * 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.connection; + +import java.nio.ByteBuffer; +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnAddr; + +/** + * This is the starting state of the {@link Connection} {@link StateMachine}. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class ConnectionStateInit extends AbstractConnectionState { + public ConnectionStateInit(StateContext context, ScheduledExecutorService scheduler) { + super(context, scheduler); + } + + @Override + public void startWorking() { + nextState(ConnectionStateConnecting.class); + } + + @Override + public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + connection.queueOffline(addr, wantsAck, data); + } + + @Override + public void onPckMessageReceived(String data) { + // nothing + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java new file mode 100644 index 0000000000000..d1b8113d702ba --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java @@ -0,0 +1,90 @@ +/** + * 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.connection; + +import java.nio.ByteBuffer; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnAddr; +import org.openhab.binding.lcn.internal.common.LcnAddrGrp; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.PckGenerator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This state discovers the LCN segment couplers. + * + * After the authorization against the LCN-PCK gateway was successful, the LCN segment couplers are discovery, to + * retrieve the segment ID of the local segment. When no segment couplers were found, a timeout sets the local segment + * ID to 0. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class ConnectionStateSegmentScan extends AbstractConnectionState { + private final Logger logger = LoggerFactory.getLogger(ConnectionStateSegmentScan.class); + public static final Pattern PATTERN_SK_RESPONSE = Pattern + .compile("=M(?\\d{3})(?\\d{3})\\.SK(?\\d+)"); + private final RequestStatus statusSegmentScan = new RequestStatus(-1, 3, "Segment Scan"); + + public ConnectionStateSegmentScan(StateContext context, ScheduledExecutorService scheduler) { + super(context, scheduler); + } + + @Override + public void startWorking() { + statusSegmentScan.refresh(); + addTimer(scheduler.scheduleWithFixedDelay(this::update, 0, 500, TimeUnit.MILLISECONDS)); + } + + private void update() { + long currTime = System.nanoTime(); + try { + if (statusSegmentScan.shouldSendNextRequest(connection.getSettings().getTimeout(), currTime)) { + connection.queueDirectly(new LcnAddrGrp(3, 3), false, PckGenerator.segmentCouplerScan()); + statusSegmentScan.onRequestSent(currTime); + } + } catch (LcnException e) { + // Give up. Probably no segments available. + connection.setLocalSegId(0); + logger.debug("No segment couplers detected"); + nextState(ConnectionStateConnected.class); + } + } + + @Override + public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + connection.queueOffline(addr, wantsAck, data); + } + + @Override + public void onPckMessageReceived(String data) { + Matcher matcher = PATTERN_SK_RESPONSE.matcher(data); + + if (matcher.matches()) { + // any segment coupler answered + if (Integer.parseInt(matcher.group("segId")) == 0) { + // local segment coupler answered + connection.setLocalSegId(Integer.parseInt(matcher.group("id"))); + logger.debug("Local segment ID is {}", connection.getLocalSegId()); + nextState(ConnectionStateConnected.class); + } + } + parseLcnBusDiconnectMessage(data); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java new file mode 100644 index 0000000000000..1c0e7b649b672 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java @@ -0,0 +1,50 @@ +/** + * 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.connection; + +import java.nio.ByteBuffer; +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnAddr; +import org.openhab.binding.lcn.internal.common.PckGenerator; + +/** + * Sets the dimming mode range (0-50 or 0-200) in the LCN-PCK for this connection, as configured by the user. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class ConnectionStateSendDimMode extends AbstractConnectionState { + public ConnectionStateSendDimMode(StateContext context, ScheduledExecutorService scheduler) { + super(context, scheduler); + } + + @Override + public void startWorking() { + connection.queueDirectlyPlainText(PckGenerator.setOperationMode(connection.getSettings().getDimMode(), + connection.getSettings().getStatusMode())); + + nextState(ConnectionStateSegmentScan.class); + } + + @Override + public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + connection.queueOffline(addr, wantsAck, data); + } + + @Override + public void onPckMessageReceived(String data) { + // nothing + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendPassword.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendPassword.java new file mode 100644 index 0000000000000..5a18071bec7db --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendPassword.java @@ -0,0 +1,43 @@ +/** + * 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.connection; + +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnDefs; + +/** + * This state sends the password during the authentication with the LCN-PCK gateway. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class ConnectionStateSendPassword extends AbstractConnectionStateSendCredentials { + public ConnectionStateSendPassword(StateContext context, ScheduledExecutorService scheduler) { + super(context, scheduler); + } + + @Override + public void startWorking() { + startTimeoutTimer(); + } + + @Override + public void onPckMessageReceived(String data) { + if (data.equals(LcnDefs.AUTH_PASSWORD)) { + connection.queueDirectlyPlainText(connection.getSettings().getPassword()); + nextState(ConnectionStateWaitForLcnBusConnected.class); + } + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendUsername.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendUsername.java new file mode 100644 index 0000000000000..0999f088fe48c --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendUsername.java @@ -0,0 +1,43 @@ +/** + * 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.connection; + +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnDefs; + +/** + * This state sends the username during the authentication with the LCN-PCK gateway. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class ConnectionStateSendUsername extends AbstractConnectionStateSendCredentials { + public ConnectionStateSendUsername(StateContext context, ScheduledExecutorService scheduler) { + super(context, scheduler); + } + + @Override + public void startWorking() { + startTimeoutTimer(); + } + + @Override + public void onPckMessageReceived(String data) { + if (data.equals(LcnDefs.AUTH_USERNAME)) { + connection.queueDirectlyPlainText(connection.getSettings().getUsername()); + nextState(ConnectionStateSendPassword.class); + } + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateShutdown.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateShutdown.java new file mode 100644 index 0000000000000..7cba533d4a8ec --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateShutdown.java @@ -0,0 +1,48 @@ +/** + * 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.connection; + +import java.nio.ByteBuffer; +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnAddr; + +/** + * This state is entered when the connection shall be shut-down finally. This happens when Thing.dispose() is called. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class ConnectionStateShutdown extends AbstractConnectionState { + public ConnectionStateShutdown(StateContext context, ScheduledExecutorService scheduler) { + super(context, scheduler); + } + + @Override + public void startWorking() { + closeSocketChannel(); + + // end state + } + + @Override + public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + // nothing + } + + @Override + public void onPckMessageReceived(String data) { + // nothing + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java new file mode 100644 index 0000000000000..820dc7a52e049 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java @@ -0,0 +1,70 @@ +/** + * 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.connection; + +import java.nio.ByteBuffer; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnAddr; +import org.openhab.binding.lcn.internal.common.LcnDefs; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.NullScheduledFuture; + +/** + * This state waits for the status answer of the LCN-PCK gateway after connection establishment, rather the LCN bus is + * connected. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class ConnectionStateWaitForLcnBusConnected extends AbstractConnectionState { + private ScheduledFuture legacyTimer = NullScheduledFuture.getInstance(); + + public ConnectionStateWaitForLcnBusConnected(StateContext context, ScheduledExecutorService scheduler) { + super(context, scheduler); + } + + @Override + public void startWorking() { + // Legacy support for LCN-PCHK 2.2 and earlier: + // There was no explicit "LCN connected" notification after successful authentication. + // Only "LCN disconnected" would be reported immediately. That means "LCN connected" used to be the default. + addTimer(legacyTimer = scheduler.schedule(() -> { + connection.getCallback().onOnline(); + nextState(ConnectionStateSendDimMode.class); + }, connection.getSettings().getTimeout(), TimeUnit.MILLISECONDS)); + } + + @Override + public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + connection.queueOffline(addr, wantsAck, data); + } + + @Override + public void onPckMessageReceived(String data) { + if (data.equals(LcnDefs.LCNCONNSTATE_DISCONNECTED)) { + legacyTimer.cancel(true); + connection.getCallback().onOffline("LCN bus not connected to LCN-PCHK/PKE"); + } else if (data.equals(LcnDefs.LCNCONNSTATE_CONNECTED)) { + legacyTimer.cancel(true); + connection.getCallback().onOnline(); + nextState(ConnectionStateSendDimMode.class); + } else if (data.equals(LcnDefs.INSUFFICIENT_LICENSES)) { + context.handleConnectionFailed( + new LcnException("LCN-PCHK/PKE has not enough licenses to handle this connection")); + } + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnectedAfterDisconnected.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnectedAfterDisconnected.java new file mode 100644 index 0000000000000..5a7d7c3a5c767 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnectedAfterDisconnected.java @@ -0,0 +1,36 @@ +/** + * 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.connection; + +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * This state is entered when the LCN-PCK gateway sent a message, that the connection to the LCN bus was lost. This can + * happen if the user plugs the USB cable to the PC coupler. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class ConnectionStateWaitForLcnBusConnectedAfterDisconnected extends ConnectionStateWaitForLcnBusConnected { + public ConnectionStateWaitForLcnBusConnectedAfterDisconnected(StateContext context, + ScheduledExecutorService scheduler) { + super(context, scheduler); + } + + @Override + public void startWorking() { + // nothing, don't start legacy timer + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java new file mode 100644 index 0000000000000..8ab99e2a1ce23 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java @@ -0,0 +1,501 @@ +/** + * 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.connection; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +import org.openhab.binding.lcn.internal.common.LcnAddrMod; +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.common.PckGenerator; +import org.openhab.binding.lcn.internal.common.Variable; +import org.openhab.binding.lcn.internal.common.VariableValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Holds data of an LCN module. + *
      + *
    • Stores the module's firmware version (if requested) + *
    • Manages the scheduling of status-requests + *
    • Manages the scheduling of acknowledged commands + *
    + * + * @author Tobias Jüttner - Initial Contribution + * @author Fabian Wolter - Migration to OH2 + */ +@NonNullByDefault +public class ModInfo { + private final Logger logger = LoggerFactory.getLogger(ModInfo.class); + /** Total number of request to sent before going into failed-state. */ + private static final int NUM_TRIES = 3; + + /** Poll interval for status values that automatically send their values on change. */ + private static final int MAX_STATUS_EVENTBASED_VALUEAGE_MSEC = 600000; + + /** Poll interval for status values that do not send their values on change (always polled). */ + private static final int MAX_STATUS_POLLED_VALUEAGE_MSEC = 30000; + + /** Status request delay after a command has been send which potentially changed that status. */ + private static final int STATUS_REQUEST_DELAY_AFTER_COMMAND_MSEC = 2000; + + /** The LCN module's address. */ + private final LcnAddrMod addr; + + /** Firmware date of the LCN module. -1 means "unknown". */ + private int firmwareVersion = -1; + + /** Firmware version request status. */ + private final RequestStatus requestFirmwareVersion = new RequestStatus(-1, NUM_TRIES, "Firmware Version"); + + /** Output-port request status (0..3). */ + private final ArrayList requestStatusOutputs = new ArrayList<>(); + + /** Relays request status (all 8). */ + private final RequestStatus requestStatusRelays = new RequestStatus(MAX_STATUS_EVENTBASED_VALUEAGE_MSEC, NUM_TRIES, + "Relays"); + + /** Binary-sensors request status (all 8). */ + private final RequestStatus requestStatusBinSensors = new RequestStatus(MAX_STATUS_EVENTBASED_VALUEAGE_MSEC, + NUM_TRIES, "Binary Sensors"); + + /** + * Variables request status. + * Lazy initialization: Will be filled once the firmware version is known. + */ + private final Map requestStatusVars = new TreeMap<>(); + + /** + * Caches the values of the variables, needed for changing the values. + */ + private final Map variableValue = new TreeMap<>(); + + /** LEDs and logic-operations request status (all 12+4). */ + private final RequestStatus requestStatusLedsAndLogicOps = new RequestStatus(MAX_STATUS_POLLED_VALUEAGE_MSEC, + NUM_TRIES, "LEDs and Logic"); + + /** Key lock-states request status (all tables, A-D). */ + private final RequestStatus requestStatusLockedKeys = new RequestStatus(MAX_STATUS_POLLED_VALUEAGE_MSEC, NUM_TRIES, + "Key Locks"); + + /** + * Holds the last LCN variable requested whose response will not contain the variable's type. + * {@link Variable#UNKNOWN} means there is currently no such request. + */ + private Variable lastRequestedVarWithoutTypeInResponse = Variable.UNKNOWN; + + /** + * List of queued PCK commands to be acknowledged by the LCN module. + * Commands are always without address header. + * Note that the first one might currently be "in progress". + */ + private final LinkedList<@Nullable ByteBuffer> pckCommandsWithAck = new LinkedList<>(); + + /** Status data for the currently processed {@link PckCommandWithAck}. */ + private final RequestStatus requestCurrentPckCommandWithAck = new RequestStatus(-1, NUM_TRIES, "Commands with Ack"); + + /** + * Constructor. + * + * @param addr the module's address + */ + public ModInfo(LcnAddrMod addr) { + this.addr = addr; + for (int i = 0; i < LcnChannelGroup.OUTPUT.getCount(); ++i) { + this.requestStatusOutputs + .add(new RequestStatus(MAX_STATUS_EVENTBASED_VALUEAGE_MSEC, NUM_TRIES, "Output " + (i + 1))); + } + + for (Variable var : Variable.values()) { + if (var != Variable.UNKNOWN) { + this.requestStatusVars.put(var, new RequestStatus(MAX_STATUS_POLLED_VALUEAGE_MSEC, NUM_TRIES, + var.getType() + " " + (var.getNumber() + 1))); + } + } + } + + /** + * Gets the last requested variable whose response will not contain the variables type. + * + * @return the "typeless" variable + */ + public Variable getLastRequestedVarWithoutTypeInResponse() { + return this.lastRequestedVarWithoutTypeInResponse; + } + + /** + * Sets the last requested variable whose response will not contain the variables type. + * + * @param var the "typeless" variable + */ + public void setLastRequestedVarWithoutTypeInResponse(Variable var) { + this.lastRequestedVarWithoutTypeInResponse = var; + } + + /** + * Queues a PCK command to be sent. + * It will request an acknowledge from the LCN module on receipt. + * If there is no response within the request timeout, the command is retried. + * + * @param data the PCK command to send (without address header) + * @param timeoutMSec the time to wait for a response before retrying a request + * @param currTime the current time stamp + */ + public void queuePckCommandWithAck(ByteBuffer data, Connection conn, long timeoutMSec, long currTime) { + this.pckCommandsWithAck.add(data); + // Try to process the new acknowledged command. Will do nothing if another one is still in progress. + this.tryProcessNextCommandWithAck(conn, timeoutMSec, currTime); + } + + /** + * Called whenever an acknowledge is received from the LCN module. + * + * @param code the LCN internal code. -1 means "positive" acknowledge + * @param timeoutMSec the time to wait for a response before retrying a request + * @param currTime the current time stamp + */ + public void onAck(int code, Connection conn, long timeoutMSec, long currTime) { + if (this.requestCurrentPckCommandWithAck.isActive()) { // Check if we wait for an ack. + this.pckCommandsWithAck.pollFirst(); + this.requestCurrentPckCommandWithAck.reset(); + // Try to process next acknowledged command + this.tryProcessNextCommandWithAck(conn, timeoutMSec, currTime); + } + } + + /** + * Sends the next acknowledged command from the queue. + * + * @param conn the {@link Connection} belonging to this {@link ModInfo} + * @param timeoutMSec the time to wait for a response before retrying a request + * @param currTime the current time stamp + * @return true if a new command was sent + * @throws LcnException when a command response timed out + */ + private boolean tryProcessNextCommandWithAck(Connection conn, long timeoutMSec, long currTime) { + // Use the chance to remove a failed command first + if (this.requestCurrentPckCommandWithAck.isFailed(timeoutMSec, currTime)) { + ByteBuffer failedCommand = this.pckCommandsWithAck.pollFirst(); + this.requestCurrentPckCommandWithAck.reset(); + + if (failedCommand != null) { + try { + logger.warn("{}: Module did not respond to command: {}", addr, + new String(failedCommand.array(), LcnDefs.LCN_ENCODING)); + } catch (UnsupportedEncodingException e) { + // ignore + } + } + } + // Peek new command + if (!this.pckCommandsWithAck.isEmpty() && !this.requestCurrentPckCommandWithAck.isActive()) { + this.requestCurrentPckCommandWithAck.nextRequestIn(0, currTime); + } + ByteBuffer command = this.pckCommandsWithAck.peekFirst(); + if (command == null) { + return false; + } + try { + if (requestCurrentPckCommandWithAck.shouldSendNextRequest(timeoutMSec, currTime)) { + conn.queueAndSend(new SendDataPck(addr, true, command)); + this.requestCurrentPckCommandWithAck.onRequestSent(currTime); + } + } catch (LcnException e) { + try { + logger.warn("{}: Could not send command: {}: {}", addr, + new String(command.array(), LcnDefs.LCN_ENCODING), e.getMessage()); + } catch (UnsupportedEncodingException e1) { + // ignore + } + } + return true; + } + + /** + * Triggers a request to retrieve the firmware version of the LCN module, if it is not known, yet. + */ + public void requestFirmwareVersion() { + if (firmwareVersion == -1) { + requestFirmwareVersion.refresh(); + } + } + + /** + * Used to check if the module has the measurement processing firmware (since Feb. 2013). + * + * @return if the module has at least 4 threshold registers and 12 variables + */ + public boolean hasExtendedMeasurementProcessing() { + if (firmwareVersion == -1) { + logger.warn("LCN module firmware version unknown"); + return false; + } + return firmwareVersion >= LcnBindingConstants.FIRMWARE_2013; + } + + /** + * Keeps the request logic active. + * Must be called periodically. + * + * @param conn the {@link Connection} belonging to this {@link ModInfo} + * @param timeoutMSec the time to wait for a response before retrying a request + * @param currTime the current time stamp + */ + void update(Connection conn, long timeoutMSec, long currTime) { + RequestStatus r; + try { + // Firmware request + if ((r = this.requestFirmwareVersion).shouldSendNextRequest(timeoutMSec, currTime)) { + conn.queue(this.addr, false, PckGenerator.requestSn()); + r.onRequestSent(currTime); + return; + } + // Output-port requests + for (int i = 0; i < 4; ++i) { + if ((r = this.requestStatusOutputs.get(i)).shouldSendNextRequest(timeoutMSec, currTime)) { + conn.queue(this.addr, false, PckGenerator.requestOutputStatus(i)); + r.onRequestSent(currTime); + return; + } + } + // Relays request + if ((r = this.requestStatusRelays).shouldSendNextRequest(timeoutMSec, currTime)) { + conn.queue(this.addr, false, PckGenerator.requestRelaysStatus()); + r.onRequestSent(currTime); + return; + } + // Binary-sensors request + if ((r = this.requestStatusBinSensors).shouldSendNextRequest(timeoutMSec, currTime)) { + conn.queue(this.addr, false, PckGenerator.requestBinSensorsStatus()); + r.onRequestSent(currTime); + return; + } + // Variable requests + if (this.firmwareVersion != -1) { // Firmware version is required + // Use the chance to remove a failed "typeless variable" request + if (this.lastRequestedVarWithoutTypeInResponse != Variable.UNKNOWN) { + if (this.requestStatusVars.get(this.lastRequestedVarWithoutTypeInResponse).isTimeout(timeoutMSec, + currTime)) { + this.lastRequestedVarWithoutTypeInResponse = Variable.UNKNOWN; + } + } + // Variables + for (Map.Entry kv : this.requestStatusVars.entrySet()) { + if ((r = kv.getValue()).shouldSendNextRequest(timeoutMSec, currTime)) { + // Detect if we can send immediately or if we have to wait for a "typeless" request first + boolean hasTypeInResponse = kv.getKey().hasTypeInResponse(this.firmwareVersion); + if (hasTypeInResponse || this.lastRequestedVarWithoutTypeInResponse == Variable.UNKNOWN) { + try { + conn.queue(this.addr, false, + PckGenerator.requestVarStatus(kv.getKey(), this.firmwareVersion)); + r.onRequestSent(currTime); + if (!hasTypeInResponse) { + this.lastRequestedVarWithoutTypeInResponse = kv.getKey(); + } + return; + } catch (LcnException ex) { + r.reset(); + } + } + } + } + } + // LEDs and logic-operations request + if ((r = this.requestStatusLedsAndLogicOps).shouldSendNextRequest(timeoutMSec, currTime)) { + conn.queue(this.addr, false, PckGenerator.requestLedsAndLogicOpsStatus()); + r.onRequestSent(currTime); + return; + } + // Key-locks request + if ((r = this.requestStatusLockedKeys).shouldSendNextRequest(timeoutMSec, currTime)) { + conn.queue(this.addr, false, PckGenerator.requestKeyLocksStatus()); + r.onRequestSent(currTime); + return; + } + // Try to send next acknowledged command. Will also detect failed ones. + this.tryProcessNextCommandWithAck(conn, timeoutMSec, currTime); + } catch (LcnException e) { + logger.warn("{}: Failed to receive status message: {}", addr, e.getMessage()); + } + } + + /** + * Gets the LCN module's firmware date. + * + * @return the date + */ + public int getFirmwareVersion() { + return this.firmwareVersion; + } + + /** + * Sets the LCN module's firmware date. + * + * @param firmwareVersion the date + */ + public void setFirmwareVersion(int firmwareVersion) { + this.firmwareVersion = firmwareVersion; + + requestFirmwareVersion.onResponseReceived(); + + // increase poll interval, if the LCN module sends status updates of a variable event-based + requestStatusVars.entrySet().stream().filter(e -> e.getKey().isEventBased(firmwareVersion)) + .forEach(e -> e.getValue().setMaxAgeMSec(MAX_STATUS_EVENTBASED_VALUEAGE_MSEC)); + } + + /** + * Updates the variable value cache. + * + * @param variable the variable to update + * @param value the new value + */ + public void updateVariableValue(Variable variable, VariableValue value) { + variableValue.put(variable, value); + } + + /** + * Gets the current value of a variable from the cache. + * + * @param variable the variable to retrieve the value for + * @return the value of the variable + * @throws LcnException when the variable is not in the cache + */ + public long getVariableValue(Variable variable) throws LcnException { + return Optional.ofNullable(variableValue.get(variable)).map(v -> v.toNative(variable.useLcnSpecialValues())) + .orElseThrow(() -> new LcnException("Current variable value unknown")); + } + + /** + * Requests the current value of all dimmer outputs. + */ + public void refreshAllOutputs() { + requestStatusOutputs.forEach(RequestStatus::refresh); + } + + /** + * Requests the current value of the given dimmer output. + * + * @param number 0..3 + */ + public void refreshOutput(int number) { + requestStatusOutputs.get(number).refresh(); + } + + /** + * Requests the current value of all relays. + */ + public void refreshRelays() { + requestStatusRelays.refresh(); + } + + /** + * Requests the current value of all binary sensor. + */ + public void refreshBinarySensors() { + requestStatusBinSensors.refresh(); + } + + /** + * Requests the current value of the given variable. + * + * @param variable the variable to request + */ + public void refreshVariable(Variable variable) { + requestStatusVars.get(variable).refresh(); + } + + /** + * Requests the current value of all LEDs and logic operations. + */ + public void refreshLedsAndLogic() { + requestStatusLedsAndLogicOps.refresh(); + } + + /** + * Requests the current value of all LEDs and logic operations, after a LED has been changed by openHAB. + */ + public void refreshStatusLedsAnLogicAfterChange() { + requestStatusLedsAndLogicOps.nextRequestIn(STATUS_REQUEST_DELAY_AFTER_COMMAND_MSEC, System.nanoTime()); + } + + /** + * Requests the current locking states of all keys. + */ + public void refreshStatusLockedKeys() { + requestStatusLockedKeys.refresh(); + } + + /** + * Requests the current locking states of all keys, after a lock state has been changed by openHAB. + */ + public void refreshStatusStatusLockedKeysAfterChange() { + requestStatusLockedKeys.nextRequestIn(STATUS_REQUEST_DELAY_AFTER_COMMAND_MSEC, System.nanoTime()); + } + + /** + * Resets the value request logic, when a requested value has been received from the LCN module: Dimmer Output + * + * @param outputId 0..3 + */ + public void onOutputResponseReceived(int outputId) { + requestStatusOutputs.get(outputId).onResponseReceived(); + } + + /** + * Resets the value request logic, when a requested value has been received from the LCN module: Relay + */ + public void onRelayResponseReceived() { + requestStatusRelays.onResponseReceived(); + } + + /** + * Resets the value request logic, when a requested value has been received from the LCN module: Binary Sensor + */ + public void onBinarySensorsResponseReceived() { + requestStatusBinSensors.onResponseReceived(); + } + + /** + * Resets the value request logic, when a requested value has been received from the LCN module: Variable + * + * @param variable the received variable type + */ + public void onVariableResponseReceived(Variable variable) { + requestStatusVars.get(variable).onResponseReceived(); + } + + /** + * Resets the value request logic, when a requested value has been received from the LCN module: LEDs and logic + */ + public void onLedsAndLogicResponseReceived() { + requestStatusLedsAndLogicOps.onResponseReceived(); + } + + /** + * Resets the value request logic, when a requested value has been received from the LCN module: Keys lock state + */ + public void onLockedKeysResponseReceived() { + requestStatusLockedKeys.onResponseReceived(); + } + +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/PckQueueItem.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/PckQueueItem.java new file mode 100644 index 0000000000000..24305e6aefef1 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/PckQueueItem.java @@ -0,0 +1,75 @@ +/** + * 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.connection; + +import java.nio.ByteBuffer; +import java.time.Instant; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnAddr; + +/** + * Holds data of one PCK command with the target address and the date when the item has been enqueued. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class PckQueueItem { + private Instant enqueued; + private LcnAddr addr; + private boolean wantsAck; + private ByteBuffer data; + + public PckQueueItem(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + this.enqueued = Instant.now(); + this.addr = addr; + this.wantsAck = wantsAck; + this.data = data; + } + + /** + * Gets the time when this message has been enqueued. + * + * @return the Instant + */ + public Instant getEnqueued() { + return enqueued; + } + + /** + * Gets the address of the destination LCN module. + * + * @return the address + */ + public LcnAddr getAddr() { + return addr; + } + + /** + * Checks whether an Ack is requested. + * + * @return true, if an Ack is requested + */ + public boolean isWantsAck() { + return wantsAck; + } + + /** + * Gets the raw PCK message to be sent. + * + * @return message as ByteBuffer + */ + public ByteBuffer getData() { + return data; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/RequestStatus.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/RequestStatus.java new file mode 100644 index 0000000000000..bc233a7802329 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/RequestStatus.java @@ -0,0 +1,195 @@ +/** + * 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.connection; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Manages timeout and retry logic for an LCN request. + * + * @author Tobias Jüttner - Initial Contribution + * @author Fabian Wolter - Migration to OH2 + */ +@NonNullByDefault +public class RequestStatus { + private final Logger logger = LoggerFactory.getLogger(RequestStatus.class); + /** Interval for forced updates. -1 if not used. */ + private volatile long maxAgeMSec; + + /** Tells how often a request will be sent if no response was received. */ + private final int numTries; + + /** true if request logic is activated. */ + private volatile boolean isActive; + + /** The time the current request was sent out or 0. */ + private volatile long currRequestTimeStamp; + + /** The time stamp of the next scheduled request or 0. */ + private volatile long nextRequestTimeStamp; + + /** Number of retries left until the request is marked as failed. */ + private volatile int numRetriesLeft; + private String label; + + /** + * Constructor. + * + * @param maxAgeMSec the forced-updates interval (-1 if not used) + * @param numTries the maximum number of tries until the request is marked as failed + */ + RequestStatus(long maxAgeMSec, int numTries, String label) { + this.maxAgeMSec = maxAgeMSec; + this.numTries = numTries; + this.label = label; + this.reset(); + } + + /** Resets the runtime data to the initial states. */ + public synchronized void reset() { + this.isActive = false; + this.currRequestTimeStamp = 0; + this.nextRequestTimeStamp = 0; + this.numRetriesLeft = 0; + } + + /** + * Checks whether the request logic is active. + * + * @return true if active + */ + public boolean isActive() { + return this.isActive; + } + + /** + * Checks whether a request is waiting for a response. + * + * @return true if waiting for a response + */ + boolean isPending() { + return this.currRequestTimeStamp != 0; + } + + /** + * Checks whether the request is active and ran into timeout while waiting for a response. + * + * @param timeoutMSec the timeout in milliseconds + * @param currTime the current time stamp + * @return true if request timed out + */ + synchronized boolean isTimeout(long timeoutMSec, long currTime) { + return this.isPending() && currTime - this.currRequestTimeStamp >= timeoutMSec * 1000000L; + } + + /** + * Checks for failed requests (active and out of retries). + * + * @param timeoutMSec the timeout in milliseconds + * @param currTime the current time stamp + * @return true if no response was received and no retries are left + */ + synchronized boolean isFailed(long timeoutMSec, long currTime) { + return this.isTimeout(timeoutMSec, currTime) && this.numRetriesLeft == 0; + } + + /** + * Schedules the next request. + * + * @param delayMSec the delay in milliseconds + * @param currTime the current time stamp + */ + public synchronized void nextRequestIn(long delayMSec, long currTime) { + this.isActive = true; + this.nextRequestTimeStamp = currTime + delayMSec * 1000000L; + } + + /** + * Schedules a request to retrieve the current value. + */ + public synchronized void refresh() { + nextRequestIn(0, System.nanoTime()); + this.numRetriesLeft = this.numTries; + } + + /** + * Checks whether sending a new request is required (should be called periodically). + * + * @param timeoutMSec the time to wait for a response before retrying the request + * @param currTime the current time stamp + * @return true to indicate a new request should be sent + * @throws LcnException when a status request timed out + */ + synchronized boolean shouldSendNextRequest(long timeoutMSec, long currTime) throws LcnException { + if (this.isActive) { + if (this.nextRequestTimeStamp != 0 && currTime >= this.nextRequestTimeStamp) { + return true; + } + // Retry of current request (after no response was received) + if (this.isTimeout(timeoutMSec, currTime)) { + if (this.numRetriesLeft > 0) { + return true; + } else if (isPending()) { + currRequestTimeStamp = 0; + throw new LcnException(label + ": Failed finally after " + numTries + " tries"); + } + } + } + return false; + } + + /** + * Must be called right after a new request has been sent. + * Must be activated first. + * + * @param currTime the current time stamp + */ + public synchronized void onRequestSent(long currTime) { + if (!this.isActive) { + logger.warn("Tried to send a request which is not active"); + } + // Updates retry counter + if (this.currRequestTimeStamp == 0) { + this.numRetriesLeft = this.numTries - 1; + } else if (this.numRetriesLeft > 0) { // Should not happen if used correctly + --this.numRetriesLeft; + } + // Mark request as pending + this.currRequestTimeStamp = currTime; + // Schedule next request + if (this.maxAgeMSec != -1) { + this.nextRequestIn(this.maxAgeMSec, currTime); + } else { + this.nextRequestTimeStamp = 0; + } + } + + /** Must be called when a response (requested or not) has been received. */ + public synchronized void onResponseReceived() { + if (this.isActive) { + this.currRequestTimeStamp = 0; // Mark request (if any) as successful + } + } + + /** + * Sets the timeout of this RequestStatus. + * + * @param maxAgeMSec the timeout in ms + */ + public void setMaxAgeMSec(long maxAgeMSec) { + this.maxAgeMSec = maxAgeMSec; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendData.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendData.java new file mode 100644 index 0000000000000..8bcd75d7e796e --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendData.java @@ -0,0 +1,41 @@ +/** + * 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.connection; + +import java.io.UnsupportedEncodingException; +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Base class for a packet to be send to LCN-PCHK. + * + * @author Tobias Jüttner - Initial Contribution + * @author Fabian Wolter - Migration to OH2 + */ +@NonNullByDefault +public abstract class SendData { + /** + * Writes the packet's data into the given buffer. + * Called right before the packet is actually sent to LCN-PCHK. + * + * @param buffer the target buffer + * @param localSegId the local segment id + * @return true if everything was set-up correctly and data was written + * @throws UnsupportedEncodingException if text could not be encoded for LCN-PCHK + * @throws BufferOverflowException if target buffer has not enough space left (buffer will not be altered) + */ + abstract boolean write(ByteBuffer buffer, int localSegId) + throws UnsupportedEncodingException, BufferOverflowException; +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPck.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPck.java new file mode 100644 index 0000000000000..7d43f488cd1f2 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPck.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.connection; + +import java.io.UnsupportedEncodingException; +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnAddr; +import org.openhab.binding.lcn.internal.common.LcnDefs; +import org.openhab.binding.lcn.internal.common.PckGenerator; + +/** + * A PCK command to be send to LCN-PCHK. + * It is already encoded as bytes to allow different text-encodings (ANSI, UTF-8). + * + * @author Tobias Jüttner - Initial Contribution + * @author Fabian Wolter - Migration to OH2 + */ +@NonNullByDefault +class SendDataPck extends SendData { + /** The target LCN address. */ + private final LcnAddr addr; + + /** true to acknowledge the command on receipt. */ + private final boolean wantsAck; + + /** PCK command (without address header) encoded as bytes. */ + private final ByteBuffer data; + + /** + * Constructor. + * + * @param addr the target LCN address + * @param wantsAck true to claim receipt + * @param data the PCK command encoded as bytes + */ + SendDataPck(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + this.addr = addr; + this.wantsAck = wantsAck; + this.data = data; + } + + /** + * Gets the PCK command. + * + * @return the PCK command encoded as bytes + */ + ByteBuffer getData() { + return this.data; + } + + @Override + boolean write(ByteBuffer buffer, int localSegId) throws UnsupportedEncodingException, BufferOverflowException { + buffer.put(PckGenerator.generateAddressHeader(this.addr, localSegId == -1 ? 0 : localSegId, this.wantsAck) + .getBytes(LcnDefs.LCN_ENCODING)); + data.rewind(); + buffer.put(this.data); + buffer.put(PckGenerator.TERMINATION.getBytes(LcnDefs.LCN_ENCODING)); + return true; + } + + @Override + public String toString() { + return "Addr: " + addr + ": " + new String(data.array(), 0, data.limit()); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPlainText.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPlainText.java new file mode 100644 index 0000000000000..c038190aad675 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPlainText.java @@ -0,0 +1,62 @@ +/** + * 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.connection; + +import java.io.UnsupportedEncodingException; +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.common.LcnDefs; +import org.openhab.binding.lcn.internal.common.PckGenerator; + +/** + * A plain text to be send to LCN-PCHK. + * + * @author Tobias Jüttner - Initial Contribution + * @author Fabian Wolter - Migration to OH2 + */ +@NonNullByDefault +class SendDataPlainText extends SendData { + /** The text. */ + private final String text; + + /** + * Constructor. + * + * @param text the text + */ + SendDataPlainText(String text) { + this.text = text; + } + + /** + * Gets the text. + * + * @return the text + */ + String getText() { + return this.text; + } + + @Override + boolean write(ByteBuffer buffer, int localSegId) throws UnsupportedEncodingException, BufferOverflowException { + buffer.put((this.text + PckGenerator.TERMINATION).getBytes(LcnDefs.LCN_ENCODING)); + return true; + } + + @Override + public String toString() { + return text; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateContext.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateContext.java new file mode 100644 index 0000000000000..9164ef1160db6 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateContext.java @@ -0,0 +1,66 @@ +/** + * 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.connection; + +import java.nio.ByteBuffer; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.lcn.internal.common.LcnAddr; + +/** + * Interface for a {@link StateMachine}. These methods are visible to the states, used by the {@link StateMachine}. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public interface StateContext { + /** + * Sets the new state of the StateMachine. + * + * @param newStateClass the class of the new State + */ + void setState(Class newStateClass); + + /** + * Checks if the given State is active by comparing the identity. + * + * @param otherState the stat to check + * @return true, if the given state is active + */ + boolean isStateActive(AbstractState otherState); + + /** + * Gets the Connection to the PCK gateway. + * + * @return the Connection + */ + Connection getConnection(); + + /** + * Notifies openHAB the Connection has failed and enters the necessary State to handle the situation. + * + * @param e the reason why the Connection has been failed + */ + void handleConnectionFailed(@Nullable Throwable e); + + /** + * Enqueues a PCK message. When the Connection is offline, the message will be buffered and sent out as soon as the + * Connection recovers. The message is discarded if it is too old, when the Connection recovers. + * + * @param addr the destination address + * @param wantsAck true, if the module shall respond with an Ack + * @param data the PCK message + */ + void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data); +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateMachine.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateMachine.java new file mode 100644 index 0000000000000..365f0155f2d75 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateMachine.java @@ -0,0 +1,109 @@ +/** + * 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.connection; + +import java.lang.reflect.InvocationTargetException; +import java.nio.ByteBuffer; +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.lcn.internal.common.LcnAddr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Implements a state machine for managing the connection to the LCN-PCK gateway. Setting states is thread-safe. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class StateMachine implements StateContext { + private final Logger logger = LoggerFactory.getLogger(StateMachine.class); + /** The StateMachine's current state */ + protected volatile AbstractConnectionState state; + private Connection connection; + private ScheduledExecutorService scheduler; + + public StateMachine(Connection connection, ScheduledExecutorService scheduler) { + this.connection = connection; + this.scheduler = scheduler; + this.state = new ConnectionStateInit(this, scheduler); + } + + @Override + public synchronized void setState(Class newStateClass) { + logger.debug("Changing state {} -> {}", state.getClass().getSimpleName(), newStateClass.getSimpleName()); + + state.cancelAllTimers(); + + try { + state = newStateClass.getDeclaredConstructor(StateContext.class, ScheduledExecutorService.class) + .newInstance(this, scheduler); + state.startWorking(); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException + | NoSuchMethodException | SecurityException e) { + logger.warn("Could not change state: {}", e.getMessage()); + } + } + + /** + * Starts the operation of the StateMachine by starting the initial State. + */ + public void startWorking() { + state.startWorking(); + } + + @Override + public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + state.queue(addr, wantsAck, data); + } + + @Override + public boolean isStateActive(AbstractState otherState) { + return state == otherState; // compare by identity + } + + @Override + public void handleConnectionFailed(@Nullable Throwable e) { + if (!(state instanceof ConnectionStateShutdown)) { + if (e != null) { + connection.getCallback().onOffline(e.getMessage()); + } else { + connection.getCallback().onOffline(""); + } + setState(ConnectionStateGracePeriodBeforeReconnect.class); + } + } + + @Override + public Connection getConnection() { + return connection; + } + + /** + * Processes a received PCK message by passing it to the current State. + * + * @param data the PCK message + */ + public void onInputReceived(String data) { + state.onPckMessageReceived(data); + } + + /** + * Shuts the StateMachine down finally. A shut-down StateMachine cannot be re-used. + */ + public void shutdownFinally() { + state.shutdownFinally(); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractS0Converter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractS0Converter.java new file mode 100644 index 0000000000000..d9cfff1c7841d --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractS0Converter.java @@ -0,0 +1,53 @@ +/** + * 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.converter; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Base class for S0 counter value converters. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public abstract class AbstractS0Converter extends AbstractVariableValueConverter { + private final Logger logger = LoggerFactory.getLogger(AbstractS0Converter.class); + protected double pulsesPerKwh; + + public AbstractS0Converter(@Nullable Object parameter) { + if (parameter == null) { + pulsesPerKwh = 1000; + logger.info("Pulses per kWh not set. Assuming 1000 imp./kWh."); + } else if (parameter instanceof BigDecimal) { + pulsesPerKwh = ((BigDecimal) parameter).doubleValue(); + } else { + logger.warn("Could not parse 'pulses', unexpected type, should be float or integer: {}", parameter); + } + } + + @Override + public int toNative(double value) { + return (int) Math.round(value * pulsesPerKwh / 1000); + } + + @Override + public double toHumanReadable(long value) { + return value / pulsesPerKwh * 1000; + } + +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractVariableValueConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractVariableValueConverter.java new file mode 100644 index 0000000000000..cada6e8807086 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractVariableValueConverter.java @@ -0,0 +1,113 @@ +/** + * 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 + */ +/** + * 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.converter; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Base class for all LCN variable value converters. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public abstract class AbstractVariableValueConverter { + private final Logger logger = LoggerFactory.getLogger(AbstractVariableValueConverter.class); + + /** + * Gets the Profile's Unit. + * + * @return the Unit + */ + protected abstract Unit getUnitType(); + + /** + * Converts the given human readable value into the native LCN value. + * + * @param humanReadableValue the value to convert + * @return the native value + */ + protected abstract int toNative(double humanReadableValue); + + /** + * Converts the given native LCN value into a human readable value. + * + * @param nativeValue the value to convert + * @return the human readable value + */ + protected abstract double toHumanReadable(long nativeValue); + + /** + * Converts a human readable value into LCN native value. + * + * @param humanReadable value to convert + * @return the native LCN value + */ + public DecimalType onCommandFromItem(double humanReadable) { + return new DecimalType(toNative(humanReadable)); + } + + /** + * Converts a human readable value into LCN native value. + * + * @param humanReadable value to convert + * @return the native LCN value + * @throws LcnException when the value could not be converted to the base unit + */ + public DecimalType onCommandFromItem(QuantityType quantityType) throws LcnException { + QuantityType quantityInBaseUnit = quantityType.toUnit(getUnitType()); + + if (quantityInBaseUnit != null) { + return onCommandFromItem(quantityInBaseUnit.doubleValue()); + } else { + throw new LcnException(quantityType + ": Incompatible unit: " + getUnitType()); + } + } + + /** + * Converts a state update from the Thing into a human readable unit. + * + * @param state from the Thing + * @return human readable State + */ + public State onStateUpdateFromHandler(State state) { + State result = state; + + if (state instanceof DecimalType) { + result = QuantityType.valueOf(toHumanReadable(((DecimalType) state).longValue()), getUnitType()); + } else { + logger.warn("Unexpected state type: {}", state.getClass().getSimpleName()); + } + + return result; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AngleConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AngleConverter.java new file mode 100644 index 0000000000000..1bc0c3489ce03 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AngleConverter.java @@ -0,0 +1,41 @@ +/** + * 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.converter; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; + +/** + * Converts the native LCN value of LCN-WIH to the sun azimuth/elevation values. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class AngleConverter extends AbstractVariableValueConverter { + @Override + protected Unit getUnitType() { + return SmartHomeUnits.DEGREE_ANGLE; + } + + @Override + public int toNative(double value) { + return (int) (Math.round(value * 10) + 1000); + } + + @Override + public double toHumanReadable(long value) { + return (value - 1000) / 10f; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Co2Converter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Co2Converter.java new file mode 100644 index 0000000000000..d7f39920496b4 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Co2Converter.java @@ -0,0 +1,41 @@ +/** + * 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.converter; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; + +/** + * Converts the native LCN value of LCN-CO2 to the ppm value. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class Co2Converter extends AbstractVariableValueConverter { + @Override + protected Unit getUnitType() { + return SmartHomeUnits.PARTS_PER_MILLION; + } + + @Override + public int toNative(double value) { + return (int) Math.round(value); + } + + @Override + public double toHumanReadable(long value) { + return value; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/CurrentConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/CurrentConverter.java new file mode 100644 index 0000000000000..87afb1f7aca27 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/CurrentConverter.java @@ -0,0 +1,41 @@ +/** + * 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.converter; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; + +/** + * Converts the native LCN value of a 4-20mA input of LCN-AD2 to current. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class CurrentConverter extends AbstractVariableValueConverter { + @Override + protected Unit getUnitType() { + return SmartHomeUnits.AMPERE; + } + + @Override + public int toNative(double value) { + return (int) Math.round(value * 100); + } + + @Override + public double toHumanReadable(long value) { + return value / 100d; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/EnergyConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/EnergyConverter.java new file mode 100644 index 0000000000000..3cbbc981ad61c --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/EnergyConverter.java @@ -0,0 +1,36 @@ +/** + * 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.converter; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; + +/** + * Converts the native LCN value of an S0 counter of LCN-BU4L to kWh. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class EnergyConverter extends AbstractS0Converter { + public EnergyConverter(@Nullable Object parameter) { + super(parameter); + } + + @Override + protected Unit getUnitType() { + return SmartHomeUnits.WATT_HOUR; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/IdentityConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/IdentityConverter.java new file mode 100644 index 0000000000000..e64a0462e697e --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/IdentityConverter.java @@ -0,0 +1,62 @@ +/** + * 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.converter; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; +import org.openhab.binding.lcn.internal.common.LcnException; + +/** + * Converts the value 1:1. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class IdentityConverter extends AbstractVariableValueConverter { + private IdentityConverter() { + // nothing + } + + @NonNullByDefault({}) + private static class LazyHolder { + static final IdentityConverter INSTANCE = new IdentityConverter(); + } + + public static IdentityConverter getInstance() { + return LazyHolder.INSTANCE; + } + + @Override + protected Unit getUnitType() { + return SmartHomeUnits.VOLT; // not used + } + + @Override + public DecimalType onCommandFromItem(QuantityType quantityType) throws LcnException { + return onCommandFromItem(quantityType.doubleValue()); + } + + @Override + public int toNative(double value) { + return (int) Math.round(value); + } + + @Override + public double toHumanReadable(long value) { + return value; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/LightConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/LightConverter.java new file mode 100644 index 0000000000000..4f71a25ec579e --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/LightConverter.java @@ -0,0 +1,47 @@ +/** + * 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.converter; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; + +/** + * Converts the native LCN value of I-Port periphery like LCN-GBL, LCN-GUS, LCN-WIH to the lux value. + * Profile doesn't support the LCN-LS on the T-Port. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class LightConverter extends AbstractVariableValueConverter { + @Override + protected Unit getUnitType() { + return SmartHomeUnits.LUX; + } + + @Override + public int toNative(double value) { + return (int) Math.round(Math.log(value) * 100); + } + + @Override + public double toHumanReadable(long value) { + // Max. value hardware can deliver is 100klx. Apply hard limit, because higher native values lead to very big + // lux values. + if (value > 1152) { + return Double.NaN; + } + return Math.exp(value / 100d); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/PowerConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/PowerConverter.java new file mode 100644 index 0000000000000..167f5f6e7a2d0 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/PowerConverter.java @@ -0,0 +1,36 @@ +/** + * 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.converter; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; + +/** + * Converts the native LCN value of an S0 input power variable of LCN-BU4L to Watts. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class PowerConverter extends AbstractS0Converter { + public PowerConverter(@Nullable Object parameter) { + super(parameter); + } + + @Override + protected Unit getUnitType() { + return SmartHomeUnits.WATT; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/TemperatureConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/TemperatureConverter.java new file mode 100644 index 0000000000000..0fca1cc58cfdb --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/TemperatureConverter.java @@ -0,0 +1,42 @@ +/** + * 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.converter; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.unit.SIUnits; + +/** + * Converts the native LCN value to temperature. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class TemperatureConverter extends AbstractVariableValueConverter { + @Override + protected Unit getUnitType() { + // The user can also configure "°F" instead of "°C" in the item. The value will be converted accordingly. + return SIUnits.CELSIUS; + } + + @Override + public int toNative(double value) { + return (int) (Math.round(value * 10) + 1000); + } + + @Override + public double toHumanReadable(long value) { + return (value - 1000) / 10d; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/VoltageConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/VoltageConverter.java new file mode 100644 index 0000000000000..ae2ffd22199d8 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/VoltageConverter.java @@ -0,0 +1,41 @@ +/** + * 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.converter; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; + +/** + * Converts the native LCN value of a 0-10V input of LCN-AD2 to voltage. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class VoltageConverter extends AbstractVariableValueConverter { + @Override + protected Unit getUnitType() { + return SmartHomeUnits.VOLT; + } + + @Override + public int toNative(double value) { + return (int) Math.round(value * 400); + } + + @Override + public double toHumanReadable(long value) { + return value / 400d; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/WindspeedConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/WindspeedConverter.java new file mode 100644 index 0000000000000..39aa0cdb04f8e --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/WindspeedConverter.java @@ -0,0 +1,41 @@ +/** + * 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.converter; + +import javax.measure.Unit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; + +/** + * Converts the native LCN value of LCN-WIH to the wind speed value. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class WindspeedConverter extends AbstractVariableValueConverter { + @Override + protected Unit getUnitType() { + return SmartHomeUnits.METRE_PER_SECOND; + } + + @Override + public int toNative(double value) { + return (int) Math.round(value * 10); + } + + @Override + public double toHumanReadable(long value) { + return value / 10d; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtService.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtService.java new file mode 100644 index 0000000000000..0a7add3995054 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtService.java @@ -0,0 +1,39 @@ +/** + * 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.pchkdiscovery; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +import com.thoughtworks.xstream.annotations.XStreamConverter; +import com.thoughtworks.xstream.converters.extended.ToAttributedValueConverter; + +/** + * Used for deserializing the XML response of the LCN-PCHK discovery protocol. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +@XStreamConverter(value = ToAttributedValueConverter.class, strings = { "content" }) +public class ExtService { + private int localPort; + @SuppressWarnings("unused") + private String content = ""; + + public ExtService(int localPort) { + this.localPort = localPort; + } + + public int getLocalPort() { + return localPort; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtServices.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtServices.java new file mode 100644 index 0000000000000..1e88d3145856f --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtServices.java @@ -0,0 +1,33 @@ +/** + * 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.pchkdiscovery; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Used for deserializing the XML response of the LCN-PCHK discovery protocol. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class ExtServices { + private ExtService ExtService; + + public ExtServices(ExtService extService) { + ExtService = extService; + } + + public ExtService getExtService() { + return ExtService; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java new file mode 100644 index 0000000000000..80582bb2d545e --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java @@ -0,0 +1,160 @@ +/** + * 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.pchkdiscovery; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.MulticastSocket; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.xml.StaxDriver; + +/** + * Discovers LCN-PCK gateways, such as LCN-PCHK. + * + * Scan approach: + * 1. Determines all local network interfaces + * 2. Send a multicast message on each interface to the PCHK multicast address 234.5.6.7 (not configurable by user). + * 3. Evaluate multicast responses of PCK gateways in the network + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.lcn") +public class LcnPchkDiscoveryService extends AbstractDiscoveryService { + private final Logger logger = LoggerFactory.getLogger(LcnPchkDiscoveryService.class); + private static final String PCHK_DISCOVERY_MULTICAST_ADDRESS = "234.5.6.7"; + private static final int PCHK_DISCOVERY_PORT = 4220; + private static final int INTERFACE_TIMEOUT_SEC = 2; + private static final Set SUPPORTED_THING_TYPES_UIDS = Collections + .unmodifiableSet(Stream.of(LcnBindingConstants.THING_TYPE_PCK_GATEWAY).collect(Collectors.toSet())); + private static final String DISCOVER_REQUEST = "openHAB"; + + public LcnPchkDiscoveryService() throws IllegalArgumentException { + super(SUPPORTED_THING_TYPES_UIDS, 0, false); + } + + private List getLocalAddresses() { + List result = new LinkedList<>(); + try { + for (NetworkInterface networkInterface : Collections.list(NetworkInterface.getNetworkInterfaces())) { + try { + if (networkInterface.isUp() && !networkInterface.isLoopback() + && !networkInterface.isPointToPoint()) { + result.addAll(Collections.list(networkInterface.getInetAddresses())); + } + } catch (SocketException exception) { + // ignore + } + } + } catch (SocketException exception) { + return Collections.emptyList(); + } + return result; + } + + @Override + protected void startScan() { + try { + InetAddress multicastAddress = InetAddress.getByName(PCHK_DISCOVERY_MULTICAST_ADDRESS); + + getLocalAddresses().forEach(localInterfaceAddress -> { + logger.debug("Searching on {} ...", localInterfaceAddress.getHostAddress()); + try (MulticastSocket socket = new MulticastSocket(PCHK_DISCOVERY_PORT)) { + socket.setInterface(localInterfaceAddress); + socket.setReuseAddress(true); + socket.setSoTimeout(INTERFACE_TIMEOUT_SEC * 1000); + socket.joinGroup(multicastAddress); + + byte[] requestData = DISCOVER_REQUEST.getBytes("UTF-8"); + DatagramPacket request = new DatagramPacket(requestData, requestData.length, multicastAddress, + PCHK_DISCOVERY_PORT); + socket.send(request); + + try { + do { + byte[] rxbuf = new byte[8192]; + DatagramPacket packet = new DatagramPacket(rxbuf, rxbuf.length); + socket.receive(packet); + + InetAddress addr = packet.getAddress(); + String response = new String(packet.getData()); + + if (response.contains("ServicesRequest")) { + continue; + } + + ServicesResponse deserialized = xmlToServiceResponse(response); + + Map properties = new HashMap<>(5); + properties.put("hostname", addr.getHostAddress()); + properties.put("port", deserialized.getExtServices().getExtService().getLocalPort()); + + String thingId = deserialized.getServer().getMachineId().replace(":", ""); + ThingUID thingUid = new ThingUID(LcnBindingConstants.THING_TYPE_PCK_GATEWAY, thingId); + + DiscoveryResultBuilder discoveryResult = DiscoveryResultBuilder.create(thingUid) + .withProperties(properties).withLabel(deserialized.getServer().getContent() + " (" + + deserialized.getServer().getMachineName() + ")"); + + thingDiscovered(discoveryResult.build()); + } while (true); + } catch (SocketTimeoutException e) { + // nothing + } + } catch (IOException e) { + logger.warn("Discovery failed for {}: {}", localInterfaceAddress, e.getMessage()); + } + }); + } catch (UnknownHostException e) { + logger.warn("Discovery failed: {}", e.getMessage()); + } + } + + ServicesResponse xmlToServiceResponse(String response) { + XStream xstream = new XStream(new StaxDriver()); + xstream.setClassLoader(getClass().getClassLoader()); + xstream.autodetectAnnotations(true); + xstream.alias("ServicesResponse", ServicesResponse.class); + xstream.alias("Server", Server.class); + xstream.alias("Version", Server.class); + xstream.alias("ExtServices", ExtServices.class); + xstream.alias("ExtService", ExtService.class); + + return (ServicesResponse) xstream.fromXML(response); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Server.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Server.java new file mode 100644 index 0000000000000..0b965c5fac80a --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Server.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.pchkdiscovery; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +import com.thoughtworks.xstream.annotations.XStreamAsAttribute; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import com.thoughtworks.xstream.converters.extended.ToAttributedValueConverter; + +/** + * Used for deserializing the XML response of the LCN-PCHK discovery protocol. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +@XStreamConverter(value = ToAttributedValueConverter.class, strings = { "content" }) +public class Server { + @XStreamAsAttribute + private int requestId; + @XStreamAsAttribute + private String machineId; + @XStreamAsAttribute + private String machineName; + @XStreamAsAttribute + private String osShort; + @XStreamAsAttribute + private String osLong; + private String content; + + public Server(int requestId, String machineId, String machineName, String osShort, String osLong, String content) { + this.requestId = requestId; + this.machineId = machineId; + this.machineName = machineName; + this.osShort = osShort; + this.osLong = osLong; + this.content = content; + } + + public int getRequestId() { + return requestId; + } + + public String getMachineId() { + return machineId; + } + + public String getOsShort() { + return osShort; + } + + public String getOsLong() { + return osLong; + } + + public String getContent() { + return content; + } + + public Object getMachineName() { + return machineName; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ServicesResponse.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ServicesResponse.java new file mode 100644 index 0000000000000..ba5a0300f9dd1 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ServicesResponse.java @@ -0,0 +1,47 @@ +/** + * 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.pchkdiscovery; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Used for deserializing the XML response of the LCN-PCHK discovery protocol. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class ServicesResponse { + private Version Version; + private Server Server; + private ExtServices ExtServices; + @SuppressWarnings("unused") + private Object Services = new Object(); + + public ServicesResponse(Version version, Server server, ExtServices extServices) { + this.Version = version; + this.Server = server; + this.ExtServices = extServices; + } + + public Server getServer() { + return Server; + } + + public Version getVersion() { + return Version; + } + + public ExtServices getExtServices() { + return ExtServices; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Version.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Version.java new file mode 100644 index 0000000000000..58f2697e046dc --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Version.java @@ -0,0 +1,43 @@ +/** + * 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.pchkdiscovery; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +import com.thoughtworks.xstream.annotations.XStreamAsAttribute; + +/** + * Used for deserializing the XML response of the LCN-PCHK discovery protocol. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class Version { + @XStreamAsAttribute + private int major; + @XStreamAsAttribute + private int minor; + + public Version(int major, int minor) { + this.major = major; + this.minor = minor; + } + + public int getMajor() { + return major; + } + + public int getMinor() { + return minor; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleSubHandler.java new file mode 100644 index 0000000000000..00b274355f7bd --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleSubHandler.java @@ -0,0 +1,267 @@ +/** + * 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.Arrays; +import java.util.Collection; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.StopMoveType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.lcn.internal.LcnModuleHandler; +import org.openhab.binding.lcn.internal.common.DimmerOutputCommand; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnDefs; +import org.openhab.binding.lcn.internal.common.LcnDefs.RelayStateModifier; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.Variable; +import org.openhab.binding.lcn.internal.common.VariableValue; +import org.openhab.binding.lcn.internal.connection.ModInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Base class for LCN module Thing sub handlers. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public abstract class AbstractLcnModuleSubHandler { + private final Logger logger = LoggerFactory.getLogger(AbstractLcnModuleSubHandler.class); + protected LcnModuleHandler handler; + protected ModInfo info; + + public AbstractLcnModuleSubHandler(LcnModuleHandler handler, ModInfo info) { + this.handler = handler; + this.info = info; + } + + /** + * Gets the Patterns, the sub handler is capable to process. + * + * @return the Patterns + */ + public abstract Collection getPckStatusMessagePatterns(); + + /** + * Processes the payload of a pre-matched PCK message. + * + * @param matcher the pre-matched matcher. + * @throws LcnException when the message cannot be processed + */ + public abstract void handleStatusMessage(Matcher matcher) throws LcnException; + + /** + * Processes a refresh request from openHAB. + * + * @param channelGroup the Channel group that shall be refreshed + * @param number the Channel number within the Channel group + */ + public abstract void handleRefresh(LcnChannelGroup channelGroup, int number); + + /** + * Processes a refresh request from openHAB. + * + * @param groupId the Channel ID that shall be refreshed + */ + public void handleRefresh(String groupId) { + // can be overwritten by subclasses. + } + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param channelGroup the addressed Channel group + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + public void handleCommandOnOff(OnOffType command, LcnChannelGroup channelGroup, int number) throws LcnException { + unsupportedCommand(command); + } + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param channelGroup the addressed Channel group + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + public void handleCommandPercent(PercentType command, LcnChannelGroup channelGroup, int number) + throws LcnException { + unsupportedCommand(command); + } + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param channelGroup the addressed Channel group + * @param idWithoutGroup the Channel's name within the Channel group + * @throws LcnException when the command could not processed + */ + public void handleCommandPercent(PercentType command, LcnChannelGroup channelGroup, String idWithoutGroup) + throws LcnException { + unsupportedCommand(command); + } + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param channelGroup the addressed Channel group + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + public void handleCommandDecimal(DecimalType command, LcnChannelGroup channelGroup, int number) + throws LcnException { + unsupportedCommand(command); + } + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + public void handleCommandDimmerOutput(DimmerOutputCommand command, int number) throws LcnException { + unsupportedCommand(command); + } + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + public void handleCommandString(StringType command, int number) throws LcnException { + unsupportedCommand(command); + } + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param channelGroup the addressed Channel group + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + public void handleCommandUpDown(UpDownType command, LcnChannelGroup channelGroup, int number) throws LcnException { + unsupportedCommand(command); + } + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param channelGroup the addressed Channel group + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + public void handleCommandStopMove(StopMoveType command, LcnChannelGroup channelGroup, int number) + throws LcnException { + unsupportedCommand(command); + } + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param groupId the Channel's name within the Channel group + * @throws LcnException when the command could not processed + */ + public void handleCommandHsb(HSBType command, String groupId) throws LcnException { + unsupportedCommand(command); + } + + private void unsupportedCommand(Command command) { + logger.warn("Unsupported command: {}", command.getClass().getSimpleName()); + } + + /** + * Tries to parses the given PCK message. Fails silently to let another sub handler give the chance to process the + * message. + * + * @param pck the message to process + * @return true, if the message could be processed successfully + */ + public boolean tryParse(String pck) { + Optional firstSuccessfulMatcher = getPckStatusMessagePatterns().stream().map(p -> p.matcher(pck)) + .filter(m -> m.matches()).filter(m -> handler.isMyAddress(m.group("segId"), m.group("modId"))) + .findAny(); + + firstSuccessfulMatcher.ifPresent(matcher -> { + try { + handleStatusMessage(matcher); + } catch (LcnException e) { + logger.warn("Parse error: {}", e.getMessage()); + } + }); + + return firstSuccessfulMatcher.isPresent(); + } + + /** + * Creates a RelayStateModifier array with all elements set to NOCHANGE. + * + * @return the created array + */ + protected RelayStateModifier[] createRelayStateModifierArray() { + RelayStateModifier[] ret = new LcnDefs.RelayStateModifier[LcnChannelGroup.RELAY.getCount()]; + Arrays.fill(ret, LcnDefs.RelayStateModifier.NOCHANGE); + return ret; + } + + /** + * Updates the state of the LCN module. + * + * @param type the channel type which shall be updated + * @param number the Channel's number within the channel type, zero-based + * @param state the new state + */ + protected void fireUpdate(LcnChannelGroup type, int number, State state) { + handler.updateChannel(type, (number + 1) + "", state); + } + + /** + * Fires the current state of a Variable to openHAB. Resets running value request logic. + * + * @param matcher the pre-matched matcher + * @param channelId the Channel's ID to update + * @param variable the Variable to update + * @return the new variable's value + */ + protected VariableValue fireUpdateAndReset(Matcher matcher, String channelId, Variable variable) { + VariableValue value = new VariableValue(Long.parseLong(matcher.group("value" + channelId))); + + info.updateVariableValue(variable, value); + info.onVariableResponseReceived(variable); + + fireUpdate(variable.getChannelType(), variable.getThresholdNumber().orElse(variable.getNumber()), + value.getState(variable)); + return value; + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleVariableSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleVariableSubHandler.java new file mode 100644 index 0000000000000..5589d1d258197 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleVariableSubHandler.java @@ -0,0 +1,140 @@ +/** + * 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 org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.openhab.binding.lcn.internal.LcnModuleHandler; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.Variable; +import org.openhab.binding.lcn.internal.connection.ModInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Base class for LCN module Thing sub handlers processing variables. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public abstract class AbstractLcnModuleVariableSubHandler extends AbstractLcnModuleSubHandler { + private final Logger logger = LoggerFactory.getLogger(AbstractLcnModuleVariableSubHandler.class); + + public AbstractLcnModuleVariableSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + requestVariable(info, channelGroup, number); + info.requestFirmwareVersion(); + } + + /** + * Requests the current state of the given Channel. + * + * @param info the modules ModInfo cache + * @param channelGroup the Channel group + * @param number the Channel's number within the Channel group + */ + protected void requestVariable(ModInfo info, LcnChannelGroup channelGroup, int number) { + try { + Variable var = getVariable(channelGroup, number); + info.refreshVariable(var); + } catch (IllegalArgumentException e) { + logger.warn("Could not parse variable name: {}{}", channelGroup, (number + 1)); + } + } + + /** + * Gets a Variable from the given parameters. + * + * @param channelGroup the Channel group the Variable is in + * @param number the number of the Variable's Channel + * @return the Variable + * @throws IllegalArgumentException when the Channel group and number do not exist + */ + protected Variable getVariable(LcnChannelGroup channelGroup, int number) throws IllegalArgumentException { + return Variable.valueOf(channelGroup.name() + (number + 1)); + } + + /** + * Calculates the relative change between the current and the demanded value of a Variable. + * + * @param command the requested value + * @param variable the Variable type + * @return the difference + * @throws LcnException when the difference is too big + */ + protected int getRelativeChange(DecimalType command, Variable variable) throws LcnException { + // LCN doesn't support setting thresholds or variables with absolute values. So, calculate the relative change. + int relativeVariableChange = (int) (command.longValue() - info.getVariableValue(variable)); + + int result; + if (relativeVariableChange > 0) { + result = Math.min(relativeVariableChange, getMaxAbsChange(variable)); + } else { + result = Math.max(relativeVariableChange, -getMaxAbsChange(variable)); + } + if (result != relativeVariableChange) { + logger.warn("Relative change of {} too big, limiting: {}", variable, relativeVariableChange); + } + return result; + } + + private int getMaxAbsChange(Variable variable) { + switch (variable) { + case RVARSETPOINT1: + case RVARSETPOINT2: + case THRESHOLDREGISTER11: + case THRESHOLDREGISTER12: + case THRESHOLDREGISTER13: + case THRESHOLDREGISTER14: + case THRESHOLDREGISTER15: + case THRESHOLDREGISTER21: + case THRESHOLDREGISTER22: + case THRESHOLDREGISTER23: + case THRESHOLDREGISTER24: + case THRESHOLDREGISTER31: + case THRESHOLDREGISTER32: + case THRESHOLDREGISTER33: + case THRESHOLDREGISTER34: + case THRESHOLDREGISTER41: + case THRESHOLDREGISTER42: + case THRESHOLDREGISTER43: + case THRESHOLDREGISTER44: + return 1000; + case VARIABLE1: + case VARIABLE2: + case VARIABLE3: + case VARIABLE4: + case VARIABLE5: + case VARIABLE6: + case VARIABLE7: + case VARIABLE8: + case VARIABLE9: + case VARIABLE10: + case VARIABLE11: + case VARIABLE12: + return 4000; + case UNKNOWN: + case S0INPUT1: + case S0INPUT2: + case S0INPUT3: + case S0INPUT4: + default: + return 0; + } + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleBinarySensorSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleBinarySensorSubHandler.java new file mode 100644 index 0000000000000..a911662d25cb0 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleBinarySensorSubHandler.java @@ -0,0 +1,62 @@ +/** + * 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 java.util.stream.IntStream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.OpenClosedType; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +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.connection.ModInfo; + +/** + * Handles State changes of binary sensors of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleBinarySensorSubHandler extends AbstractLcnModuleSubHandler { + private static final Pattern PATTERN = Pattern.compile(LcnBindingConstants.ADDRESS_REGEX + "Bx(?\\d+)"); + + public LcnModuleBinarySensorSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + info.refreshBinarySensors(); + } + + @Override + public void handleStatusMessage(Matcher matcher) { + info.onBinarySensorsResponseReceived(); + + boolean[] states = LcnDefs.getBooleanValue(Integer.parseInt(matcher.group("byteValue"))); + + IntStream.range(0, LcnChannelGroup.BINARYSENSOR.getCount()) + .forEach(i -> fireUpdate(LcnChannelGroup.BINARYSENSOR, i, + states[i] ? OpenClosedType.OPEN : OpenClosedType.CLOSED)); + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Collections.singleton(PATTERN); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleCodeSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleCodeSubHandler.java new file mode 100644 index 0000000000000..69a9102013a56 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleCodeSubHandler.java @@ -0,0 +1,108 @@ +/** + * 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.Arrays; +import java.util.Collection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +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.connection.ModInfo; + +/** + * Handles State changes of transponders and remote controls of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleCodeSubHandler extends AbstractLcnModuleSubHandler { + private static final Pattern TRANSPONDER_PATTERN = Pattern + .compile(LcnBindingConstants.ADDRESS_REGEX + "\\.ZT(?\\d{3})(?\\d{3})(?\\d{3})"); + private static final Pattern REMOTE_CONTROL_PATTERN = Pattern.compile(LcnBindingConstants.ADDRESS_REGEX + + "\\.ZI(?\\d{3})(?\\d{3})(?\\d{3})(?\\d{3})(?\\d{3})"); + + public LcnModuleCodeSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + // nothing + } + + @Override + public void handleStatusMessage(Matcher matcher) { + String code = String.format("%02X%02X%02X", Integer.parseInt(matcher.group("byte0")), + Integer.parseInt(matcher.group("byte1")), Integer.parseInt(matcher.group("byte2"))); + + if (matcher.pattern() == TRANSPONDER_PATTERN) { + handler.triggerChannel(LcnChannelGroup.CODE, "transponder", code); + } else if (matcher.pattern() == REMOTE_CONTROL_PATTERN) { + int keyNumber = Integer.parseInt(matcher.group("key")); + String keyLayer; + + if (keyNumber > 30) { + keyLayer = "D"; + keyNumber -= 30; + } else if (keyNumber > 20) { + keyLayer = "C"; + keyNumber -= 20; + } else if (keyNumber > 10) { + keyLayer = "B"; + keyNumber -= 10; + } else if (keyNumber > 0) { + keyLayer = "A"; + } else { + return; + } + + int action = Integer.parseInt(matcher.group("action")); + + if (action > 10) { + handler.triggerChannel(LcnChannelGroup.CODE, "remotecontrolbatterylow", code); + action -= 10; + } + + LcnDefs.SendKeyCommand actionType; + switch (action) { + case 1: + actionType = LcnDefs.SendKeyCommand.HIT; + break; + case 2: + actionType = LcnDefs.SendKeyCommand.MAKE; + break; + case 3: + actionType = LcnDefs.SendKeyCommand.BREAK; + break; + default: + return; + } + + handler.triggerChannel(LcnChannelGroup.CODE, "remotecontrolkey", + keyLayer + keyNumber + ":" + actionType.name()); + + handler.triggerChannel(LcnChannelGroup.CODE, "remotecontrolcode", + code + ":" + keyLayer + keyNumber + ":" + actionType.name()); + } + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Arrays.asList(TRANSPONDER_PATTERN, REMOTE_CONTROL_PATTERN); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleKeyLockTableSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleKeyLockTableSubHandler.java new file mode 100644 index 0000000000000..b05116476e3cd --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleKeyLockTableSubHandler.java @@ -0,0 +1,95 @@ +/** + * 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.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.IntStream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +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.LcnDefs.KeyLockStateModifier; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.PckGenerator; +import org.openhab.binding.lcn.internal.connection.ModInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handles Commands and State changes of key table locks of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleKeyLockTableSubHandler extends AbstractLcnModuleSubHandler { + private final Logger logger = LoggerFactory.getLogger(LcnModuleKeyLockTableSubHandler.class); + private static final Pattern PATTERN = Pattern.compile(LcnBindingConstants.ADDRESS_REGEX + + "\\.TX(?\\d{3})(?\\d{3})(?\\d{3})((?\\d{3}))?"); + + public LcnModuleKeyLockTableSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + info.refreshStatusLockedKeys(); + } + + @Override + public void handleRefresh(String groupId) { + // nothing + } + + @Override + public void handleCommandOnOff(OnOffType command, LcnChannelGroup channelGroup, int number) throws LcnException { + KeyLockStateModifier[] keyLockStateModifiers = new LcnDefs.KeyLockStateModifier[channelGroup.getCount()]; + Arrays.fill(keyLockStateModifiers, LcnDefs.KeyLockStateModifier.NOCHANGE); + keyLockStateModifiers[number] = command == OnOffType.ON ? LcnDefs.KeyLockStateModifier.ON + : LcnDefs.KeyLockStateModifier.OFF; + int tableId = channelGroup.ordinal() - LcnChannelGroup.KEYLOCKTABLEA.ordinal(); + handler.sendPck(PckGenerator.lockKeys(tableId, keyLockStateModifiers)); + info.refreshStatusStatusLockedKeysAfterChange(); + } + + @Override + public void handleStatusMessage(Matcher matcher) { + info.onLockedKeysResponseReceived(); + + IntStream.range(0, LcnDefs.KEY_TABLE_COUNT).forEach(tableId -> { + String stateString = matcher.group(String.format("table%d", tableId)); + if (stateString != null) { + boolean[] states = LcnDefs.getBooleanValue(Integer.parseInt(stateString)); + try { + LcnChannelGroup channelGroup = LcnChannelGroup.fromTableId(tableId); + for (int i = 0; i < states.length; i++) { + fireUpdate(channelGroup, i, states[i] ? OnOffType.ON : OnOffType.OFF); + } + } catch (LcnException e) { + logger.warn("Failed to set key table lock state: {}", e.getMessage()); + } + } + }); + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Collections.singleton(PATTERN); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLedSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLedSubHandler.java new file mode 100644 index 0000000000000..6fd5d95cefa9f --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLedSubHandler.java @@ -0,0 +1,66 @@ +/** + * 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.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.StringType; +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.common.PckGenerator; +import org.openhab.binding.lcn.internal.connection.ModInfo; + +/** + * Handles Commands and State changes of LEDs of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleLedSubHandler extends AbstractLcnModuleSubHandler { + public LcnModuleLedSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + info.refreshLedsAndLogic(); + } + + @Override + public void handleCommandOnOff(OnOffType command, LcnChannelGroup channelGroup, int number) throws LcnException { + handleCommandString(new StringType(command.toString()), number); + } + + @Override + public void handleCommandString(StringType command, int number) throws LcnException { + handler.sendPck(PckGenerator.controlLed(number, LcnDefs.LedStatus.valueOf(command.toString()))); + info.refreshStatusLedsAnLogicAfterChange(); + } + + @Override + public void handleStatusMessage(Matcher matcher) { + /** Status messages are handled in {@link LcnModuleLogicSubHandler}. */ + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Collections.emptyList(); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLogicSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLogicSubHandler.java new file mode 100644 index 0000000000000..21b5403ae16b1 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLogicSubHandler.java @@ -0,0 +1,126 @@ +/** + * 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.Arrays; +import java.util.Collection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.IntStream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.StringType; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +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.LcnDefs.LogicOpStatus; +import org.openhab.binding.lcn.internal.connection.ModInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handles State changes of logic operations of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleLogicSubHandler extends AbstractLcnModuleSubHandler { + private final Logger logger = LoggerFactory.getLogger(LcnModuleLogicSubHandler.class); + private static final Pattern PATTERN_SINGLE_LOGIC = Pattern + .compile(LcnBindingConstants.ADDRESS_REGEX + "S(?\\d{1})(?\\d{3})"); + private static final Pattern PATTERN_ALL = Pattern + .compile(LcnBindingConstants.ADDRESS_REGEX + "\\.TL(?[AEBF]{12})(?[NTV]{4})"); + + public LcnModuleLogicSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + info.refreshLedsAndLogic(); + } + + @Override + public void handleStatusMessage(Matcher matcher) { + info.onLedsAndLogicResponseReceived(); + + if (matcher.pattern() == PATTERN_ALL) { + IntStream.range(0, LcnChannelGroup.LED.getCount()).forEach(i -> { + switch (matcher.group("ledStates").toUpperCase().charAt(i)) { + case 'A': + fireLed(i, LcnDefs.LedStatus.OFF); + break; + case 'E': + fireLed(i, LcnDefs.LedStatus.ON); + break; + case 'B': + fireLed(i, LcnDefs.LedStatus.BLINK); + break; + case 'F': + fireLed(i, LcnDefs.LedStatus.FLICKER); + break; + default: + logger.warn("Failed to parse LED state: {}", matcher.group("ledStates")); + } + }); + IntStream.range(0, LcnChannelGroup.LOGIC.getCount()).forEach(i -> { + switch (matcher.group("logicOpStates").toUpperCase().charAt(i)) { + case 'N': + fireLogic(i, LcnDefs.LogicOpStatus.NOT); + break; + case 'T': + fireLogic(i, LcnDefs.LogicOpStatus.OR); + break; + case 'V': + fireLogic(i, LcnDefs.LogicOpStatus.AND); + break; + default: + logger.warn("Failed to parse logic state: {}", matcher.group("logicOpStates")); + } + }); + } else if (matcher.pattern() == PATTERN_SINGLE_LOGIC) { + String rawState = matcher.group("logicOpState"); + + LogicOpStatus state; + switch (rawState) { + case "000": + state = LcnDefs.LogicOpStatus.NOT; + break; + case "025": + state = LcnDefs.LogicOpStatus.OR; + break; + case "050": + state = LcnDefs.LogicOpStatus.AND; + break; + default: + logger.warn("Failed to parse logic state: {}", rawState); + return; + } + fireLogic(Integer.parseInt(matcher.group("id")) - 1, state); + } + } + + private void fireLed(int number, LcnDefs.LedStatus status) { + fireUpdate(LcnChannelGroup.LED, number, new StringType(status.toString())); + } + + private void fireLogic(int number, LcnDefs.LogicOpStatus status) { + fireUpdate(LcnChannelGroup.LOGIC, number, new StringType(status.toString())); + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Arrays.asList(PATTERN_ALL, PATTERN_SINGLE_LOGIC); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleMetaAckSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleMetaAckSubHandler.java new file mode 100644 index 0000000000000..6ced1c8c35699 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleMetaAckSubHandler.java @@ -0,0 +1,90 @@ +/** + * 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.Arrays; +import java.util.Collection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +import org.openhab.binding.lcn.internal.LcnModuleHandler; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.connection.ModInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handle Acks received from an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleMetaAckSubHandler extends AbstractLcnModuleSubHandler { + private final Logger logger = LoggerFactory.getLogger(LcnModuleMetaAckSubHandler.class); + /** The pattern for the Ack PCK message */ + public static final Pattern PATTERN_POS = Pattern.compile("-M(?\\d{3})(?\\d{3})!"); + private static final Pattern PATTERN_NEG = Pattern.compile("-M(?\\d{3})(?\\d{3})(?\\d+)"); + + public LcnModuleMetaAckSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + // nothing + } + + @Override + public void handleStatusMessage(Matcher matcher) { + if (matcher.pattern() == PATTERN_POS) { + handler.onAckRceived(); + } else if (matcher.pattern() == PATTERN_NEG) { + logger.warn("{}: NACK received: {}", handler.getStatusMessageAddress(), + codeToString(Integer.parseInt(matcher.group("code")))); + } + } + + private String codeToString(int code) { + switch (code) { + case LcnBindingConstants.CODE_ACK: + return "ACK"; + case 5: + return "Unknown command"; + case 6: + return "Invalid parameter count"; + case 7: + return "Invalid parameter"; + case 8: + return "Command not allowed (e.g. output locked)"; + case 9: + return "Command not allowed by module's configuration"; + case 10: + return "Module not capable"; + case 11: + return "Periphery missing"; + case 12: + return "Programming mode necessary"; + case 14: + return "Mains fuse blown"; + default: + return "Unknown"; + } + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Arrays.asList(PATTERN_POS, PATTERN_NEG); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleMetaFirmwareSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleMetaFirmwareSubHandler.java new file mode 100644 index 0000000000000..59621e8e1261f --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleMetaFirmwareSubHandler.java @@ -0,0 +1,55 @@ +/** + * 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.LcnBindingConstants; +import org.openhab.binding.lcn.internal.LcnModuleHandler; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.connection.ModInfo; + +/** + * Handles serial number and firmware versions received from an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleMetaFirmwareSubHandler extends AbstractLcnModuleSubHandler { + /** The pattern for the serial number and firmware PCK message */ + public static final Pattern PATTERN = Pattern.compile(LcnBindingConstants.ADDRESS_REGEX + + "\\.SN(?[0-9|A-F]{10})(?[0-9|A-F]{2})FW(?[0-9|A-F]{6})HW(?\\d+)"); + + public LcnModuleMetaFirmwareSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + // nothing + } + + @Override + public void handleStatusMessage(Matcher matcher) { + info.setFirmwareVersion(Integer.parseInt(matcher.group("firmwareVersion"), 16)); + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Collections.singleton(PATTERN); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleOutputSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleOutputSubHandler.java new file mode 100644 index 0000000000000..9516b7d46b721 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleOutputSubHandler.java @@ -0,0 +1,185 @@ +/** + * 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.Arrays; +import java.util.Collection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +import org.openhab.binding.lcn.internal.LcnModuleHandler; +import org.openhab.binding.lcn.internal.common.DimmerOutputCommand; +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.common.PckGenerator; +import org.openhab.binding.lcn.internal.connection.ModInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handles Commands and State changes of dimmer outputs of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleOutputSubHandler extends AbstractLcnModuleSubHandler { + private final Logger logger = LoggerFactory.getLogger(LcnModuleOutputSubHandler.class); + private static final int COLOR_RAMP_MS = 1000; + private static final String OUTPUT_COLOR = "color"; + private static final Pattern PERCENT_PATTERN; + private static final Pattern NATIVE_PATTERN; + private volatile HSBType currentColor = new HSBType(); + private volatile PercentType output4 = new PercentType(); + + public LcnModuleOutputSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + static { + PERCENT_PATTERN = Pattern.compile(LcnBindingConstants.ADDRESS_REGEX + "A(?\\d)(?\\d+)"); + NATIVE_PATTERN = Pattern.compile(LcnBindingConstants.ADDRESS_REGEX + "O(?\\d)(?\\d+)"); + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Arrays.asList(NATIVE_PATTERN, PERCENT_PATTERN); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + info.refreshOutput(number); + } + + @Override + public void handleRefresh(String groupId) { + if (OUTPUT_COLOR.equals(groupId)) { + info.refreshAllOutputs(); + } + } + + @Override + public void handleCommandOnOff(OnOffType command, LcnChannelGroup channelGroup, int number) throws LcnException { + // don't use OnOffType.as() here, because it returns @Nullable + handler.sendPck(PckGenerator.dimOutput(number, command == OnOffType.ON ? 100 : 0, 0)); + } + + @Override + public void handleCommandPercent(PercentType command, LcnChannelGroup channelGroup, int number) + throws LcnException { + handler.sendPck(PckGenerator.dimOutput(number, command.doubleValue(), 0)); + } + + @Override + public void handleCommandPercent(PercentType command, LcnChannelGroup channelGroup, String idWithoutGroup) + throws LcnException { + if (!OUTPUT_COLOR.equals(idWithoutGroup)) { + throw new LcnException("Unknown group ID: " + idWithoutGroup); + } + updateAndSendColor(new HSBType(currentColor.getHue(), currentColor.getSaturation(), command)); + } + + @Override + public void handleCommandHsb(HSBType command, String groupId) throws LcnException { + if (!OUTPUT_COLOR.equals(groupId)) { + throw new LcnException("Unknown group ID: " + groupId); + } + updateAndSendColor(command); + } + + private synchronized void updateAndSendColor(HSBType hsbType) throws LcnException { + currentColor = hsbType; + handler.updateChannel(LcnChannelGroup.OUTPUT, OUTPUT_COLOR, currentColor); + + if (info.getFirmwareVersion() >= LcnBindingConstants.FIRMWARE_2014) { + handler.sendPck(PckGenerator.dimAllOutputs(currentColor.getRed().doubleValue(), + currentColor.getGreen().doubleValue(), currentColor.getBlue().doubleValue(), output4.doubleValue(), + COLOR_RAMP_MS)); + } else { + handler.sendPck(PckGenerator.dimOutput(0, currentColor.getRed().doubleValue(), COLOR_RAMP_MS)); + handler.sendPck(PckGenerator.dimOutput(1, currentColor.getGreen().doubleValue(), COLOR_RAMP_MS)); + handler.sendPck(PckGenerator.dimOutput(2, currentColor.getBlue().doubleValue(), COLOR_RAMP_MS)); + } + } + + @Override + public void handleCommandDimmerOutput(DimmerOutputCommand command, int number) throws LcnException { + int rampMs = command.getRampMs(); + if (command.isControlAllOutputs()) { // control all dimmer outputs + if (rampMs == LcnDefs.FIXED_RAMP_MS) { + // compatibility command + handler.sendPck(PckGenerator.controlAllOutputs(command.intValue())); + } else { + // command since firmware 180501 + handler.sendPck(PckGenerator.dimAllOutputs(command.doubleValue(), command.doubleValue(), + command.doubleValue(), command.doubleValue(), rampMs)); + } + } else if (command.isControlOutputs12()) { // control dimmer outputs 1+2 + if (command.intValue() == 0 || command.intValue() == 100) { + handler.sendPck(PckGenerator.controlOutputs12(command.intValue() > 0, rampMs >= LcnDefs.FIXED_RAMP_MS)); + } else { + // ignore ramp when dimming + handler.sendPck(PckGenerator.dimOutputs12(command.doubleValue())); + } + } else { + handler.sendPck(PckGenerator.dimOutput(number, command.doubleValue(), rampMs)); + } + } + + @Override + public void handleStatusMessage(Matcher matcher) { + int outputId = Integer.parseInt(matcher.group("outputId")) - 1; + + if (!LcnChannelGroup.OUTPUT.isValidId(outputId)) { + logger.warn("outputId out of range: {}", outputId); + return; + } + double percent; + if (matcher.pattern() == PERCENT_PATTERN) { + percent = Integer.parseInt(matcher.group("percent")); + } else if (matcher.pattern() == NATIVE_PATTERN) { + percent = (double) Integer.parseInt(matcher.group("value")) / 2; + } else { + logger.warn("Unexpected pattern: {}", matcher.pattern()); + return; + } + if (percent < 0 || percent > 100) { + logger.warn("Output value out of range: {}", percent); + return; + } + info.onOutputResponseReceived(outputId); + + PercentType percentType = new PercentType((int) Math.round(percent)); + fireUpdate(LcnChannelGroup.OUTPUT, outputId, percentType); + + if (outputId == 3) { + synchronized (this) { + output4 = percentType; + } + } + + if (percent > 0) { + if (outputId == 0) { + fireUpdate(LcnChannelGroup.ROLLERSHUTTEROUTPUT, 0, UpDownType.UP); + } else if (outputId == 1) { + fireUpdate(LcnChannelGroup.ROLLERSHUTTEROUTPUT, 0, UpDownType.DOWN); + } + } + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRelaySubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRelaySubHandler.java new file mode 100644 index 0000000000000..d2b166cb67adc --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRelaySubHandler.java @@ -0,0 +1,86 @@ +/** + * 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 java.util.stream.IntStream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +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.LcnDefs.RelayStateModifier; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.PckGenerator; +import org.openhab.binding.lcn.internal.connection.ModInfo; + +/** + * Handles Commands and State changes of Relays of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleRelaySubHandler extends AbstractLcnModuleSubHandler { + private static final Pattern PATTERN = Pattern.compile(LcnBindingConstants.ADDRESS_REGEX + "Rx(?\\d+)"); + + public LcnModuleRelaySubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + info.refreshRelays(); + } + + @Override + public void handleCommandOnOff(OnOffType command, LcnChannelGroup channelGroup, int number) throws LcnException { + RelayStateModifier[] relayStateModifiers = createRelayStateModifierArray(); + relayStateModifiers[number] = command == OnOffType.ON ? LcnDefs.RelayStateModifier.ON + : LcnDefs.RelayStateModifier.OFF; + handler.sendPck(PckGenerator.controlRelays(relayStateModifiers)); + } + + @Override + public void handleCommandPercent(PercentType command, LcnChannelGroup channelGroup, int number) + throws LcnException { + // don't use OnOffType.as(), because it returns @Nullable + handleCommandOnOff(command.intValue() > 0 ? OnOffType.ON : OnOffType.OFF, channelGroup, number); + } + + @Override + public void handleStatusMessage(Matcher matcher) { + info.onRelayResponseReceived(); + + boolean[] states = LcnDefs.getBooleanValue(Integer.parseInt(matcher.group("byteValue"))); + + IntStream.range(0, LcnChannelGroup.RELAY.getCount()) + .forEach(i -> fireUpdate(LcnChannelGroup.RELAY, i, OnOffType.from(states[i]))); + + IntStream.range(0, LcnChannelGroup.ROLLERSHUTTERRELAY.getCount()).forEach(i -> { + UpDownType state = states[i * 2 + 1] ? UpDownType.DOWN : UpDownType.UP; + fireUpdate(LcnChannelGroup.ROLLERSHUTTERRELAY, i, state); + }); + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Collections.singleton(PATTERN); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterOutputSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterOutputSubHandler.java new file mode 100644 index 0000000000000..71b7521ebcd93 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterOutputSubHandler.java @@ -0,0 +1,82 @@ +/** + * 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.eclipse.smarthome.core.library.types.StopMoveType; +import org.eclipse.smarthome.core.library.types.UpDownType; +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.common.PckGenerator; +import org.openhab.binding.lcn.internal.connection.ModInfo; + +/** + * Handles Commands and State changes of roller shutters connected to dimmer outputs of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleRollershutterOutputSubHandler extends AbstractLcnModuleSubHandler { + public LcnModuleRollershutterOutputSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + info.refreshOutput(number); + } + + @Override + public void handleCommandUpDown(UpDownType command, LcnChannelGroup channelGroup, int number) throws LcnException { + // When configured as shutter in LCN-PRO, an output gets switched off, when the other is + // switched on and vice versa. + if (command == UpDownType.UP) { + // first output: 100% + handler.sendPck(PckGenerator.dimOutput(0, 100, LcnDefs.ROLLER_SHUTTER_RAMP_MS)); + } else { + // second output: 100% + handler.sendPck(PckGenerator.dimOutput(1, 100, LcnDefs.ROLLER_SHUTTER_RAMP_MS)); + } + } + + @Override + public void handleCommandStopMove(StopMoveType command, LcnChannelGroup channelGroup, int number) + throws LcnException { + if (command == StopMoveType.STOP) { + // both outputs off + handler.sendPck(PckGenerator.dimOutput(0, 0, 0)); + handler.sendPck(PckGenerator.dimOutput(1, 0, 0)); + } else { + // roller shutters on outputs are stateless, assume always down when MOVE is sent + // second output: 100% + handler.sendPck(PckGenerator.dimOutput(1, 100, LcnDefs.ROLLER_SHUTTER_RAMP_MS)); + } + } + + @Override + public void handleStatusMessage(Matcher matcher) { + // status messages of roller shutters on dimmer outputs are handled in the dimmer output sub handler + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Collections.emptyList(); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterRelaySubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterRelaySubHandler.java new file mode 100644 index 0000000000000..ef46add8a5f01 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterRelaySubHandler.java @@ -0,0 +1,77 @@ +/** + * 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.eclipse.smarthome.core.library.types.StopMoveType; +import org.eclipse.smarthome.core.library.types.UpDownType; +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.LcnDefs.RelayStateModifier; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.PckGenerator; +import org.openhab.binding.lcn.internal.connection.ModInfo; + +/** + * Handles Commands and State changes of roller shutters connected to relays of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleRollershutterRelaySubHandler extends AbstractLcnModuleSubHandler { + public LcnModuleRollershutterRelaySubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + info.refreshRelays(); + } + + @Override + public void handleCommandUpDown(UpDownType command, LcnChannelGroup channelGroup, int number) throws LcnException { + RelayStateModifier[] relayStateModifiers = createRelayStateModifierArray(); + // direction relay + relayStateModifiers[number * 2 + 1] = command == UpDownType.DOWN ? LcnDefs.RelayStateModifier.ON + : LcnDefs.RelayStateModifier.OFF; + // power relay + relayStateModifiers[number * 2] = LcnDefs.RelayStateModifier.ON; + handler.sendPck(PckGenerator.controlRelays(relayStateModifiers)); + } + + @Override + public void handleCommandStopMove(StopMoveType command, LcnChannelGroup channelGroup, int number) + throws LcnException { + RelayStateModifier[] relayStateModifiers = createRelayStateModifierArray(); + // power relay + relayStateModifiers[number * 2] = command == StopMoveType.MOVE ? LcnDefs.RelayStateModifier.ON + : LcnDefs.RelayStateModifier.OFF; + handler.sendPck(PckGenerator.controlRelays(relayStateModifiers)); + } + + @Override + public void handleStatusMessage(Matcher matcher) { + // status messages of roller shutters on relays are handled in the relay sub handler + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Collections.emptyList(); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarLockSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarLockSubHandler.java new file mode 100644 index 0000000000000..a2611cc38213c --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarLockSubHandler.java @@ -0,0 +1,66 @@ +/** + * 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.eclipse.smarthome.core.library.types.OnOffType; +import org.openhab.binding.lcn.internal.LcnModuleHandler; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.PckGenerator; +import org.openhab.binding.lcn.internal.common.Variable; +import org.openhab.binding.lcn.internal.connection.ModInfo; + +/** + * Handles Commands and State changes of regulator locks of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleRvarLockSubHandler extends AbstractLcnModuleVariableSubHandler { + public LcnModuleRvarLockSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleRefresh(LcnChannelGroup channelGroup, int number) { + super.handleRefresh(LcnChannelGroup.RVARSETPOINT, number); + } + + @Override + public void handleCommandOnOff(OnOffType command, LcnChannelGroup channelGroup, int number) throws LcnException { + boolean locked = command == OnOffType.ON; + handler.sendPck(PckGenerator.lockRegulator(number, locked)); + + // request new lock state, if the module doesn't send it on itself + Variable variable = getVariable(LcnChannelGroup.RVARSETPOINT, number); + if (variable.shouldPollStatusAfterRegulatorLock(info.getFirmwareVersion(), locked)) { + info.refreshVariable(variable); + } + } + + @Override + public void handleStatusMessage(Matcher matcher) { + // status messages are handled in the RVar setpoint sub handler + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Collections.emptyList(); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarSetpointSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarSetpointSubHandler.java new file mode 100644 index 0000000000000..5bf2d0c29c79d --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarSetpointSubHandler.java @@ -0,0 +1,79 @@ +/** + * 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.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +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.common.PckGenerator; +import org.openhab.binding.lcn.internal.common.Variable; +import org.openhab.binding.lcn.internal.common.VariableValue; +import org.openhab.binding.lcn.internal.connection.ModInfo; + +/** + * Handles Commands and State changes of regulator setpoints of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleRvarSetpointSubHandler extends AbstractLcnModuleVariableSubHandler { + private static final Pattern PATTERN = Pattern + .compile(LcnBindingConstants.ADDRESS_REGEX + "\\.S(?\\d)(?\\d+)"); + + public LcnModuleRvarSetpointSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleCommandDecimal(DecimalType command, LcnChannelGroup channelGroup, int number) + throws LcnException { + Variable variable = getVariable(channelGroup, number); + + if (info.hasExtendedMeasurementProcessing()) { + handler.sendPck(PckGenerator.setSetpointAbsolute(number, command.intValue())); + } else { + try { + int relativeVariableChange = getRelativeChange(command, variable); + handler.sendPck( + PckGenerator.setSetpointRelative(number, LcnDefs.RelVarRef.CURRENT, relativeVariableChange)); + } catch (LcnException e) { + // current value unknown for some reason, refresh it in case we come again here + info.refreshVariable(variable); + throw e; + } + } + } + + @Override + public void handleStatusMessage(Matcher matcher) throws LcnException { + Variable variable = Variable.setPointIdToVar(Integer.parseInt(matcher.group("id")) - 1); + VariableValue value = fireUpdateAndReset(matcher, "", variable); + + fireUpdate(LcnChannelGroup.RVARLOCK, variable.getNumber(), OnOffType.from(value.isRegulatorLocked())); + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Collections.singleton(PATTERN); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleS0CounterSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleS0CounterSubHandler.java new file mode 100644 index 0000000000000..428b8352f822c --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleS0CounterSubHandler.java @@ -0,0 +1,58 @@ +/** + * 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.eclipse.smarthome.core.library.types.DecimalType; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +import org.openhab.binding.lcn.internal.LcnModuleHandler; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.Variable; +import org.openhab.binding.lcn.internal.connection.ModInfo; + +/** + * Handles Commands and State changes of S0 counter inputs of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleS0CounterSubHandler extends AbstractLcnModuleVariableSubHandler { + private static final Pattern PATTERN = Pattern + .compile(LcnBindingConstants.ADDRESS_REGEX + "\\.C(?\\d)(?\\d+)"); + + public LcnModuleS0CounterSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleCommandDecimal(DecimalType command, LcnChannelGroup channelGroup, int number) + throws LcnException { + throw new LcnException("Setting S0 counters is not supported"); + } + + @Override + public void handleStatusMessage(Matcher matcher) throws LcnException { + fireUpdateAndReset(matcher, "", Variable.s0IdToVar(Integer.parseInt(matcher.group("id")) - 1)); + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Collections.singleton(PATTERN); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleThresholdSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleThresholdSubHandler.java new file mode 100644 index 0000000000000..9b7a0f7f57ea3 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleThresholdSubHandler.java @@ -0,0 +1,104 @@ +/** + * 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.Arrays; +import java.util.Collection; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.IntStream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +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.common.PckGenerator; +import org.openhab.binding.lcn.internal.common.Variable; +import org.openhab.binding.lcn.internal.connection.ModInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handles Commands and State changes of thresholds of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleThresholdSubHandler extends AbstractLcnModuleVariableSubHandler { + private final Logger logger = LoggerFactory.getLogger(LcnModuleThresholdSubHandler.class); + private static final Pattern PATTERN = Pattern + .compile(LcnBindingConstants.ADDRESS_REGEX + "\\.T(?\\d)(?\\d)(?\\d+)"); + private static final Pattern PATTERN_BEFORE_2013 = Pattern.compile(LcnBindingConstants.ADDRESS_REGEX + + "\\.S1(?\\d{5})(?\\d{5})(?\\d{5})(?\\d{5})(?\\d{5})(?\\d{5})"); + + public LcnModuleThresholdSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleCommandDecimal(DecimalType command, LcnChannelGroup channelGroup, int number) + throws LcnException { + Variable variable = getVariable(channelGroup, number); + try { + int relativeChange = getRelativeChange(command, variable); + handler.sendPck(PckGenerator.setThresholdRelative(variable, LcnDefs.RelVarRef.CURRENT, relativeChange, + info.hasExtendedMeasurementProcessing())); + + // request new value, if the module doesn't send it on itself + if (variable.shouldPollStatusAfterCommand(info.getFirmwareVersion())) { + info.refreshVariable(variable); + } + } catch (LcnException e) { + // current value unknown for some reason, refresh it in case we come again here + info.refreshVariable(variable); + throw e; + } + } + + @Override + public void handleStatusMessage(Matcher matcher) { + IntStream stream; + Optional groupSuffix; + int registerNumber; + if (matcher.pattern() == PATTERN) { + int thresholdId = Integer.parseInt(matcher.group("thresholdId")) - 1; + registerNumber = Integer.parseInt(matcher.group("registerId")) - 1; + stream = IntStream.rangeClosed(thresholdId, thresholdId); + groupSuffix = Optional.of(""); + } else if (matcher.pattern() == PATTERN_BEFORE_2013) { + stream = IntStream.range(0, LcnDefs.THRESHOLD_COUNT_BEFORE_2013); + groupSuffix = Optional.empty(); + registerNumber = 0; + } else { + logger.warn("Unexpected pattern: {}", matcher.pattern()); + return; + } + + stream.forEach(i -> { + try { + fireUpdateAndReset(matcher, groupSuffix.orElse(i + ""), Variable.thrsIdToVar(registerNumber, i)); + } catch (LcnException e) { + logger.warn("Parse error: {}", e.getMessage()); + } + }); + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Arrays.asList(PATTERN, PATTERN_BEFORE_2013); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleVariableSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleVariableSubHandler.java new file mode 100644 index 0000000000000..ac1ce6f3cc945 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleVariableSubHandler.java @@ -0,0 +1,88 @@ +/** + * 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.Arrays; +import java.util.Collection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.openhab.binding.lcn.internal.LcnBindingConstants; +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.common.PckGenerator; +import org.openhab.binding.lcn.internal.common.Variable; +import org.openhab.binding.lcn.internal.connection.ModInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Handles Commands and State changes of variables of an LCN module. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleVariableSubHandler extends AbstractLcnModuleVariableSubHandler { + private final Logger logger = LoggerFactory.getLogger(LcnModuleVariableSubHandler.class); + private static final Pattern PATTERN = Pattern + .compile(LcnBindingConstants.ADDRESS_REGEX + "\\.A(?\\d{3})(?\\d+)"); + private static final Pattern PATTERN_LEGACY = Pattern + .compile(LcnBindingConstants.ADDRESS_REGEX + "\\.(?\\d+)"); + + public LcnModuleVariableSubHandler(LcnModuleHandler handler, ModInfo info) { + super(handler, info); + } + + @Override + public void handleCommandDecimal(DecimalType command, LcnChannelGroup channelGroup, int number) + throws LcnException { + Variable variable = getVariable(channelGroup, number); + try { + int relativeChange = getRelativeChange(command, variable); + handler.sendPck(PckGenerator.setVariableRelative(variable, LcnDefs.RelVarRef.CURRENT, relativeChange)); + + // request new value, if the module doesn't send it on itself + if (variable.shouldPollStatusAfterCommand(info.getFirmwareVersion())) { + info.refreshVariable(variable); + } + } catch (LcnException e) { + // current value unknown for some reason, refresh it in case we come again here + info.refreshVariable(variable); + throw e; + } + } + + @Override + public void handleStatusMessage(Matcher matcher) throws LcnException { + Variable variable; + if (matcher.pattern() == PATTERN) { + variable = Variable.varIdToVar(Integer.parseInt(matcher.group("id")) - 1); + } else if (matcher.pattern() == PATTERN_LEGACY) { + variable = info.getLastRequestedVarWithoutTypeInResponse(); + info.setLastRequestedVarWithoutTypeInResponse(Variable.UNKNOWN); // Reset + } else { + logger.warn("Unexpected pattern: {}", matcher.pattern()); + return; + } + fireUpdateAndReset(matcher, "", variable); + } + + @Override + public Collection getPckStatusMessagePatterns() { + return Arrays.asList(PATTERN, PATTERN_LEGACY); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..c494e66acc295 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,10 @@ + + + + LCN Binding + This is the binding for Local Control Network (LCN) + Fabian Wolter + + diff --git a/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/config/config.xml b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/config/config.xml new file mode 100644 index 0000000000000..cf111d56acf93 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/config/config.xml @@ -0,0 +1,100 @@ + + + + + + + The hostname or the IP address of the PCK gateway + network-address + true + + + + The IP port of the PCK gateway + 4114 + true + + + + The login username of the PCK gateway + true + + + + The login password of the PCK gateway + password + + + + IMPORTANT: Dimming range of all modules. Must be the same value as configured in LCN-PRO (Options/Settings/Expert Settings). If you only have modules with firmware newer than Feb. 2013, you probably want to choose 0 - 200.]]> + native200 + + + + + true + + + + Period after which an LCN command is resent, when no acknowledge has been received (in ms). + 3500 + true + + + + + + + The module ID, configured in LCN-PRO + + + + The segment ID the module is in (0 if no segments are present) + + + + + + + The group number, configured in LCN-PRO + + + + The module ID of any module in the group. The state of this module is used for visualization of the group as representative for all modules. + + + + The segment ID of all modules in this group (0 if no segments are present) + 0 + + + + + + + Unit of the sensor + native + + + + + + + + + + + + + true + + + + Only for S0 counters (power or energy) + + + 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 new file mode 100644 index 0000000000000..29ca7960c205e --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/i18n/lcn_de.properties @@ -0,0 +1,171 @@ +# binding +binding.lcn.name = LCN Binding +binding.lcn.description = Binding für Local Control Network (LCN) + +# thing types +thing-type.lcn.pckGateway.label = LCN-PCK-Koppler +thing-type.lcn.pckGateway.description = z.B. die LCN-PCHK-Software oder das Hutschienenmodul LCN-PKE +thing-type.lcn.module.label = LCN-Modul +thing-type.lcn.module.description = z.B. LCN-UPP, LCN-SH, LCN-HU +thing-type.lcn.group.label = LCN-Gruppe +thing-type.lcn.group.description = Eine Gruppe mit mehreren Modulen, wie in LCN-PRO parametriert + +# thing type config description +thing-type.config.lcn.pckGateway.hostname.description = Hostname oder die IP-Adresse des PCK-Kopplers +thing-type.config.lcn.pckGateway.port.description = Netzwerk-Port auf dem der PCK-Koppler läuft +thing-type.config.lcn.pckGateway.username.description = Benutzername vom PCK-Koppler +thing-type.config.lcn.pckGateway.password.description = Login-Passwort vom PCK-Koppler +thing-type.config.lcn.pckGateway.mode.description = WICHTIG: Der Dimmbereich von allen LCN-Modulen. Muss der gleiche Wert, wie in LCN-PRO sein (Optionen/Einstellungen/Experteneinstellungen). Wenn nur Module älter als 2013 im Bus vorhanden sind, muss hier wahrscheinlich 0 - 200 ausgewählt werden. +thing-type.config.lcn.pckGateway.timeoutMs.description = Zeit nach der eine PCK-Nachricht erneut gesendet wird, wenn vom Modul keine positive Quittung empfangen wurde. + +thing-type.config.lcn.module.moduleId.label = Modul-ID +thing-type.config.lcn.module.moduleId.description = Modul-ID, wie in LCN-PRO parametriert +thing-type.config.lcn.module.segmentId.label = Segment-ID +thing-type.config.lcn.module.segmentId.description = ID des Segments, in dem sich das Modul befindet (0 wenn keine Segmente vorhanden sind) + +thing-type.config.lcn.group.groupId.label = Gruppennummer +thing-type.config.lcn.group.groupId.description = Nummer der Gruppe, wie in LCN-PRO parametriert +thing-type.config.lcn.group.moduleId.label = Modul-ID eines Moduls aus der Gruppe +thing-type.config.lcn.group.moduleId.description = Der Zustand dieses Moduls wird zur Visualisierung der Gruppe, stellvertretend für alle Module, genutzt +thing-type.config.lcn.group.segmentId.label = Segment-ID +thing-type.config.lcn.group.segmentId.description = Segment-ID in dem sich die Module der Gruppe befinden (0 wenn keine Segmente vorhanden sind) + +# channel type config description +channel-type.config.lcn.variable.unit.label = Einheit +channel-type.config.lcn.variable.unit.description = Einheit des Sensors +channel-type.config.lcn.variable.unit.option.native = LCN-Wert +channel-type.config.lcn.variable.unit.option.temperature = Temperatur (°C) +channel-type.config.lcn.variable.unit.option.light = Licht (Lux) +channel-type.config.lcn.variable.unit.option.co2 = CO\u2082 (ppm) +channel-type.config.lcn.variable.unit.option.power = Leistung (W) +channel-type.config.lcn.variable.unit.option.energy = Zählerstand (kWh) +channel-type.config.lcn.variable.unit.option.current = Strom (mA) +channel-type.config.lcn.variable.unit.option.voltage = Spannung (V) +channel-type.config.lcn.variable.unit.option.angle = Winkel (°) +channel-type.config.lcn.variable.unit.option.windspeed = Windgeschwindigkeit (m/s) +channel-type.config.lcn.variable.parameter.label = Impulse pro kWh +channel-type.config.lcn.variable.parameter.description = Nur für S0-Zähler + +# channel types +channel-group-type.lcn.outputs.label = Ausgänge +channel-group-type.lcn.outputs.channel.1.label = Ausgang 1 +channel-group-type.lcn.outputs.channel.2.label = Ausgang 2 +channel-group-type.lcn.outputs.channel.3.label = Ausgang 3 +channel-group-type.lcn.outputs.channel.4.label = Ausgang 4 +channel-group-type.lcn.outputs.channel.color.label = RGB-Steuerung für Ausgänge 1-3 +channel-group-type.lcn.rollershutteroutputs.label = Rollläden an Ausgängen +channel-group-type.lcn.rollershutteroutputs.channel.1.label = Rollläden an Ausgängen 1+2 +channel-group-type.lcn.relays.label = Relais +channel-group-type.lcn.relays.channel.1.label = Relais 1 +channel-group-type.lcn.relays.channel.2.label = Relais 2 +channel-group-type.lcn.relays.channel.3.label = Relais 3 +channel-group-type.lcn.relays.channel.4.label = Relais 4 +channel-group-type.lcn.relays.channel.5.label = Relais 5 +channel-group-type.lcn.relays.channel.6.label = Relais 6 +channel-group-type.lcn.relays.channel.7.label = Relais 7 +channel-group-type.lcn.relays.channel.8.label = Relais 8 +channel-group-type.lcn.rollershutterrelays.label = Rollläden an Relais +channel-group-type.lcn.rollershutterrelays.channel.1.label = Rollläden an Relais 1+2 +channel-group-type.lcn.rollershutterrelays.channel.2.label = Rollläden an Relais 3+4 +channel-group-type.lcn.rollershutterrelays.channel.3.label = Rollläden an Relais 5+6 +channel-group-type.lcn.rollershutterrelays.channel.4.label = Rollläden an Relais 7+8 +channel-group-type.lcn.logics.label = Logik-Funktionen +channel-group-type.lcn.logics.channel.1.label = Logik-Funktion 1 +channel-group-type.lcn.logics.channel.2.label = Logik-Funktion 2 +channel-group-type.lcn.logics.channel.3.label = Logik-Funktion 3 +channel-group-type.lcn.logics.channel.4.label = Logik-Funktion 4 +channel-group-type.lcn.binarysensors.label = Binärsensoren +channel-group-type.lcn.binarysensors.channel.1.label = Binärsensor 1 +channel-group-type.lcn.binarysensors.channel.2.label = Binärsensor 2 +channel-group-type.lcn.binarysensors.channel.3.label = Binärsensor 3 +channel-group-type.lcn.binarysensors.channel.4.label = Binärsensor 4 +channel-group-type.lcn.binarysensors.channel.5.label = Binärsensor 5 +channel-group-type.lcn.binarysensors.channel.6.label = Binärsensor 6 +channel-group-type.lcn.binarysensors.channel.7.label = Binärsensor 7 +channel-group-type.lcn.binarysensors.channel.8.label = Binärsensor 8 +channel-group-type.lcn.variables.label = Variablen +channel-group-type.lcn.variables.channel.1.label = Variable 1 +channel-group-type.lcn.variables.channel.2.label = Variable 2 +channel-group-type.lcn.variables.channel.3.label = Variable 3 +channel-group-type.lcn.variables.channel.4.label = Variable 4 +channel-group-type.lcn.variables.channel.5.label = Variable 5 +channel-group-type.lcn.variables.channel.6.label = Variable 6 +channel-group-type.lcn.variables.channel.7.label = Variable 7 +channel-group-type.lcn.variables.channel.8.label = Variable 8 +channel-group-type.lcn.variables.channel.9.label = Variable 9 +channel-group-type.lcn.variables.channel.10.label = Variable 10 +channel-group-type.lcn.variables.channel.11.label = Variable 11 +channel-group-type.lcn.variables.channel.12.label = Variable 12 +channel-group-type.lcn.rvarsetpoints.label = Regler +channel-group-type.lcn.rvarsetpoints.channel.1.label = Regler 1 Sollwert +channel-group-type.lcn.rvarsetpoints.channel.2.label = Regler 2 Sollwert +channel-group-type.lcn.rvarlocks.label = Regler Sperren +channel-group-type.lcn.rvarlocks.channel.1.label = Regler 1 Sperre +channel-group-type.lcn.rvarlocks.channel.2.label = Regler 2 Sperre +channel-group-type.lcn.thresholdregisters1.label = Schwellwertregister 1 +channel-group-type.lcn.thresholdregisters1.channel.1.label = Schwellwert 1 +channel-group-type.lcn.thresholdregisters1.channel.2.label = Schwellwert 2 +channel-group-type.lcn.thresholdregisters1.channel.3.label = Schwellwert 3 +channel-group-type.lcn.thresholdregisters1.channel.4.label = Schwellwert 4 +channel-group-type.lcn.thresholdregisters1.channel.5.label = Schwellwert 5 +channel-group-type.lcn.thresholdregisters2.label = Schwellwertregister 2 +channel-group-type.lcn.thresholdregisters2.channel.1.label = Schwellwert 1 +channel-group-type.lcn.thresholdregisters2.channel.2.label = Schwellwert 2 +channel-group-type.lcn.thresholdregisters2.channel.3.label = Schwellwert 3 +channel-group-type.lcn.thresholdregisters2.channel.4.label = Schwellwert 4 +channel-group-type.lcn.thresholdregisters3.label = Schwellwertregister 3 +channel-group-type.lcn.thresholdregisters3.channel.1.label = Schwellwert 1 +channel-group-type.lcn.thresholdregisters3.channel.2.label = Schwellwert 2 +channel-group-type.lcn.thresholdregisters3.channel.3.label = Schwellwert 3 +channel-group-type.lcn.thresholdregisters3.channel.4.label = Schwellwert 4 +channel-group-type.lcn.thresholdregisters4.label = Schwellwertregister 4 +channel-group-type.lcn.thresholdregisters4.channel.1.label = Schwellwert 1 +channel-group-type.lcn.thresholdregisters4.channel.2.label = Schwellwert 2 +channel-group-type.lcn.thresholdregisters4.channel.3.label = Schwellwert 3 +channel-group-type.lcn.thresholdregisters4.channel.4.label = Schwellwert 4 +channel-group-type.lcn.s0inputs.label = S0-Zähler +channel-group-type.lcn.s0inputs.channel.1.label = S0-Zähler 1 +channel-group-type.lcn.s0inputs.channel.2.label = S0-Zähler 2 +channel-group-type.lcn.s0inputs.channel.3.label = S0-Zähler 3 +channel-group-type.lcn.s0inputs.channel.4.label = S0-Zähler 4 +channel-group-type.lcn.keyslocktablea.label = Tastensperren Tabelle A +channel-group-type.lcn.keyslocktablea.channel.1.label = A1 Sperre +channel-group-type.lcn.keyslocktablea.channel.2.label = A2 Sperre +channel-group-type.lcn.keyslocktablea.channel.3.label = A3 Sperre +channel-group-type.lcn.keyslocktablea.channel.4.label = A4 Sperre +channel-group-type.lcn.keyslocktablea.channel.5.label = A5 Sperre +channel-group-type.lcn.keyslocktablea.channel.6.label = A6 Sperre +channel-group-type.lcn.keyslocktablea.channel.7.label = A7 Sperre +channel-group-type.lcn.keyslocktablea.channel.8.label = A8 Sperre +channel-group-type.lcn.keyslocktableb.label = Tastensperren Tabelle B +channel-group-type.lcn.keyslocktableb.channel.1.label = B1 Sperre +channel-group-type.lcn.keyslocktableb.channel.2.label = B2 Sperre +channel-group-type.lcn.keyslocktableb.channel.3.label = B3 Sperre +channel-group-type.lcn.keyslocktableb.channel.4.label = B4 Sperre +channel-group-type.lcn.keyslocktableb.channel.5.label = B5 Sperre +channel-group-type.lcn.keyslocktableb.channel.6.label = B6 Sperre +channel-group-type.lcn.keyslocktableb.channel.7.label = B7 Sperre +channel-group-type.lcn.keyslocktableb.channel.8.label = B8 Sperre +channel-group-type.lcn.keyslocktablec.label = Tastensperren Tabelle C +channel-group-type.lcn.keyslocktablec.channel.1.label = C1 Sperre +channel-group-type.lcn.keyslocktablec.channel.2.label = C2 Sperre +channel-group-type.lcn.keyslocktablec.channel.3.label = C3 Sperre +channel-group-type.lcn.keyslocktablec.channel.4.label = C4 Sperre +channel-group-type.lcn.keyslocktablec.channel.5.label = C5 Sperre +channel-group-type.lcn.keyslocktablec.channel.6.label = C6 Sperre +channel-group-type.lcn.keyslocktablec.channel.7.label = C7 Sperre +channel-group-type.lcn.keyslocktablec.channel.8.label = C8 Sperre +channel-group-type.lcn.keyslocktabled.label = Tastensperren Tabelle D +channel-group-type.lcn.keyslocktabled.channel.1.label = D1 Sperre +channel-group-type.lcn.keyslocktabled.channel.2.label = D2 Sperre +channel-group-type.lcn.keyslocktabled.channel.3.label = D3 Sperre +channel-group-type.lcn.keyslocktabled.channel.4.label = D4 Sperre +channel-group-type.lcn.keyslocktabled.channel.5.label = D5 Sperre +channel-group-type.lcn.keyslocktabled.channel.6.label = D6 Sperre +channel-group-type.lcn.keyslocktabled.channel.7.label = D7 Sperre +channel-group-type.lcn.keyslocktabled.channel.8.label = D8 Sperre +channel-group-type.lcn.codes.label = Transponder & Fernbedienungen +channel-group-type.lcn.codes.channel.transponder.label = Transponder-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 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 new file mode 100644 index 0000000000000..75c16c09166b9 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/thing/thing-types.xml @@ -0,0 +1,637 @@ + + + + + + An LCN gateway speaking the PCK language. E.g. LCN-PCHK software or the DIN rail device LCN-PKE. + + + + + + + + + + + An LCN bus module, e.g. LCN-UPP, LCN-SH, LCN-HU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + An LCN group with multiple modules, configured in LCN-PRO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dimmer + + veto + + + + Color + + veto + + + + + + + + + + + + + + + + + + + + + + + + + Switch + + veto + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Roller Shutter + + veto + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String + + + + + + + + + + veto + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String + + + + + + + + + veto + + + + + + + + + + + + + + + + + + + + + + Contact + + veto + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Number + + veto + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Switch + + veto + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Switch + + veto + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trigger + + + + + + trigger + + + + + + trigger + + + + + + trigger + + + + diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/ModuleActionsTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/ModuleActionsTest.java new file mode 100644 index 0000000000000..04d588e78bef3 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/ModuleActionsTest.java @@ -0,0 +1,193 @@ +/** + * 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; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.MockitoAnnotations; +import org.openhab.binding.lcn.internal.common.LcnDefs; +import org.openhab.binding.lcn.internal.common.LcnException; + +/** + * Test class for {@link LcnModuleActions}. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class ModuleActionsTest { + private LcnModuleActions a = new LcnModuleActions(); + private LcnModuleHandler handler = mock(LcnModuleHandler.class); + @Captor + private @NonNullByDefault({}) ArgumentCaptor byteBufferCaptor; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + a = new LcnModuleActions(); + a.setThingHandler(handler); + } + + private ByteBuffer stringToByteBuffer(String string) throws UnsupportedEncodingException { + ByteBuffer bb = ByteBuffer.wrap(string.getBytes(LcnDefs.LCN_ENCODING)); + bb.position(bb.capacity()); + return bb; + } + + @Test + public void testSendDynamicText1CharRow1() throws LcnException, UnsupportedEncodingException { + a.sendDynamicText(1, "a"); + + verify(handler).sendPck(stringToByteBuffer("GTDT11a\0\0\0\0\0\0\0\0\0\0\0")); + } + + @Test + public void testSendDynamicText1ChunkRow1() throws LcnException, UnsupportedEncodingException { + a.sendDynamicText(1, "abcdfghijklm"); + + verify(handler).sendPck(stringToByteBuffer("GTDT11abcdfghijklm")); + } + + @Test + public void testSendDynamicText1Chunk1CharRow1() throws LcnException, UnsupportedEncodingException { + a.sendDynamicText(1, "abcdfghijklmn"); + + verify(handler, times(2)).sendPck(byteBufferCaptor.capture()); + + assertThat(byteBufferCaptor.getAllValues(), contains(stringToByteBuffer("GTDT11abcdfghijklm"), + stringToByteBuffer("GTDT12n\0\0\0\0\0\0\0\0\0\0\0"))); + } + + @Test + public void testSendDynamicText5ChunksRow1() throws LcnException, UnsupportedEncodingException { + a.sendDynamicText(1, "abcdfghijklmnopqrstuvwxyzabcdfghijklmnopqrstuvwxyzabcdfghijk"); + + verify(handler, times(5)).sendPck(byteBufferCaptor.capture()); + + assertThat(byteBufferCaptor.getAllValues(), + containsInAnyOrder(stringToByteBuffer("GTDT11abcdfghijklm"), stringToByteBuffer("GTDT12nopqrstuvwxy"), + stringToByteBuffer("GTDT13zabcdfghijkl"), stringToByteBuffer("GTDT14mnopqrstuvwx"), + stringToByteBuffer("GTDT15yzabcdfghijk"))); + } + + @Test + public void testSendDynamicText5Chunks1CharRow1Truncated() throws LcnException, UnsupportedEncodingException { + a.sendDynamicText(1, "abcdfghijklmnopqrstuvwxyzabcdfghijklmnopqrstuvwxyzabcdfghijkl"); + + verify(handler, times(5)).sendPck(byteBufferCaptor.capture()); + + assertThat(byteBufferCaptor.getAllValues(), + containsInAnyOrder(stringToByteBuffer("GTDT11abcdfghijklm"), stringToByteBuffer("GTDT12nopqrstuvwxy"), + stringToByteBuffer("GTDT13zabcdfghijkl"), stringToByteBuffer("GTDT14mnopqrstuvwx"), + stringToByteBuffer("GTDT15yzabcdfghijk"))); + } + + @Test + public void testSendDynamicText5Chunks1UmlautRow1Truncated() throws LcnException, UnsupportedEncodingException { + a.sendDynamicText(1, "äcdfghijklmnopqrstuvwxyzabcdfghijklmnopqrstuvwxyzabcdfghijkl"); + + verify(handler, times(5)).sendPck(byteBufferCaptor.capture()); + + assertThat(byteBufferCaptor.getAllValues(), + containsInAnyOrder(stringToByteBuffer("GTDT11äcdfghijklm"), stringToByteBuffer("GTDT12nopqrstuvwxy"), + stringToByteBuffer("GTDT13zabcdfghijkl"), stringToByteBuffer("GTDT14mnopqrstuvwx"), + stringToByteBuffer("GTDT15yzabcdfghijk"))); + } + + @Test + public void testSendDynamicTextRow4() throws LcnException, UnsupportedEncodingException { + a.sendDynamicText(4, "abcdfghijklmn"); + + verify(handler, times(2)).sendPck(byteBufferCaptor.capture()); + + assertThat(byteBufferCaptor.getAllValues(), contains(stringToByteBuffer("GTDT41abcdfghijklm"), + stringToByteBuffer("GTDT42n\0\0\0\0\0\0\0\0\0\0\0"))); + } + + @Test + public void testSendDynamicTextSplitInCharacter() throws LcnException, UnsupportedEncodingException { + a.sendDynamicText(4, "Test 123 öäüß"); + + verify(handler, times(2)).sendPck(byteBufferCaptor.capture()); + + assertThat(byteBufferCaptor.getAllValues(), + contains(stringToByteBuffer("GTDT41Test 123 ö\303"), stringToByteBuffer("GTDT42\244üß\0\0\0\0\0\0"))); + } + + @Test + public void testSendKeysInvalidTable() throws LcnException { + a.hitKey("E", 3, "MAKE"); + verify(handler, times(0)).sendPck(anyString()); + } + + @Test + public void testSendKeysNullTable() throws LcnException { + a.hitKey(null, 3, "MAKE"); + verify(handler, times(0)).sendPck(anyString()); + } + + @Test + public void testSendKeysNullAction() throws LcnException { + a.hitKey("D", 3, null); + verify(handler, times(0)).sendPck(anyString()); + } + + @Test + public void testSendKeysInvalidKey0() throws LcnException { + a.hitKey("D", 0, "MAKE"); + verify(handler, times(0)).sendPck(anyString()); + } + + @Test + public void testSendKeysInvalidKey9() throws LcnException { + a.hitKey("D", 9, "MAKE"); + verify(handler, times(0)).sendPck(anyString()); + } + + @Test + public void testSendKeysInvalidAction() throws LcnException { + a.hitKey("D", 8, "invalid"); + verify(handler, times(0)).sendPck(anyString()); + } + + @Test + public void testSendKeysA1Hit() throws LcnException { + a.hitKey("a", 1, "HIT"); + + verify(handler).sendPck("TSK--10000000"); + } + + @Test + public void testSendKeysC8Hit() throws LcnException { + a.hitKey("C", 8, "break"); + + verify(handler).sendPck("TS--O00000001"); + } + + @Test + public void testSendKeysD3Make() throws LcnException { + a.hitKey("D", 3, "MAKE"); + + verify(handler).sendPck("TS---L00100000"); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryServiceTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryServiceTest.java new file mode 100644 index 0000000000000..38feefcc26362 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryServiceTest.java @@ -0,0 +1,58 @@ +/** + * 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.pchkdiscovery; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@link LcnPchkDiscoveryService}. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnPchkDiscoveryServiceTest { + private LcnPchkDiscoveryService s = new LcnPchkDiscoveryService(); + private ServicesResponse r = s.xmlToServiceResponse(RESPONSE); + private static final String RESPONSE = "LCN-PCHK 3.2.2 running on Unix/LinuxPCHK 3.2.2 bus"; + + @Before + public void setUp() { + s = new LcnPchkDiscoveryService(); + r = s.xmlToServiceResponse(RESPONSE); + } + + @Test + public void testXmlMachineId() { + assertThat(r.getServer().getMachineId(), is("b8:27:eb:fe:a4:bb")); + } + + @Test + public void testXmlMachineName() { + assertThat(r.getServer().getMachineName(), is("raspberrypi")); + } + + @Test + public void testXmlServerContent() { + assertThat(r.getServer().getContent(), is("LCN-PCHK 3.2.2 running on Unix/Linux")); + } + + @Test + public void testXmlPort() { + assertThat(r.getExtServices().getExtService().getLocalPort(), is(4114)); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/AbstractTestLcnModuleSubHandler.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/AbstractTestLcnModuleSubHandler.java new file mode 100644 index 0000000000000..7cee0058d48b5 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/AbstractTestLcnModuleSubHandler.java @@ -0,0 +1,43 @@ +/** + * 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.when; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.openhab.binding.lcn.internal.LcnModuleHandler; +import org.openhab.binding.lcn.internal.connection.ModInfo; + +/** + * Test class. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class AbstractTestLcnModuleSubHandler { + @Mock + protected @NonNullByDefault({}) LcnModuleHandler handler; + @Mock + protected @NonNullByDefault({}) ModInfo info; + + public AbstractTestLcnModuleSubHandler() { + setUp(); + } + + public void setUp() { + MockitoAnnotations.initMocks(this); + when(handler.isMyAddress("000", "005")).thenReturn(true); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleBinarySensorSubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleBinarySensorSubHandlerTest.java new file mode 100644 index 0000000000000..ba6f4a2581cf8 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleBinarySensorSubHandlerTest.java @@ -0,0 +1,77 @@ +/** + * 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.eclipse.smarthome.core.library.types.OpenClosedType; +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 LcnModuleBinarySensorSubHandlerTest extends AbstractTestLcnModuleSubHandler { + private @NonNullByDefault({}) LcnModuleBinarySensorSubHandler l; + + @Override + @Before + public void setUp() { + super.setUp(); + + l = new LcnModuleBinarySensorSubHandler(handler, info); + } + + @Test + public void testStatusAllClosed() { + l.tryParse("=M000005Bx000"); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "1", OpenClosedType.CLOSED); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "2", OpenClosedType.CLOSED); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "3", OpenClosedType.CLOSED); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "4", OpenClosedType.CLOSED); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "5", OpenClosedType.CLOSED); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "6", OpenClosedType.CLOSED); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "7", OpenClosedType.CLOSED); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "8", OpenClosedType.CLOSED); + } + + @Test + public void testStatusAllOpen() { + l.tryParse("=M000005Bx255"); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "1", OpenClosedType.OPEN); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "2", OpenClosedType.OPEN); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "3", OpenClosedType.OPEN); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "5", OpenClosedType.OPEN); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "6", OpenClosedType.OPEN); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "7", OpenClosedType.OPEN); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "8", OpenClosedType.OPEN); + } + + @Test + public void testStatus1And7Closed() { + l.tryParse("=M000005Bx065"); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "1", OpenClosedType.OPEN); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "2", OpenClosedType.CLOSED); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "3", OpenClosedType.CLOSED); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "4", OpenClosedType.CLOSED); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "5", OpenClosedType.CLOSED); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "6", OpenClosedType.CLOSED); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "7", OpenClosedType.OPEN); + verify(handler).updateChannel(LcnChannelGroup.BINARYSENSOR, "8", OpenClosedType.CLOSED); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleKeyLockTableSubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleKeyLockTableSubHandlerTest.java new file mode 100644 index 0000000000000..011bc56801970 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleKeyLockTableSubHandlerTest.java @@ -0,0 +1,173 @@ +/** + * 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.eclipse.smarthome.core.library.types.OnOffType; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnException; + +/** + * Test class. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleKeyLockTableSubHandlerTest extends AbstractTestLcnModuleSubHandler { + private @NonNullByDefault({}) LcnModuleKeyLockTableSubHandler l; + + @Override + @Before + public void setUp() { + super.setUp(); + + l = new LcnModuleKeyLockTableSubHandler(handler, info); + } + + @Test + public void testStatus() { + l.tryParse("=M000005.TX098036000255"); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEA, "1", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEA, "2", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEA, "3", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEA, "4", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEA, "5", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEA, "6", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEA, "7", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEA, "8", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEB, "1", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEB, "2", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEB, "3", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEB, "4", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEB, "5", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEB, "6", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEB, "7", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEB, "8", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEC, "1", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEC, "2", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEC, "3", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEC, "4", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEC, "5", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEC, "6", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEC, "7", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLEC, "8", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLED, "1", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLED, "2", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLED, "3", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLED, "4", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLED, "5", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLED, "6", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLED, "7", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.KEYLOCKTABLED, "8", OnOffType.ON); + } + + @Test + public void testHandleCommandA1Off() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.KEYLOCKTABLEA, 0); + verify(handler).sendPck("TXA0-------"); + } + + @Test + public void testHandleCommandA1On() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.KEYLOCKTABLEA, 0); + verify(handler).sendPck("TXA1-------"); + } + + @Test + public void testHandleCommandA8Off() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.KEYLOCKTABLEA, 7); + verify(handler).sendPck("TXA-------0"); + } + + @Test + public void testHandleCommandA8On() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.KEYLOCKTABLEA, 7); + verify(handler).sendPck("TXA-------1"); + } + + @Test + public void testHandleCommandB1Off() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.KEYLOCKTABLEB, 0); + verify(handler).sendPck("TXB0-------"); + } + + @Test + public void testHandleCommandB1On() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.KEYLOCKTABLEB, 0); + verify(handler).sendPck("TXB1-------"); + } + + @Test + public void testHandleCommandB8Off() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.KEYLOCKTABLEB, 7); + verify(handler).sendPck("TXB-------0"); + } + + @Test + public void testHandleCommandB8On() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.KEYLOCKTABLEB, 7); + verify(handler).sendPck("TXB-------1"); + } + + @Test + public void testHandleCommandC1Off() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.KEYLOCKTABLEC, 0); + verify(handler).sendPck("TXC0-------"); + } + + @Test + public void testHandleCommandC1On() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.KEYLOCKTABLEC, 0); + verify(handler).sendPck("TXC1-------"); + } + + @Test + public void testHandleCommandC8Off() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.KEYLOCKTABLEC, 7); + verify(handler).sendPck("TXC-------0"); + } + + @Test + public void testHandleCommandC8On() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.KEYLOCKTABLEC, 7); + verify(handler).sendPck("TXC-------1"); + } + + @Test + public void testHandleCommandD1Off() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.KEYLOCKTABLED, 0); + verify(handler).sendPck("TXD0-------"); + } + + @Test + public void testHandleCommandD1On() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.KEYLOCKTABLED, 0); + verify(handler).sendPck("TXD1-------"); + } + + @Test + public void testHandleCommandD8Off() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.KEYLOCKTABLED, 7); + verify(handler).sendPck("TXD-------0"); + } + + @Test + public void testHandleCommandD8On() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.KEYLOCKTABLED, 7); + verify(handler).sendPck("TXD-------1"); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLedSubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLedSubHandlerTest.java new file mode 100644 index 0000000000000..aea07c1f19923 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLedSubHandlerTest.java @@ -0,0 +1,84 @@ +/** + * 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.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnDefs; +import org.openhab.binding.lcn.internal.common.LcnException; + +/** + * Test class. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleLedSubHandlerTest extends AbstractTestLcnModuleSubHandler { + private @NonNullByDefault({}) LcnModuleLedSubHandler l; + + @Override + @Before + public void setUp() { + super.setUp(); + + l = new LcnModuleLedSubHandler(handler, info); + } + + @Test + public void testHandleCommandLed1Off() throws LcnException { + l.handleCommandString(new StringType(LcnDefs.LedStatus.OFF.name()), 0); + verify(handler).sendPck("LA001A"); + } + + @Test + public void testHandleCommandLed1On() throws LcnException { + l.handleCommandString(new StringType(LcnDefs.LedStatus.ON.name()), 0); + verify(handler).sendPck("LA001E"); + } + + @Test + public void testHandleCommandLed1Blink() throws LcnException { + l.handleCommandString(new StringType(LcnDefs.LedStatus.BLINK.name()), 0); + verify(handler).sendPck("LA001B"); + } + + @Test + public void testHandleCommandLed1Flicker() throws LcnException { + l.handleCommandString(new StringType(LcnDefs.LedStatus.FLICKER.name()), 0); + verify(handler).sendPck("LA001F"); + } + + @Test + public void testHandleCommandLed12On() throws LcnException { + l.handleCommandString(new StringType(LcnDefs.LedStatus.ON.name()), 11); + verify(handler).sendPck("LA012E"); + } + + @Test + public void testHandleOnOffCommandLed1Off() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.LED, 0); + verify(handler).sendPck("LA001A"); + } + + @Test + public void testHandleOnOffCommandLed1On() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.LED, 0); + verify(handler).sendPck("LA001E"); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLogicSubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLogicSubHandlerTest.java new file mode 100644 index 0000000000000..106fd18f6d45d --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleLogicSubHandlerTest.java @@ -0,0 +1,106 @@ +/** + * 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.eclipse.smarthome.core.library.types.StringType; +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 LcnModuleLogicSubHandlerTest extends AbstractTestLcnModuleSubHandler { + private static final StringType ON = new StringType("ON"); + private static final StringType OFF = new StringType("OFF"); + private static final StringType BLINK = new StringType("BLINK"); + private static final StringType FLICKER = new StringType("FLICKER"); + private static final StringType NOT = new StringType("NOT"); + private static final StringType OR = new StringType("OR"); + private static final StringType AND = new StringType("AND"); + private @NonNullByDefault({}) LcnModuleLogicSubHandler l; + + @Override + @Before + public void setUp() { + super.setUp(); + + l = new LcnModuleLogicSubHandler(handler, info); + } + + @Test + public void testStatusLedOffLogicNot() { + l.tryParse("=M000005.TLAAAAAAAAAAAANNNN"); + verify(handler).updateChannel(LcnChannelGroup.LED, "1", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "2", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "3", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "4", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "5", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "6", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "7", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "8", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "9", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "10", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "11", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "12", OFF); + verify(handler).updateChannel(LcnChannelGroup.LOGIC, "1", NOT); + verify(handler).updateChannel(LcnChannelGroup.LOGIC, "2", NOT); + verify(handler).updateChannel(LcnChannelGroup.LOGIC, "3", NOT); + verify(handler).updateChannel(LcnChannelGroup.LOGIC, "4", NOT); + } + + @Test + public void testStatusMixed() { + l.tryParse("=M000005.TLAEBFAAAAAAAFNVNT"); + verify(handler).updateChannel(LcnChannelGroup.LED, "1", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "2", ON); + verify(handler).updateChannel(LcnChannelGroup.LED, "3", BLINK); + verify(handler).updateChannel(LcnChannelGroup.LED, "4", FLICKER); + verify(handler).updateChannel(LcnChannelGroup.LED, "5", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "6", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "7", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "8", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "9", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "10", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "11", OFF); + verify(handler).updateChannel(LcnChannelGroup.LED, "12", FLICKER); + verify(handler).updateChannel(LcnChannelGroup.LOGIC, "1", NOT); + verify(handler).updateChannel(LcnChannelGroup.LOGIC, "2", AND); + verify(handler).updateChannel(LcnChannelGroup.LOGIC, "3", NOT); + verify(handler).updateChannel(LcnChannelGroup.LOGIC, "4", OR); + } + + @Test + public void testStatusSingleLogic1Not() { + l.tryParse("=M000005S1000"); + verify(handler).updateChannel(LcnChannelGroup.LOGIC, "1", NOT); + } + + @Test + public void testStatusSingleLogic4Or() { + l.tryParse("=M000005S4025"); + verify(handler).updateChannel(LcnChannelGroup.LOGIC, "4", OR); + } + + @Test + public void testStatusSingleLogic3And() { + l.tryParse("=M000005S3050"); + verify(handler).updateChannel(LcnChannelGroup.LOGIC, "3", AND); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleOutputSubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleOutputSubHandlerTest.java new file mode 100644 index 0000000000000..e18eebff250cc --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleOutputSubHandlerTest.java @@ -0,0 +1,198 @@ +/** + * 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 java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.lcn.internal.common.DimmerOutputCommand; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnDefs; +import org.openhab.binding.lcn.internal.common.LcnException; + +/** + * Test class. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleOutputSubHandlerTest extends AbstractTestLcnModuleSubHandler { + private @NonNullByDefault({}) LcnModuleOutputSubHandler l; + + @Override + @Before + public void setUp() { + super.setUp(); + + l = new LcnModuleOutputSubHandler(handler, info); + } + + @Test + public void testStatusOutput1OffPercent() { + l.tryParse("=M000005A1000"); + verify(handler).updateChannel(LcnChannelGroup.OUTPUT, "1", new PercentType(0)); + } + + @Test + public void testStatusOutput2OffPercent() { + l.tryParse("=M000005A2000"); + verify(handler).updateChannel(LcnChannelGroup.OUTPUT, "2", new PercentType(0)); + } + + @Test + public void testStatusOutput1OffNative() { + l.tryParse("=M000005O1000"); + verify(handler).updateChannel(LcnChannelGroup.OUTPUT, "1", new PercentType(0)); + } + + @Test + public void testStatusOutput2OffNative() { + l.tryParse("=M000005O2000"); + verify(handler).updateChannel(LcnChannelGroup.OUTPUT, "2", new PercentType(0)); + } + + @Test + public void testStatusOutput1OnPercent() { + l.tryParse("=M000005A1100"); + verify(handler).updateChannel(LcnChannelGroup.OUTPUT, "1", new PercentType(100)); + } + + @Test + public void testStatusOutput2OnPercent() { + l.tryParse("=M000005A2100"); + verify(handler).updateChannel(LcnChannelGroup.OUTPUT, "2", new PercentType(100)); + } + + @Test + public void testStatusOutput1OnNative() { + l.tryParse("=M000005O1200"); + verify(handler).updateChannel(LcnChannelGroup.OUTPUT, "1", new PercentType(100)); + } + + @Test + public void testStatusOutput2OnNative() { + l.tryParse("=M000005O2200"); + verify(handler).updateChannel(LcnChannelGroup.OUTPUT, "2", new PercentType(100)); + } + + @Test + public void testStatusOutput2On50Percent() { + l.tryParse("=M000005A2050"); + verify(handler).updateChannel(LcnChannelGroup.OUTPUT, "2", new PercentType(50)); + } + + @Test + public void testStatusOutput1On50Native() { + l.tryParse("=M000005O1100"); + verify(handler).updateChannel(LcnChannelGroup.OUTPUT, "1", new PercentType(50)); + } + + @Test + public void testHandleCommandOutput1On() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.OUTPUT, 0); + verify(handler).sendPck("A1DI100000"); + } + + @Test + public void testHandleCommandOutput2On() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.OUTPUT, 1); + verify(handler).sendPck("A2DI100000"); + } + + @Test + public void testHandleCommandOutput1Off() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.OUTPUT, 0); + verify(handler).sendPck("A1DI000000"); + } + + @Test + public void testHandleCommandOutput2Off() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.OUTPUT, 1); + verify(handler).sendPck("A2DI000000"); + } + + @Test + public void testHandleCommandOutput1Percent10() throws LcnException { + l.handleCommandPercent(new PercentType(99), LcnChannelGroup.OUTPUT, 0); + verify(handler).sendPck("A1DI099000"); + } + + @Test + public void testHandleCommandOutput2Percent1() throws LcnException { + l.handleCommandPercent(new PercentType(1), LcnChannelGroup.OUTPUT, 1); + verify(handler).sendPck("A2DI001000"); + } + + @Test + public void testHandleCommandOutput1Percent995() throws LcnException { + l.handleCommandPercent(new PercentType(BigDecimal.valueOf(99.5)), LcnChannelGroup.OUTPUT, 0); + verify(handler).sendPck("O1DI199000"); + } + + @Test + public void testHandleCommandOutput2Percent05() throws LcnException { + l.handleCommandPercent(new PercentType(BigDecimal.valueOf(0.5)), LcnChannelGroup.OUTPUT, 1); + verify(handler).sendPck("O2DI001000"); + } + + @Test + public void testHandleCommandDimmerOutputAll60FixedRamp() throws LcnException { + l.handleCommandDimmerOutput(new DimmerOutputCommand(BigDecimal.valueOf(60), true, false, LcnDefs.FIXED_RAMP_MS), + 0); + verify(handler).sendPck("AH060"); + } + + @Test + public void testHandleCommandDimmerOutputAll40CustomRamp() throws LcnException { + l.handleCommandDimmerOutput(new DimmerOutputCommand(BigDecimal.valueOf(40), true, false, 1000), 0); + verify(handler).sendPck("OY080080080080004"); + } + + @Test + public void testHandleCommandDimmerOutput12Value100FixedRamp() throws LcnException { + l.handleCommandDimmerOutput( + new DimmerOutputCommand(BigDecimal.valueOf(100), false, true, LcnDefs.FIXED_RAMP_MS), 0); + verify(handler).sendPck("X2001200200"); + } + + @Test + public void testHandleCommandDimmerOutput12Value0FixedRamp() throws LcnException { + l.handleCommandDimmerOutput(new DimmerOutputCommand(BigDecimal.valueOf(0), false, true, LcnDefs.FIXED_RAMP_MS), + 0); + verify(handler).sendPck("X2001000000"); + } + + @Test + public void testHandleCommandDimmerOutput12Value100NoRamp() throws LcnException { + l.handleCommandDimmerOutput(new DimmerOutputCommand(BigDecimal.valueOf(100), false, true, 0), 0); + verify(handler).sendPck("X2001253253"); + } + + @Test + public void testHandleCommandDimmerOutput12Value0NoRamp() throws LcnException { + l.handleCommandDimmerOutput(new DimmerOutputCommand(BigDecimal.valueOf(0), false, true, 0), 0); + verify(handler).sendPck("X2001252252"); + } + + @Test + public void testHandleCommandDimmerOutput12Value40() throws LcnException { + l.handleCommandDimmerOutput(new DimmerOutputCommand(BigDecimal.valueOf(40), false, true, 0), 0); + verify(handler).sendPck("AY040040"); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRelaySubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRelaySubHandlerTest.java new file mode 100644 index 0000000000000..25c181309b02e --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRelaySubHandlerTest.java @@ -0,0 +1,114 @@ +/** + * 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.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnException; + +/** + * Test class. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleRelaySubHandlerTest extends AbstractTestLcnModuleSubHandler { + private @NonNullByDefault({}) LcnModuleRelaySubHandler l; + + @Override + @Before + public void setUp() { + super.setUp(); + + l = new LcnModuleRelaySubHandler(handler, info); + } + + @Test + public void testStatusAllOff() { + l.tryParse("=M000005Rx000"); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "1", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "2", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "3", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "4", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "5", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "6", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "7", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "8", OnOffType.OFF); + } + + @Test + public void testStatusAllOn() { + l.tryParse("=M000005Rx255"); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "1", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "2", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "3", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "5", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "6", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "7", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "8", OnOffType.ON); + } + + @Test + public void testStatusRelay1Relay7On() { + l.tryParse("=M000005Rx065"); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "1", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "2", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "3", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "4", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "5", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "6", OnOffType.OFF); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "7", OnOffType.ON); + verify(handler).updateChannel(LcnChannelGroup.RELAY, "8", OnOffType.OFF); + } + + @Test + public void testHandleCommandRelay1On() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.RELAY, 0); + verify(handler).sendPck("R81-------"); + } + + @Test + public void testHandleCommandRelay8On() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.RELAY, 7); + verify(handler).sendPck("R8-------1"); + } + + @Test + public void testHandleCommandRelay1Off() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.RELAY, 0); + verify(handler).sendPck("R80-------"); + } + + @Test + public void testHandleCommandRelay8Off() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.RELAY, 7); + verify(handler).sendPck("R8-------0"); + } + + @Test + public void testHandleCommandRelay8Percent1() throws LcnException { + l.handleCommandPercent(new PercentType(1), LcnChannelGroup.RELAY, 7); + verify(handler).sendPck("R8-------1"); + } + + @Test + public void testHandleCommandRelay1Percent0() throws LcnException { + l.handleCommandPercent(PercentType.ZERO, LcnChannelGroup.RELAY, 0); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterOutputSubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterOutputSubHandlerTest.java new file mode 100644 index 0000000000000..3809f0a099788 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterOutputSubHandlerTest.java @@ -0,0 +1,66 @@ +/** + * 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.eclipse.smarthome.core.library.types.StopMoveType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnException; + +/** + * Test class. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleRollershutterOutputSubHandlerTest extends AbstractTestLcnModuleSubHandler { + private @NonNullByDefault({}) LcnModuleRollershutterOutputSubHandler l; + + @Override + @Before + public void setUp() { + super.setUp(); + + l = new LcnModuleRollershutterOutputSubHandler(handler, info); + } + + @Test + public void testUp() throws LcnException { + l.handleCommandUpDown(UpDownType.UP, LcnChannelGroup.ROLLERSHUTTEROUTPUT, 0); + verify(handler).sendPck("A1DI100008"); + } + + @Test + public void testDown() throws LcnException { + l.handleCommandUpDown(UpDownType.DOWN, LcnChannelGroup.ROLLERSHUTTEROUTPUT, 0); + verify(handler).sendPck("A2DI100008"); + } + + @Test + public void testStop() throws LcnException { + l.handleCommandStopMove(StopMoveType.STOP, LcnChannelGroup.ROLLERSHUTTEROUTPUT, 0); + verify(handler).sendPck("A1DI000000"); + verify(handler).sendPck("A2DI000000"); + } + + @Test + public void testMove() throws LcnException { + l.handleCommandStopMove(StopMoveType.MOVE, LcnChannelGroup.ROLLERSHUTTEROUTPUT, 0); + verify(handler).sendPck("A2DI100008"); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterRelaySubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterRelaySubHandlerTest.java new file mode 100644 index 0000000000000..99816dc13d055 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRollershutterRelaySubHandlerTest.java @@ -0,0 +1,89 @@ +/** + * 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.eclipse.smarthome.core.library.types.StopMoveType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnException; + +/** + * Test class. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleRollershutterRelaySubHandlerTest extends AbstractTestLcnModuleSubHandler { + private @NonNullByDefault({}) LcnModuleRollershutterRelaySubHandler l; + + @Override + @Before + public void setUp() { + super.setUp(); + + l = new LcnModuleRollershutterRelaySubHandler(handler, info); + } + + @Test + public void testUp1() throws LcnException { + l.handleCommandUpDown(UpDownType.UP, LcnChannelGroup.ROLLERSHUTTERRELAY, 0); + verify(handler).sendPck("R810------"); + } + + @Test + public void testUp4() throws LcnException { + l.handleCommandUpDown(UpDownType.UP, LcnChannelGroup.ROLLERSHUTTERRELAY, 3); + verify(handler).sendPck("R8------10"); + } + + @Test + public void testDown1() throws LcnException { + l.handleCommandUpDown(UpDownType.DOWN, LcnChannelGroup.ROLLERSHUTTERRELAY, 0); + verify(handler).sendPck("R811------"); + } + + @Test + public void testDown4() throws LcnException { + l.handleCommandUpDown(UpDownType.DOWN, LcnChannelGroup.ROLLERSHUTTERRELAY, 3); + verify(handler).sendPck("R8------11"); + } + + @Test + public void testStop1() throws LcnException { + l.handleCommandStopMove(StopMoveType.STOP, LcnChannelGroup.ROLLERSHUTTERRELAY, 0); + verify(handler).sendPck("R80-------"); + } + + @Test + public void testStop4() throws LcnException { + l.handleCommandStopMove(StopMoveType.STOP, LcnChannelGroup.ROLLERSHUTTERRELAY, 3); + verify(handler).sendPck("R8------0-"); + } + + @Test + public void testMove1() throws LcnException { + l.handleCommandStopMove(StopMoveType.MOVE, LcnChannelGroup.ROLLERSHUTTERRELAY, 0); + verify(handler).sendPck("R81-------"); + } + + @Test + public void testMove4() throws LcnException { + l.handleCommandStopMove(StopMoveType.MOVE, LcnChannelGroup.ROLLERSHUTTERRELAY, 3); + verify(handler).sendPck("R8------1-"); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarLockSubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarLockSubHandlerTest.java new file mode 100644 index 0000000000000..8acf7e33e4c63 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarLockSubHandlerTest.java @@ -0,0 +1,64 @@ +/** + * 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.eclipse.smarthome.core.library.types.OnOffType; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnException; + +/** + * Test class. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleRvarLockSubHandlerTest extends AbstractTestLcnModuleSubHandler { + private @NonNullByDefault({}) LcnModuleRvarLockSubHandler l; + + @Override + @Before + public void setUp() { + super.setUp(); + + l = new LcnModuleRvarLockSubHandler(handler, info); + } + + @Test + public void testLock1() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.RVARLOCK, 0); + verify(handler).sendPck("REAXS"); + } + + @Test + public void testLock2() throws LcnException { + l.handleCommandOnOff(OnOffType.ON, LcnChannelGroup.RVARLOCK, 1); + verify(handler).sendPck("REBXS"); + } + + @Test + public void testUnlock1() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.RVARLOCK, 0); + verify(handler).sendPck("REAXA"); + } + + @Test + public void testUnlock2() throws LcnException { + l.handleCommandOnOff(OnOffType.OFF, LcnChannelGroup.RVARLOCK, 1); + verify(handler).sendPck("REBXA"); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarSetpointSubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarSetpointSubHandlerTest.java new file mode 100644 index 0000000000000..be0d3df53395b --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleRvarSetpointSubHandlerTest.java @@ -0,0 +1,138 @@ +/** + * 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.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.Variable; + +/** + * Test class. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleRvarSetpointSubHandlerTest extends AbstractTestLcnModuleSubHandler { + private @NonNullByDefault({}) LcnModuleRvarSetpointSubHandler l; + + @Override + @Before + public void setUp() { + super.setUp(); + + l = new LcnModuleRvarSetpointSubHandler(handler, info); + } + + @Test + public void testhandleCommandRvar1Positive() throws LcnException { + when(info.hasExtendedMeasurementProcessing()).thenReturn(true); + l.handleCommandDecimal(new DecimalType(1000), LcnChannelGroup.RVARSETPOINT, 0); + verify(handler).sendPck("X2030032000"); + } + + @Test + public void testhandleCommandRvar2Positive() throws LcnException { + when(info.hasExtendedMeasurementProcessing()).thenReturn(true); + l.handleCommandDecimal(new DecimalType(1100), LcnChannelGroup.RVARSETPOINT, 1); + verify(handler).sendPck("X2030096100"); + } + + @Test + public void testhandleCommandRvar1Negative() throws LcnException { + when(info.hasExtendedMeasurementProcessing()).thenReturn(true); + l.handleCommandDecimal(new DecimalType(0), LcnChannelGroup.RVARSETPOINT, 0); + verify(handler).sendPck("X2030043232"); + } + + @Test + public void testhandleCommandRvar2Negative() throws LcnException { + when(info.hasExtendedMeasurementProcessing()).thenReturn(true); + l.handleCommandDecimal(new DecimalType(999), LcnChannelGroup.RVARSETPOINT, 1); + verify(handler).sendPck("X2030104001"); + } + + @Test + public void testhandleCommandRvar1PositiveLegacy() throws LcnException { + when(info.getVariableValue(Variable.RVARSETPOINT1)).thenReturn(1000L); + when(info.hasExtendedMeasurementProcessing()).thenReturn(false); + l.handleCommandDecimal(new DecimalType(1100), LcnChannelGroup.RVARSETPOINT, 0); + verify(handler).sendPck("REASA+100"); + } + + @Test + public void testhandleCommandRvar2PositiveLegacy() throws LcnException { + when(info.getVariableValue(Variable.RVARSETPOINT2)).thenReturn(1000L); + when(info.hasExtendedMeasurementProcessing()).thenReturn(false); + l.handleCommandDecimal(new DecimalType(1100), LcnChannelGroup.RVARSETPOINT, 1); + verify(handler).sendPck("REBSA+100"); + } + + @Test + public void testhandleCommandRvar1NegativeLegacy() throws LcnException { + when(info.getVariableValue(Variable.RVARSETPOINT1)).thenReturn(1000L); + when(info.hasExtendedMeasurementProcessing()).thenReturn(false); + l.handleCommandDecimal(new DecimalType(900), LcnChannelGroup.RVARSETPOINT, 0); + verify(handler).sendPck("REASA-100"); + } + + @Test + public void testhandleCommandRvar2NegativeLegacy() throws LcnException { + when(info.getVariableValue(Variable.RVARSETPOINT2)).thenReturn(1000L); + when(info.hasExtendedMeasurementProcessing()).thenReturn(false); + l.handleCommandDecimal(new DecimalType(900), LcnChannelGroup.RVARSETPOINT, 1); + verify(handler).sendPck("REBSA-100"); + } + + @Test + public void testRvar1() { + l.tryParse("=M000005.S11234"); + verify(handler).updateChannel(LcnChannelGroup.RVARSETPOINT, "1", new DecimalType(1234)); + verify(handler).updateChannel(LcnChannelGroup.RVARLOCK, "1", OnOffType.OFF); + } + + @Test + public void testRvar2() { + l.tryParse("=M000005.S21234"); + verify(handler).updateChannel(LcnChannelGroup.RVARSETPOINT, "2", new DecimalType(1234)); + verify(handler).updateChannel(LcnChannelGroup.RVARLOCK, "2", OnOffType.OFF); + } + + @Test + public void testRvar1SensorDefective() { + l.tryParse("=M000005.S132512"); + verify(handler).updateChannel(LcnChannelGroup.RVARSETPOINT, "1", new StringType("DEFECTIVE")); + verify(handler).updateChannel(LcnChannelGroup.RVARLOCK, "1", OnOffType.OFF); + } + + @Test + public void testRvar1Locked() { + l.tryParse("=M000005.S134002"); + verify(handler).updateChannel(LcnChannelGroup.RVARSETPOINT, "1", new DecimalType(1234)); + verify(handler).updateChannel(LcnChannelGroup.RVARLOCK, "1", OnOffType.ON); + } + + @Test + public void testRvar2Locked() { + l.tryParse("=M000005.S234002"); + verify(handler).updateChannel(LcnChannelGroup.RVARSETPOINT, "2", new DecimalType(1234)); + verify(handler).updateChannel(LcnChannelGroup.RVARLOCK, "2", OnOffType.ON); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleS0CounterSubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleS0CounterSubHandlerTest.java new file mode 100644 index 0000000000000..4f5f900c17e35 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleS0CounterSubHandlerTest.java @@ -0,0 +1,57 @@ +/** + * 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.eclipse.smarthome.core.library.types.DecimalType; +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 LcnModuleS0CounterSubHandlerTest extends AbstractTestLcnModuleSubHandler { + private @NonNullByDefault({}) LcnModuleS0CounterSubHandler l; + + @Override + @Before + public void setUp() { + super.setUp(); + + l = new LcnModuleS0CounterSubHandler(handler, info); + } + + @Test + public void testZero() { + l.tryParse("=M000005.C10"); + verify(handler).updateChannel(LcnChannelGroup.S0INPUT, "1", new DecimalType(0)); + } + + @Test + public void testMaxValue() { + l.tryParse("=M000005.C14294967295"); + verify(handler).updateChannel(LcnChannelGroup.S0INPUT, "1", new DecimalType(4294967295L)); + } + + @Test + public void test4() { + l.tryParse("=M000005.C412345"); + verify(handler).updateChannel(LcnChannelGroup.S0INPUT, "4", new DecimalType(12345)); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleThresholdSubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleThresholdSubHandlerTest.java new file mode 100644 index 0000000000000..ad3ee3bb85ab5 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleThresholdSubHandlerTest.java @@ -0,0 +1,133 @@ +/** + * 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.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.Variable; + +/** + * Test class. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleThresholdSubHandlerTest extends AbstractTestLcnModuleSubHandler { + private @NonNullByDefault({}) LcnModuleThresholdSubHandler l; + + @Override + @Before + public void setUp() { + super.setUp(); + + l = new LcnModuleThresholdSubHandler(handler, info); + } + + @Test + public void testThreshold11() { + l.tryParse("=M000005.T1112345"); + verify(handler).updateChannel(LcnChannelGroup.THRESHOLDREGISTER1, "1", new DecimalType(12345)); + } + + @Test + public void testThreshold14() { + l.tryParse("=M000005.T140"); + verify(handler).updateChannel(LcnChannelGroup.THRESHOLDREGISTER1, "4", new DecimalType(0)); + } + + @Test + public void testThreshold41() { + l.tryParse("=M000005.T4112345"); + verify(handler).updateChannel(LcnChannelGroup.THRESHOLDREGISTER4, "1", new DecimalType(12345)); + } + + @Test + public void testThresholdLegacy() { + l.tryParse("=M000005.S1123451123411123000000000112345"); + verify(handler).updateChannel(LcnChannelGroup.THRESHOLDREGISTER1, "1", new DecimalType(12345)); + verify(handler).updateChannel(LcnChannelGroup.THRESHOLDREGISTER1, "2", new DecimalType(11234)); + verify(handler).updateChannel(LcnChannelGroup.THRESHOLDREGISTER1, "3", new DecimalType(11123)); + verify(handler).updateChannel(LcnChannelGroup.THRESHOLDREGISTER1, "4", new DecimalType(0)); + verify(handler).updateChannel(LcnChannelGroup.THRESHOLDREGISTER1, "5", new DecimalType(1)); + } + + @Test + public void testhandleCommandThreshold11Positive() throws LcnException { + when(info.getVariableValue(Variable.THRESHOLDREGISTER11)).thenReturn(1000L); + when(info.hasExtendedMeasurementProcessing()).thenReturn(true); + l.handleCommandDecimal(new DecimalType(1100), LcnChannelGroup.THRESHOLDREGISTER1, 0); + verify(handler).sendPck("SSR0100AR11"); + } + + @Test + public void testhandleCommandThreshold11Negative() throws LcnException { + when(info.getVariableValue(Variable.THRESHOLDREGISTER11)).thenReturn(1000L); + when(info.hasExtendedMeasurementProcessing()).thenReturn(true); + l.handleCommandDecimal(new DecimalType(900), LcnChannelGroup.THRESHOLDREGISTER1, 0); + verify(handler).sendPck("SSR0100SR11"); + } + + @Test + public void testhandleCommandThreshold44Positive() throws LcnException { + when(info.getVariableValue(Variable.THRESHOLDREGISTER44)).thenReturn(1000L); + when(info.hasExtendedMeasurementProcessing()).thenReturn(true); + l.handleCommandDecimal(new DecimalType(1100), LcnChannelGroup.THRESHOLDREGISTER4, 3); + verify(handler).sendPck("SSR0100AR44"); + } + + @Test + public void testhandleCommandThreshold44Negative() throws LcnException { + when(info.getVariableValue(Variable.THRESHOLDREGISTER44)).thenReturn(1000L); + when(info.hasExtendedMeasurementProcessing()).thenReturn(true); + l.handleCommandDecimal(new DecimalType(900), LcnChannelGroup.THRESHOLDREGISTER4, 3); + verify(handler).sendPck("SSR0100SR44"); + } + + @Test + public void testhandleCommandThreshold11LegacyPositive() throws LcnException { + when(info.getVariableValue(Variable.THRESHOLDREGISTER11)).thenReturn(1000L); + when(info.hasExtendedMeasurementProcessing()).thenReturn(false); + l.handleCommandDecimal(new DecimalType(1100), LcnChannelGroup.THRESHOLDREGISTER1, 0); + verify(handler).sendPck("SSR0100A10000"); + } + + @Test + public void testhandleCommandThreshold11LegacyNegative() throws LcnException { + when(info.getVariableValue(Variable.THRESHOLDREGISTER11)).thenReturn(1000L); + when(info.hasExtendedMeasurementProcessing()).thenReturn(false); + l.handleCommandDecimal(new DecimalType(900), LcnChannelGroup.THRESHOLDREGISTER1, 0); + verify(handler).sendPck("SSR0100S10000"); + } + + @Test + public void testhandleCommandThreshold14Legacy() throws LcnException { + when(info.getVariableValue(Variable.THRESHOLDREGISTER14)).thenReturn(1000L); + when(info.hasExtendedMeasurementProcessing()).thenReturn(false); + l.handleCommandDecimal(new DecimalType(1100), LcnChannelGroup.THRESHOLDREGISTER1, 3); + verify(handler).sendPck("SSR0100A00010"); + } + + @Test + public void testhandleCommandThreshold15Legacy() throws LcnException { + when(info.getVariableValue(Variable.THRESHOLDREGISTER15)).thenReturn(1000L); + when(info.hasExtendedMeasurementProcessing()).thenReturn(false); + l.handleCommandDecimal(new DecimalType(1100), LcnChannelGroup.THRESHOLDREGISTER1, 4); + verify(handler).sendPck("SSR0100A00001"); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleVariableSubHandlerTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleVariableSubHandlerTest.java new file mode 100644 index 0000000000000..de976ee4acb8d --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleVariableSubHandlerTest.java @@ -0,0 +1,89 @@ +/** + * 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.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnException; +import org.openhab.binding.lcn.internal.common.Variable; + +/** + * Test class. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public class LcnModuleVariableSubHandlerTest extends AbstractTestLcnModuleSubHandler { + private @NonNullByDefault({}) LcnModuleVariableSubHandler l; + + @Override + @Before + public void setUp() { + super.setUp(); + + l = new LcnModuleVariableSubHandler(handler, info); + } + + @Test + public void testStatusVariable1() { + l.tryParse("=M000005.A00112345"); + verify(handler).updateChannel(LcnChannelGroup.VARIABLE, "1", new DecimalType(12345)); + } + + @Test + public void testStatusVariable12() { + l.tryParse("=M000005.A01212345"); + verify(handler).updateChannel(LcnChannelGroup.VARIABLE, "12", new DecimalType(12345)); + } + + @Test + public void testStatusLegacyVariable3() { + when(info.getLastRequestedVarWithoutTypeInResponse()).thenReturn(Variable.VARIABLE3); + l.tryParse("=M000005.12345"); + verify(handler).updateChannel(LcnChannelGroup.VARIABLE, "3", new DecimalType(12345)); + } + + @Test + public void testHandleCommandLegacyTvarPositive() throws LcnException { + when(info.hasExtendedMeasurementProcessing()).thenReturn(false); + when(info.getVariableValue(Variable.VARIABLE1)).thenReturn(1000L); + l.handleCommandDecimal(new DecimalType(1234), LcnChannelGroup.VARIABLE, 0); + verify(handler).sendPck("ZA234"); + } + + @Test + public void testHandleCommandLegacyTvarNegative() throws LcnException { + when(info.hasExtendedMeasurementProcessing()).thenReturn(false); + when(info.getVariableValue(Variable.VARIABLE1)).thenReturn(2000L); + l.handleCommandDecimal(new DecimalType(1100), LcnChannelGroup.VARIABLE, 0); + verify(handler).sendPck("ZS900"); + } + + @Test + public void testStatusVariable10SensorDefective() { + l.tryParse("=M000005.A01032512"); + verify(handler).updateChannel(LcnChannelGroup.VARIABLE, "10", new StringType("DEFECTIVE")); + } + + @Test + public void testStatusVariable8NotConfigured() { + l.tryParse("=M000005.A00865535"); + verify(handler).updateChannel(LcnChannelGroup.VARIABLE, "8", new StringType("Not configured in LCN-PRO")); + } +} diff --git a/bundles/org.openhab.binding.test/.classpath b/bundles/org.openhab.binding.test/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.test/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.test/.project b/bundles/org.openhab.binding.test/.project new file mode 100644 index 0000000000000..6194f1b0b2bc5 --- /dev/null +++ b/bundles/org.openhab.binding.test/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.test + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/bundles/org.openhab.binding.test/NOTICE b/bundles/org.openhab.binding.test/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.binding.test/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.binding.test/README.md b/bundles/org.openhab.binding.test/README.md new file mode 100644 index 0000000000000..68627f1b47dce --- /dev/null +++ b/bundles/org.openhab.binding.test/README.md @@ -0,0 +1,56 @@ +# test Binding + +_Give some details about what this binding is meant for - a protocol, system, specific device._ + +_If possible, provide some resources like pictures, a YouTube video, etc. to give an impression of what can be done with this binding. You can place such resources into a `doc` folder next to this README.md._ + +## Supported Things + +_Please describe the different supported things / devices within this section._ +_Which different types are supported, which models were tested etc.?_ +_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/ESH-INF/thing``` of your binding._ + +## Discovery + +_Describe the available auto-discovery features here. Mention for what it works and what needs to be kept in mind when using it._ + +## Binding Configuration + +_If your binding requires or supports general configuration settings, please create a folder ```cfg``` and place the configuration file ```.cfg``` inside it. In this section, you should link to this file and provide some information about the options. The file could e.g. look like:_ + +``` +# Configuration for the Philips Hue Binding +# +# Default secret key for the pairing of the Philips Hue Bridge. +# It has to be between 10-40 (alphanumeric) characters +# This may be changed by the user for security reasons. +secret=openHABSecret +``` + +_Note that it is planned to generate some part of this based on the information that is available within ```src/main/resources/ESH-INF/binding``` of your binding._ + +_If your binding does not offer any generic configurations, you can remove this section completely._ + +## Thing Configuration + +_Describe what is needed to manually configure a thing, either through the (Paper) UI or via a thing-file. This should be mainly about its mandatory and optional configuration parameters. A short example entry for a thing file can help!_ + +_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/ESH-INF/thing``` of your binding._ + +## Channels + +_Here you should provide information about available channel types, what their meaning is and how they can be used._ + +_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/ESH-INF/thing``` of your binding._ + +| channel | type | description | +|----------|--------|------------------------------| +| control | Switch | This is the control channel | + +## Full Example + +_Provide a full usage example based on textual configuration files (*.things, *.items, *.sitemap)._ + +## Any custom content here! + +_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_ diff --git a/bundles/org.openhab.binding.test/pom.xml b/bundles/org.openhab.binding.test/pom.xml new file mode 100644 index 0000000000000..9b71b550a3263 --- /dev/null +++ b/bundles/org.openhab.binding.test/pom.xml @@ -0,0 +1,16 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.3-SNAPSHOT + + + org.openhab.binding.test + + openHAB Add-ons :: Bundles :: test Binding + + diff --git a/bundles/org.openhab.binding.test/src/main/feature/feature.xml b/bundles/org.openhab.binding.test/src/main/feature/feature.xml new file mode 100644 index 0000000000000..c8deb162e6c40 --- /dev/null +++ b/bundles/org.openhab.binding.test/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.test/${project.version} + + diff --git a/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testBindingConstants.java b/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testBindingConstants.java new file mode 100644 index 0000000000000..9716ca15780f8 --- /dev/null +++ b/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testBindingConstants.java @@ -0,0 +1,34 @@ +/** + * 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.test.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link testBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author ich - Initial contribution + */ +@NonNullByDefault +public class testBindingConstants { + + private static final String BINDING_ID = "test"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_SAMPLE = new ThingTypeUID(BINDING_ID, "sample"); + + // List of all Channel ids + public static final String CHANNEL_1 = "channel1"; +} diff --git a/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testConfiguration.java b/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testConfiguration.java new file mode 100644 index 0000000000000..fcc0c3a0adedd --- /dev/null +++ b/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testConfiguration.java @@ -0,0 +1,26 @@ +/** + * 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.test.internal; + +/** + * The {@link testConfiguration} class contains fields mapping thing configuration parameters. + * + * @author ich - Initial contribution + */ +public class testConfiguration { + + /** + * Sample configuration parameter. Replace with your own. + */ + public String config1; +} diff --git a/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandler.java b/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandler.java new file mode 100644 index 0000000000000..d452eba8c20df --- /dev/null +++ b/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandler.java @@ -0,0 +1,98 @@ +/** + * 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.test.internal; + +import static org.openhab.binding.test.internal.testBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link testHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author ich - Initial contribution + */ +@NonNullByDefault +public class testHandler extends BaseThingHandler { + + private final Logger logger = LoggerFactory.getLogger(testHandler.class); + + private @Nullable testConfiguration config; + + public testHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (CHANNEL_1.equals(channelUID.getId())) { + if (command instanceof RefreshType) { + // TODO: handle data refresh + } + + // TODO: handle command + + // Note: if communication with thing fails for some reason, + // indicate that by setting the status with detail information: + // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + // "Could not control device at IP address x.x.x.x"); + } + } + + @Override + public void initialize() { + // logger.debug("Start initializing!"); + config = getConfigAs(testConfiguration.class); + + // TODO: Initialize the handler. + // The framework requires you to return from this method quickly. Also, before leaving this method a thing + // status from one of ONLINE, OFFLINE or UNKNOWN must be set. This might already be the real thing status in + // case you can decide it directly. + // In case you can not decide the thing status directly (e.g. for long running connection handshake using WAN + // access or similar) you should set status UNKNOWN here and then decide the real status asynchronously in the + // background. + + // set the thing status to UNKNOWN temporarily and let the background task decide for the real status. + // the framework is then able to reuse the resources from the thing handler initialization. + // we set this upfront to reliably check status updates in unit tests. + updateStatus(ThingStatus.UNKNOWN); + + // Example for background initialization: + scheduler.execute(() -> { + boolean thingReachable = true; // + // when done do: + if (thingReachable) { + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE); + } + }); + + // logger.debug("Finished initializing!"); + + // Note: When initialization can NOT be done set the status with more details for further + // analysis. See also class ThingStatusDetail for all available status details. + // Add a description to give user information to understand why thing does not work as expected. E.g. + // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + // "Can not access device as username and/or password are invalid"); + } +} diff --git a/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandlerFactory.java b/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandlerFactory.java new file mode 100644 index 0000000000000..d6bc82520613f --- /dev/null +++ b/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandlerFactory.java @@ -0,0 +1,56 @@ +/** + * 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.test.internal; + +import static org.openhab.binding.test.internal.testBindingConstants.*; + +import java.util.Collections; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link testHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author ich - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.test", service = ThingHandlerFactory.class) +public class testHandlerFactory extends BaseThingHandlerFactory { + + private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_SAMPLE); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (THING_TYPE_SAMPLE.equals(thingTypeUID)) { + return new testHandler(thing); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..00139b390269a --- /dev/null +++ b/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,10 @@ + + + + test Binding + This is the binding for test. + ich + + diff --git a/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/i18n/test_xx_XX.properties b/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/i18n/test_xx_XX.properties new file mode 100644 index 0000000000000..c51a1a75a053d --- /dev/null +++ b/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/i18n/test_xx_XX.properties @@ -0,0 +1,17 @@ +# FIXME: please substitute the xx_XX with a proper locale, ie. de_DE +# FIXME: please do not add the file to the repo if you add or change no content +# binding +binding.test.name = +binding.test.description = + +# thing types +thing-type.test.sample.label = +thing-type.test.sample.description = + +# thing type config description +thing-type.config.test.sample.config1.label = +thing-type.config.test.sample.config1.description = + +# channel types +channel-type.test.sample-channel.label = +channel-type.test.sample-channel.description = diff --git a/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..4410c184a4860 --- /dev/null +++ b/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/thing/thing-types.xml @@ -0,0 +1,32 @@ + + + + + + + Sample thing for test Binding + + + + + + + + + This is a sample text configuration parameter + + + + + + + + testItem + + Sample channel for test Binding + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index a5941fd5e237a..7780d49a3fe4d 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -130,6 +130,7 @@ org.openhab.binding.konnected org.openhab.binding.kostalinverter org.openhab.binding.lametrictime + org.openhab.binding.lcn org.openhab.binding.leapmotion org.openhab.binding.lghombot org.openhab.binding.lgtvserial From a0d78733a4c71137dc0e12ae39072904caf1ab6e Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Sun, 24 May 2020 23:48:44 +0200 Subject: [PATCH 02/16] Minor improvements Signed-off-by: Fabian Wolter --- .../lcn/internal/LcnHandlerFactory.java | 1 - .../lcn/internal/LcnModuleHandler.java | 2 +- .../lcn/internal/PckGatewayHandler.java | 3 - .../lcn/internal/common/LcnChannelGroup.java | 2 + .../lcn/internal/common/PckGenerator.java | 4 +- .../binding/lcn/internal/common/Variable.java | 4 +- .../lcn/internal/connection/Connection.java | 3 +- .../connection/ConnectionSettings.java | 1 - .../lcn/internal/connection/ModInfo.java | 1 - .../converter/AbstractS0Converter.java | 3 +- .../AbstractLcnModuleSubHandler.java | 111 ++----------- .../subhandler/ILcnModuleSubHandler.java | 155 ++++++++++++++++++ .../main/resources/ESH-INF/config/config.xml | 5 +- .../resources/ESH-INF/thing/thing-types.xml | 110 ++++++------- 14 files changed, 234 insertions(+), 171 deletions(-) create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/ILcnModuleSubHandler.java diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnHandlerFactory.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnHandlerFactory.java index 67702f1fd6c36..e551c3ff78e35 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnHandlerFactory.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnHandlerFactory.java @@ -69,7 +69,6 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { new Hashtable<@Nullable String, @Nullable Object>()); return handler; } - return null; } } 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 592c5e4a0f79e..1fdbddf008877 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 @@ -228,7 +228,7 @@ public void handleCommand(ChannelUID channelUid, Command command) { } @NonNullByDefault({}) // getOrDefault() - private AbstractVariableValueConverter getConverter(ChannelUID channelUid) throws LcnException { + private AbstractVariableValueConverter getConverter(ChannelUID channelUid) { return converters.getOrDefault(channelUid, IdentityConverter.getInstance()); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java index 5d947ec6af1f0..90364fe241f48 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal; -import java.io.IOException; import java.nio.ByteBuffer; import java.util.Optional; import java.util.function.Consumer; @@ -105,8 +104,6 @@ public void onPckMessageReceived(String message) { }); updateStatus(ThingStatus.UNKNOWN); - } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorMessage + e.getMessage()); } catch (LcnException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMessage + e.getMessage()); } 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 dfff4af1b56e4..34807dd055371 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 @@ -12,6 +12,7 @@ */ package org.openhab.binding.lcn.internal.common; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lcn.internal.subhandler.AbstractLcnModuleSubHandler; import org.openhab.binding.lcn.internal.subhandler.LcnModuleBinarySensorSubHandler; import org.openhab.binding.lcn.internal.subhandler.LcnModuleCodeSubHandler; @@ -33,6 +34,7 @@ * * @author Fabian Wolter - Initial contribution */ +@NonNullByDefault public enum LcnChannelGroup { OUTPUT(4, LcnModuleOutputSubHandler.class), ROLLERSHUTTEROUTPUT(1, LcnModuleRollershutterOutputSubHandler.class), diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/PckGenerator.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/PckGenerator.java index 47b791e43ae06..0fcbcfea8d841 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/PckGenerator.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/PckGenerator.java @@ -349,7 +349,7 @@ public static String requestBinSensorsStatus() { * @return the PCK command (without address header) as text * @throws LcnException */ - public static String setSetpointAbsolute(int number, int value) throws LcnException { + public static String setSetpointAbsolute(int number, int value) { int internalValue = value; // Set absolute (not in PCK yet) int b1 = number << 6; // 01000000 @@ -374,7 +374,7 @@ public static String setSetpointAbsolute(int number, int value) throws LcnExcept * @return the PCK command (without address header) as text * @throws LcnException if command is not supported */ - public static String setVariableRelative(Variable variable, LcnDefs.RelVarRef type, int value) throws LcnException { + public static String setVariableRelative(Variable variable, LcnDefs.RelVarRef type, int value) { if (variable.getNumber() == 0) { // Old command for variable 1 / T-var (compatible with all modules) return String.format("Z%s%d", value >= 0 ? "A" : "S", Math.abs(value)); diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java index ce8be45e0f374..51d43435d02ce 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java @@ -16,6 +16,7 @@ import java.util.function.Predicate; import java.util.stream.Stream; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lcn.internal.LcnBindingConstants; /** @@ -24,8 +25,9 @@ * @author Tobias Jüttner - Initial Contribution * @author Fabian Wolter - Migration to OH2 */ +@NonNullByDefault public enum Variable { - UNKNOWN(0, Type.UNKNOWN, null), // Used if the real type is not known (yet) + UNKNOWN(0, Type.UNKNOWN, LcnChannelGroup.VARIABLE), // Used if the real type is not known (yet) VARIABLE1(0, Type.VARIABLE, LcnChannelGroup.VARIABLE), // or TVar VARIABLE2(1, Type.VARIABLE, LcnChannelGroup.VARIABLE), VARIABLE3(2, Type.VARIABLE, LcnChannelGroup.VARIABLE), diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java index 65547810a63f7..67f54b73cc8e9 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java @@ -82,8 +82,7 @@ public class Connection { * @param callback the callback to the owner * @throws IOException */ - public Connection(ConnectionSettings sets, ScheduledExecutorService scheduler, ConnectionCallback callback) - throws IOException { + public Connection(ConnectionSettings sets, ScheduledExecutorService scheduler, ConnectionCallback callback) { this.settings = sets; this.callback = callback; this.scheduler = scheduler; diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionSettings.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionSettings.java index 117c42539556a..dae04ef589fe6 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionSettings.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionSettings.java @@ -155,5 +155,4 @@ public boolean equals(@Nullable Object o) { && this.dimMode == other.dimMode && this.statusMode == other.statusMode && this.timeoutMSec == other.timeoutMSec; } - } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java index 8ab99e2a1ce23..c5c446d48891b 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java @@ -497,5 +497,4 @@ public void onLedsAndLogicResponseReceived() { public void onLockedKeysResponseReceived() { requestStatusLockedKeys.onResponseReceived(); } - } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractS0Converter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractS0Converter.java index d9cfff1c7841d..9971af24102d8 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractS0Converter.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractS0Converter.java @@ -49,5 +49,4 @@ public int toNative(double value) { public double toHumanReadable(long value) { return value / pulsesPerKwh * 1000; } - -} \ No newline at end of file +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleSubHandler.java index 00b274355f7bd..7c0547f03be05 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleSubHandler.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleSubHandler.java @@ -13,10 +13,8 @@ package org.openhab.binding.lcn.internal.subhandler; import java.util.Arrays; -import java.util.Collection; import java.util.Optional; import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.DecimalType; @@ -46,7 +44,7 @@ * @author Fabian Wolter - Initial contribution */ @NonNullByDefault -public abstract class AbstractLcnModuleSubHandler { +public abstract class AbstractLcnModuleSubHandler implements ILcnModuleSubHandler { private final Logger logger = LoggerFactory.getLogger(AbstractLcnModuleSubHandler.class); protected LcnModuleHandler handler; protected ModInfo info; @@ -56,143 +54,56 @@ public AbstractLcnModuleSubHandler(LcnModuleHandler handler, ModInfo info) { this.info = info; } - /** - * Gets the Patterns, the sub handler is capable to process. - * - * @return the Patterns - */ - public abstract Collection getPckStatusMessagePatterns(); - - /** - * Processes the payload of a pre-matched PCK message. - * - * @param matcher the pre-matched matcher. - * @throws LcnException when the message cannot be processed - */ - public abstract void handleStatusMessage(Matcher matcher) throws LcnException; - - /** - * Processes a refresh request from openHAB. - * - * @param channelGroup the Channel group that shall be refreshed - * @param number the Channel number within the Channel group - */ - public abstract void handleRefresh(LcnChannelGroup channelGroup, int number); - - /** - * Processes a refresh request from openHAB. - * - * @param groupId the Channel ID that shall be refreshed - */ + @Override public void handleRefresh(String groupId) { // can be overwritten by subclasses. } - /** - * Handles a Command from openHAB. - * - * @param command the command to handle - * @param channelGroup the addressed Channel group - * @param number the Channel's number within the Channel group - * @throws LcnException when the command could not processed - */ + @Override public void handleCommandOnOff(OnOffType command, LcnChannelGroup channelGroup, int number) throws LcnException { unsupportedCommand(command); } - /** - * Handles a Command from openHAB. - * - * @param command the command to handle - * @param channelGroup the addressed Channel group - * @param number the Channel's number within the Channel group - * @throws LcnException when the command could not processed - */ + @Override public void handleCommandPercent(PercentType command, LcnChannelGroup channelGroup, int number) throws LcnException { unsupportedCommand(command); } - /** - * Handles a Command from openHAB. - * - * @param command the command to handle - * @param channelGroup the addressed Channel group - * @param idWithoutGroup the Channel's name within the Channel group - * @throws LcnException when the command could not processed - */ + @Override public void handleCommandPercent(PercentType command, LcnChannelGroup channelGroup, String idWithoutGroup) throws LcnException { unsupportedCommand(command); } - /** - * Handles a Command from openHAB. - * - * @param command the command to handle - * @param channelGroup the addressed Channel group - * @param number the Channel's number within the Channel group - * @throws LcnException when the command could not processed - */ + @Override public void handleCommandDecimal(DecimalType command, LcnChannelGroup channelGroup, int number) throws LcnException { unsupportedCommand(command); } - /** - * Handles a Command from openHAB. - * - * @param command the command to handle - * @param number the Channel's number within the Channel group - * @throws LcnException when the command could not processed - */ + @Override public void handleCommandDimmerOutput(DimmerOutputCommand command, int number) throws LcnException { unsupportedCommand(command); } - /** - * Handles a Command from openHAB. - * - * @param command the command to handle - * @param number the Channel's number within the Channel group - * @throws LcnException when the command could not processed - */ + @Override public void handleCommandString(StringType command, int number) throws LcnException { unsupportedCommand(command); } - /** - * Handles a Command from openHAB. - * - * @param command the command to handle - * @param channelGroup the addressed Channel group - * @param number the Channel's number within the Channel group - * @throws LcnException when the command could not processed - */ + @Override public void handleCommandUpDown(UpDownType command, LcnChannelGroup channelGroup, int number) throws LcnException { unsupportedCommand(command); } - /** - * Handles a Command from openHAB. - * - * @param command the command to handle - * @param channelGroup the addressed Channel group - * @param number the Channel's number within the Channel group - * @throws LcnException when the command could not processed - */ + @Override public void handleCommandStopMove(StopMoveType command, LcnChannelGroup channelGroup, int number) throws LcnException { unsupportedCommand(command); } - /** - * Handles a Command from openHAB. - * - * @param command the command to handle - * @param groupId the Channel's name within the Channel group - * @throws LcnException when the command could not processed - */ + @Override public void handleCommandHsb(HSBType command, String groupId) throws LcnException { unsupportedCommand(command); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/ILcnModuleSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/ILcnModuleSubHandler.java new file mode 100644 index 0000000000000..aff33cc1f0ebd --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/ILcnModuleSubHandler.java @@ -0,0 +1,155 @@ +/** + * 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.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.StopMoveType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.openhab.binding.lcn.internal.common.DimmerOutputCommand; +import org.openhab.binding.lcn.internal.common.LcnChannelGroup; +import org.openhab.binding.lcn.internal.common.LcnException; + +/** + * Interface for LCN module Thing sub handlers processing variables. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public interface ILcnModuleSubHandler { + /** + * Gets the Patterns, the sub handler is capable to process. + * + * @return the Patterns + */ + Collection getPckStatusMessagePatterns(); + + /** + * Processes the payload of a pre-matched PCK message. + * + * @param matcher the pre-matched matcher. + * @throws LcnException when the message cannot be processed + */ + void handleStatusMessage(Matcher matcher) throws LcnException; + + /** + * Processes a refresh request from openHAB. + * + * @param channelGroup the Channel group that shall be refreshed + * @param number the Channel number within the Channel group + */ + void handleRefresh(LcnChannelGroup channelGroup, int number); + + /** + * Processes a refresh request from openHAB. + * + * @param groupId the Channel ID that shall be refreshed + */ + void handleRefresh(String groupId); + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param channelGroup the addressed Channel group + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + void handleCommandOnOff(OnOffType command, LcnChannelGroup channelGroup, int number) throws LcnException; + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param channelGroup the addressed Channel group + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + void handleCommandPercent(PercentType command, LcnChannelGroup channelGroup, int number) throws LcnException; + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param channelGroup the addressed Channel group + * @param idWithoutGroup the Channel's name within the Channel group + * @throws LcnException when the command could not processed + */ + void handleCommandPercent(PercentType command, LcnChannelGroup channelGroup, String idWithoutGroup) + throws LcnException; + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param channelGroup the addressed Channel group + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + void handleCommandDecimal(DecimalType command, LcnChannelGroup channelGroup, int number) throws LcnException; + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + void handleCommandDimmerOutput(DimmerOutputCommand command, int number) throws LcnException; + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + void handleCommandString(StringType command, int number) throws LcnException; + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param channelGroup the addressed Channel group + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + void handleCommandUpDown(UpDownType command, LcnChannelGroup channelGroup, int number) throws LcnException; + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param channelGroup the addressed Channel group + * @param number the Channel's number within the Channel group + * @throws LcnException when the command could not processed + */ + void handleCommandStopMove(StopMoveType command, LcnChannelGroup channelGroup, int number) throws LcnException; + + /** + * Handles a Command from openHAB. + * + * @param command the command to handle + * @param groupId the Channel's name within the Channel group + * @throws LcnException when the command could not processed + */ + void handleCommandHsb(HSBType command, String groupId) throws LcnException; +} diff --git a/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/config/config.xml b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/config/config.xml index cf111d56acf93..e4a52125a1948 100644 --- a/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/config/config.xml +++ b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/config/config.xml @@ -64,7 +64,8 @@ - The module ID of any module in the group. The state of this module is used for visualization of the group as representative for all modules. + The module ID of any module in the group. The state of this module is used for visualization of the + group as representative for all modules. @@ -80,7 +81,7 @@ native - + 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 75c16c09166b9..1623b4cf0c066 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 @@ -7,75 +7,75 @@ An LCN gateway speaking the PCK language. E.g. LCN-PCHK software or the DIN rail device LCN-PKE. - + - + An LCN bus module, e.g. LCN-UPP, LCN-SH, LCN-HU - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + - + An LCN group with multiple modules, configured in LCN-PRO - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - + + @@ -307,7 +307,7 @@ Number veto - + @@ -604,34 +604,34 @@ - - - - + + + + trigger - + trigger - + trigger - + trigger - + From e30dc55c9fa422439f89c1222dba4b63aace3f9a Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Sun, 31 May 2020 23:17:28 +0200 Subject: [PATCH 03/16] Improve discovery Signed-off-by: Fabian Wolter --- bundles/org.openhab.binding.lcn/pom.xml | 2 +- .../lcn/internal/LcnHandlerFactory.java | 10 +-- .../internal/LcnModuleDiscoveryService.java | 82 ++++++++++++++----- .../lcn/internal/PckGatewayHandler.java | 8 ++ 4 files changed, 72 insertions(+), 30 deletions(-) diff --git a/bundles/org.openhab.binding.lcn/pom.xml b/bundles/org.openhab.binding.lcn/pom.xml index 817992be5cc35..eccef96b93fe3 100644 --- a/bundles/org.openhab.binding.lcn/pom.xml +++ b/bundles/org.openhab.binding.lcn/pom.xml @@ -7,7 +7,7 @@ org.openhab.addons.bundles org.openhab.addons.reactor.bundles - 2.5.5-SNAPSHOT + 2.5.6-SNAPSHOT org.openhab.binding.lcn diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnHandlerFactory.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnHandlerFactory.java index e551c3ff78e35..b66e61f9d7ddd 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnHandlerFactory.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnHandlerFactory.java @@ -15,14 +15,12 @@ import static org.openhab.binding.lcn.internal.LcnBindingConstants.*; import java.util.Collections; -import java.util.Hashtable; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.config.discovery.DiscoveryService; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingTypeUID; @@ -61,13 +59,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { } if (THING_TYPE_PCK_GATEWAY.equals(thingTypeUID)) { - PckGatewayHandler handler = new PckGatewayHandler((Bridge) thing); - - LcnModuleDiscoveryService discoveryService = new LcnModuleDiscoveryService(handler); - - bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, - new Hashtable<@Nullable String, @Nullable Object>()); - return handler; + return new PckGatewayHandler((Bridge) thing); } return null; } 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 de9a8d4b1fffa..98835bf1b73e7 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 @@ -29,20 +29,25 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.DiscoveryService; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; import org.openhab.binding.lcn.internal.common.LcnAddrMod; import org.openhab.binding.lcn.internal.common.NullScheduledFuture; import org.openhab.binding.lcn.internal.connection.Connection; import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaAckSubHandler; import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaFirmwareSubHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Scans all LCN segments for LCN modules. * * Scan approach: - * 1. Send Leerkomando to the broadcast address with request for Ack set - * 2. For every received Ack, send to the module: + * 1. Send "Leerkomando" to the broadcast address with request for Ack set + * 2. For every received Ack, send the following requests to the module: * - serial number request (SN) * - module's name first part request (NM1) * - module's name second part request (NM2) @@ -51,16 +56,17 @@ * @author Fabian Wolter - Initial Contribution */ @NonNullByDefault -public class LcnModuleDiscoveryService extends AbstractDiscoveryService { +public class LcnModuleDiscoveryService extends AbstractDiscoveryService + implements DiscoveryService, ThingHandlerService { + private final Logger logger = LoggerFactory.getLogger(LcnModuleDiscoveryService.class); private static final Pattern NAME_PATTERN = Pattern .compile("=M(?\\d{3})(?\\d{3}).N(?[1-2]{1})(?.*)"); private static final int MODULE_NAME_PART_COUNT = 2; private static final int DISCOVERY_TIMEOUT_SEC = 90; private static final int ACK_TIMEOUT_MS = 1000; - private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet( - Stream.of(LcnBindingConstants.THING_TYPE_PCK_GATEWAY, LcnBindingConstants.THING_TYPE_MODULE) - .collect(Collectors.toSet())); - private PckGatewayHandler bridgeHandler; + private static final Set SUPPORTED_THING_TYPES_UIDS = Collections + .unmodifiableSet(Stream.of(LcnBindingConstants.THING_TYPE_MODULE).collect(Collectors.toSet())); + private @Nullable PckGatewayHandler bridgeHandler; private Map> moduleNames = new HashMap<>(); private Map discoveryResultBuilders = new HashMap<>(); private List successfullyDiscovered = new LinkedList<>(); @@ -69,15 +75,37 @@ public class LcnModuleDiscoveryService extends AbstractDiscoveryService { private volatile ScheduledFuture queueProcessor = NullScheduledFuture.getInstance(); private ScheduledFuture builderTask = NullScheduledFuture.getInstance(); - public LcnModuleDiscoveryService(PckGatewayHandler bridgeHandler) throws IllegalArgumentException { + public LcnModuleDiscoveryService() { super(SUPPORTED_THING_TYPES_UIDS, DISCOVERY_TIMEOUT_SEC, false); - this.bridgeHandler = bridgeHandler; + } + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + if (handler instanceof PckGatewayHandler) { + this.bridgeHandler = (PckGatewayHandler) handler; + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return bridgeHandler; + } + + @Override + public void deactivate() { + super.deactivate(); + stopScan(); } @SuppressWarnings({ "unused", "null" }) @Override protected void startScan() { synchronized (this) { + if (bridgeHandler == null) { + logger.warn("Bridge handler not set"); + return; + } + if (bridgeHandler.getConnection() == null) { builderTask.cancel(true); } @@ -173,17 +201,25 @@ private synchronized void rescheduleQueueProcessor() { // delay serial number and module name requests to not clog the bus queueProcessor.cancel(true); queueProcessor = scheduler.scheduleWithFixedDelay(() -> { - synchronized (serialNumberRequestQueue) { - LcnAddrMod serial = serialNumberRequestQueue.poll(); - if (serial != null) { - bridgeHandler.sendSerialNumberRequest(serial); - } - } + PckGatewayHandler localBridgeHandler = bridgeHandler; + if (localBridgeHandler != null) { + synchronized (serialNumberRequestQueue) { + synchronized (moduleNameRequestQueue) { + LcnAddrMod serial = serialNumberRequestQueue.poll(); + if (serial != null) { + localBridgeHandler.sendSerialNumberRequest(serial); + } + + LcnAddrMod name = moduleNameRequestQueue.poll(); + if (name != null) { + localBridgeHandler.sendModuleNameRequest(name); + } - synchronized (moduleNameRequestQueue) { - LcnAddrMod name = moduleNameRequestQueue.poll(); - if (name != null) { - bridgeHandler.sendModuleNameRequest(name); + // stop scan when all LCN modules have been requested + if (serial == null && name == null) { + scheduler.schedule(this::stopScan, ACK_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + } } } }, ACK_TIMEOUT_MS, ACK_TIMEOUT_MS, TimeUnit.MILLISECONDS); @@ -193,7 +229,13 @@ private synchronized void rescheduleQueueProcessor() { public synchronized void stopScan() { builderTask.cancel(true); queueProcessor.cancel(true); - bridgeHandler.removeAllPckListeners(); + PckGatewayHandler localBridgeHandler = bridgeHandler; + if (localBridgeHandler != null) { + localBridgeHandler.removeAllPckListeners(); + } + successfullyDiscovered.clear(); + moduleNames.clear(); + super.stopScan(); } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java index 90364fe241f48..e73f973a56d0d 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java @@ -13,6 +13,8 @@ package org.openhab.binding.lcn.internal; import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.Collections; import java.util.Optional; import java.util.function.Consumer; @@ -25,6 +27,7 @@ import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; import org.eclipse.smarthome.core.types.Command; import org.openhab.binding.lcn.internal.common.LcnAddr; import org.openhab.binding.lcn.internal.common.LcnAddrMod; @@ -109,6 +112,11 @@ public void onPckMessageReceived(String message) { } } + @Override + public Collection> getServices() { + return Collections.singleton(LcnModuleDiscoveryService.class); + } + @Override public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) { if (childThing.getThingTypeUID().equals(LcnBindingConstants.THING_TYPE_MODULE) From f6c8d4f8d969cbbfa051f03b747f630902ea29ab Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Tue, 2 Jun 2020 23:23:37 +0200 Subject: [PATCH 04/16] Fix formatting and improve LcnModuleHandler lifecycle Signed-off-by: Fabian Wolter --- .../openhab/binding/lcn/internal/LcnModuleHandler.java | 9 +++++---- bundles/pom.xml | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) 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 1fdbddf008877..47b84e73045d2 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 @@ -92,7 +92,6 @@ public LcnModuleHandler(Thing thing) { @Override public void initialize() { - updateStatus(ThingStatus.UNKNOWN); LcnModuleConfiguration localConfig = getConfigAs(LcnModuleConfiguration.class); LcnAddrMod localModuleAddress = moduleAddress = new LcnAddrMod(localConfig.segmentId, localConfig.moduleId); @@ -100,7 +99,6 @@ public void initialize() { // create sub handlers ModInfo info = getPckGatewayHandler().getModInfo(localModuleAddress); for (LcnChannelGroup type : LcnChannelGroup.values()) { - AbstractLcnModuleSubHandler newHandler = type.getSubHandlerClass() .getDeclaredConstructor(LcnModuleHandler.class, ModInfo.class).newInstance(this, info); @@ -108,8 +106,6 @@ public void initialize() { } // meta sub handlers, which are not assigned to a channel group - // initialize() can be invoked multiple times on the same handler, when changing a Channel's config - metadataSubHandlers.clear(); metadataSubHandlers.add(new LcnModuleMetaAckSubHandler(this, info)); metadataSubHandlers.add(new LcnModuleMetaFirmwareSubHandler(this, info)); @@ -418,4 +414,9 @@ public LcnAddrMod getStatusMessageAddress() { return new LcnAddrMod(0, 0); } } + + @Override + public void dispose() { + metadataSubHandlers.clear(); + } } diff --git a/bundles/pom.xml b/bundles/pom.xml index 7780d49a3fe4d..4cb08811b0457 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -130,7 +130,7 @@ org.openhab.binding.konnected org.openhab.binding.kostalinverter org.openhab.binding.lametrictime - org.openhab.binding.lcn + org.openhab.binding.lcn org.openhab.binding.leapmotion org.openhab.binding.lghombot org.openhab.binding.lgtvserial From dbe413f2f442645719b349fb40ffb7d2e2ca997e Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Sat, 6 Jun 2020 15:21:56 +0200 Subject: [PATCH 05/16] Reviewed own code Signed-off-by: Fabian Wolter --- .../lcn/internal/DimmerOutputProfile.java | 10 +- .../lcn/internal/LcnBindingConstants.java | 2 +- .../binding/lcn/internal/LcnGroupHandler.java | 4 +- .../lcn/internal/LcnModuleActions.java | 2 + .../internal/LcnModuleDiscoveryService.java | 61 ++++++----- .../lcn/internal/LcnModuleHandler.java | 103 ++++++++---------- .../lcn/internal/PckGatewayHandler.java | 9 +- .../binding/lcn/internal/common/LcnDefs.java | 32 ------ .../lcn/internal/common/LcnException.java | 2 +- .../connection/AbstractConnectionState.java | 2 +- .../internal/connection/AbstractState.java | 9 +- .../lcn/internal/connection/Connection.java | 9 +- .../connection/ConnectionStateConnecting.java | 2 +- .../internal/converter/LightConverter.java | 2 +- .../LcnPchkDiscoveryService.java | 4 +- 15 files changed, 111 insertions(+), 142 deletions(-) diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/DimmerOutputProfile.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/DimmerOutputProfile.java index 938611f30f6f5..a0e7781332cf7 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/DimmerOutputProfile.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/DimmerOutputProfile.java @@ -47,9 +47,9 @@ public class DimmerOutputProfile implements StateProfile { public DimmerOutputProfile(ProfileCallback callback, ProfileContext profileContext) { this.callback = callback; - Optional ramp = Optional.ofNullable(profileContext.getConfiguration().get("ramp")); - Optional allOutputs = Optional.ofNullable(profileContext.getConfiguration().get("controlAllOutputs")); - Optional outputs12 = Optional.ofNullable(profileContext.getConfiguration().get("controlOutputs12")); + Optional ramp = getConfig(profileContext, "ramp"); + Optional allOutputs = getConfig(profileContext, "controlAllOutputs"); + Optional outputs12 = getConfig(profileContext, "controlOutputs12"); ramp.ifPresent(b -> { if (b instanceof BigDecimal) { @@ -76,6 +76,10 @@ public DimmerOutputProfile(ProfileCallback callback, ProfileContext profileConte }); } + private Optional getConfig(ProfileContext profileContext, String key) { + return Optional.ofNullable(profileContext.getConfiguration().get(key)); + } + @Override public void onCommandFromItem(Command command) { if (rampMs != 0 && rampMs != LcnDefs.FIXED_RAMP_MS && controlOutputs12) { diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnBindingConstants.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnBindingConstants.java index fefe862475a3d..39c7d2b4ba62a 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnBindingConstants.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnBindingConstants.java @@ -30,7 +30,7 @@ public class LcnBindingConstants { * variable updates. */ public static final int FIRMWARE_2013 = 0x170206; - /** Firmware version which supports controling all 4 outputs simultaneously */ + /** Firmware version which supports controlling all 4 outputs simultaneously */ public static final int FIRMWARE_2014 = 0x180501; /** List of all Thing Type UIDs */ public static final ThingTypeUID THING_TYPE_PCK_GATEWAY = new ThingTypeUID(BINDING_ID, "pckGateway"); diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnGroupHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnGroupHandler.java index f86c4b51a64e3..5ce3f6bdfeac3 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnGroupHandler.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnGroupHandler.java @@ -21,9 +21,9 @@ /** * The {@link LcnGroupHandler} is responsible for handling commands, which are - * sent to one of the channels and their destination is an LCN group address. + * addressed to an LCN group. * - * The module in the attribute moduleAddress is used for state updates of the group as representative for all modules in + * The module in the field moduleAddress is used for state updates of the group as representative for all modules in * the group. * * @author Fabian Wolter - Initial contribution diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java index 82ed4fbe20cc1..22f4b7f88ed4d 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java @@ -163,6 +163,7 @@ public static void hitKey(@Nullable ThingActions actions, @Nullable String table } } + /** Static alias to support the old DSL rules engine and make the action available there. */ public static void flickerOutput(@Nullable ThingActions actions, int output, int depth, int ramp, int count) { if (actions instanceof LcnModuleActions) { ((LcnModuleActions) actions).flickerOutput(output, depth, ramp, count); @@ -172,6 +173,7 @@ public static void flickerOutput(@Nullable ThingActions actions, int output, int } } + /** Static alias to support the old DSL rules engine and make the action available there. */ public static void sendDynamicText(@Nullable ThingActions actions, int row, @Nullable String text) { if (actions instanceof LcnModuleActions) { ((LcnModuleActions) actions).sendDynamicText(row, text); 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 98835bf1b73e7..c0ff70c637e64 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 @@ -17,7 +17,9 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; @@ -67,11 +69,12 @@ public class LcnModuleDiscoveryService extends AbstractDiscoveryService private static final Set SUPPORTED_THING_TYPES_UIDS = Collections .unmodifiableSet(Stream.of(LcnBindingConstants.THING_TYPE_MODULE).collect(Collectors.toSet())); private @Nullable PckGatewayHandler bridgeHandler; - private Map> moduleNames = new HashMap<>(); - private Map discoveryResultBuilders = new HashMap<>(); + private Map> moduleNames = Collections.synchronizedMap(new HashMap<>()); + private Map discoveryResultBuilders = Collections + .synchronizedMap(new HashMap<>()); private List successfullyDiscovered = new LinkedList<>(); - private LinkedList<@Nullable LcnAddrMod> serialNumberRequestQueue = new LinkedList<>(); - private LinkedList<@Nullable LcnAddrMod> moduleNameRequestQueue = new LinkedList<>(); + private Queue<@Nullable LcnAddrMod> serialNumberRequestQueue = new ConcurrentLinkedQueue<>(); + private Queue<@Nullable LcnAddrMod> moduleNameRequestQueue = new ConcurrentLinkedQueue<>(); private volatile ScheduledFuture queueProcessor = NullScheduledFuture.getInstance(); private ScheduledFuture builderTask = NullScheduledFuture.getInstance(); @@ -101,34 +104,37 @@ public void deactivate() { @Override protected void startScan() { synchronized (this) { - if (bridgeHandler == null) { + PckGatewayHandler localBridgeHandler = bridgeHandler; + if (localBridgeHandler == null) { logger.warn("Bridge handler not set"); return; } - if (bridgeHandler.getConnection() == null) { + if (localBridgeHandler.getConnection() == null) { builderTask.cancel(true); } - bridgeHandler.registerPckListener(data -> { + localBridgeHandler.registerPckListener(data -> { Matcher matcher; if ((matcher = LcnModuleMetaAckSubHandler.PATTERN_POS.matcher(data)).matches() || (matcher = LcnModuleMetaFirmwareSubHandler.PATTERN.matcher(data)).matches() || (matcher = NAME_PATTERN.matcher(data)).matches()) { synchronized (LcnModuleDiscoveryService.this) { - Connection connection = bridgeHandler.getConnection(); + Connection connection = localBridgeHandler.getConnection(); if (connection == null) { return; } LcnAddrMod addr = new LcnAddrMod( - bridgeHandler.toLogicalSegmentId(Integer.parseInt(matcher.group("segId"))), + localBridgeHandler.toLogicalSegmentId(Integer.parseInt(matcher.group("segId"))), Integer.parseInt(matcher.group("modId"))); if (matcher.pattern() == LcnModuleMetaAckSubHandler.PATTERN_POS) { - // the module could send an Ack with a response to another command. So, ignore the Ack, when + // Received an ACK frame + + // The module could send an Ack with a response to another command. So, ignore the Ack, when // we received our data already. if (!discoveryResultBuilders.containsKey(addr)) { serialNumberRequestQueue.add(addr); @@ -141,11 +147,13 @@ protected void startScan() { rescheduleQueueProcessor(); // delay request of names until all modules finished ACKing } } else if (matcher.pattern() == LcnModuleMetaFirmwareSubHandler.PATTERN) { + // Received a firmware version info frame + Map properties = new HashMap<>(5); properties.put("segmentId", addr.getSegmentId()); properties.put("moduleId", addr.getModuleId()); - ThingUID bridgeUid = bridgeHandler.getThing().getUID(); + ThingUID bridgeUid = localBridgeHandler.getThing().getUID(); String thingId = matcher.group("sn"); ThingUID thingUid = new ThingUID(LcnBindingConstants.THING_TYPE_MODULE, bridgeUid, thingId); @@ -154,6 +162,8 @@ protected void startScan() { discoveryResultBuilders.put(addr, discoveryResult); } else if (matcher.pattern() == NAME_PATTERN) { + // Received part of a module's name frame + final int part = Integer.parseInt(matcher.group("part")) - 1; final String name = matcher.group("name"); @@ -177,7 +187,6 @@ protected void startScan() { discoveryResultBuilders.entrySet().stream().filter(e -> moduleNames.containsKey(e.getKey())) .filter(e -> moduleNames.get(e.getKey()).size() == MODULE_NAME_PART_COUNT) .filter(e -> !successfullyDiscovered.contains(e.getKey())).forEach(e -> { - // collect() to remove while iterating StringBuilder thingName = new StringBuilder(); if (e.getKey().getSegmentId() != 0) { thingName.append("Segment " + e.getKey().getSegmentId() + " "); @@ -193,7 +202,7 @@ protected void startScan() { } }, 500, 500, TimeUnit.MILLISECONDS); - bridgeHandler.sendModuleDiscoveryCommand(); + localBridgeHandler.sendModuleDiscoveryCommand(); } } @@ -203,23 +212,19 @@ private synchronized void rescheduleQueueProcessor() { queueProcessor = scheduler.scheduleWithFixedDelay(() -> { PckGatewayHandler localBridgeHandler = bridgeHandler; if (localBridgeHandler != null) { - synchronized (serialNumberRequestQueue) { - synchronized (moduleNameRequestQueue) { - LcnAddrMod serial = serialNumberRequestQueue.poll(); - if (serial != null) { - localBridgeHandler.sendSerialNumberRequest(serial); - } + LcnAddrMod serial = serialNumberRequestQueue.poll(); + if (serial != null) { + localBridgeHandler.sendSerialNumberRequest(serial); + } - LcnAddrMod name = moduleNameRequestQueue.poll(); - if (name != null) { - localBridgeHandler.sendModuleNameRequest(name); - } + LcnAddrMod name = moduleNameRequestQueue.poll(); + if (name != null) { + localBridgeHandler.sendModuleNameRequest(name); + } - // stop scan when all LCN modules have been requested - if (serial == null && name == null) { - scheduler.schedule(this::stopScan, ACK_TIMEOUT_MS, TimeUnit.MILLISECONDS); - } - } + // stop scan when all LCN modules have been requested + if (serial == null && name == null) { + scheduler.schedule(this::stopScan, ACK_TIMEOUT_MS, TimeUnit.MILLISECONDS); } } }, ACK_TIMEOUT_MS, ACK_TIMEOUT_MS, TimeUnit.MILLISECONDS); 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 47b84e73045d2..d4932699ddff3 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 @@ -70,7 +70,7 @@ /** * The {@link LcnModuleHandler} is responsible for handling commands, which are - * sent to one of the channels. + * sent to or received from one of the channels. * * @author Fabian Wolter - Initial contribution */ @@ -159,63 +159,55 @@ public void initialize() { @Override public void handleCommand(ChannelUID channelUid, Command command) { - // this method can be invoked, when initialize() has not finished, yet. - if (thing.getStatus() != ThingStatus.ONLINE) { - return; - } - try { - // refresh command can be received, when Bridge is initializing, so synchronize - synchronized (getPckGatewayHandler()) { - String groupId = channelUid.getGroupId(); + String groupId = channelUid.getGroupId(); - if (!channelUid.isInGroup()) { - return; - } + if (!channelUid.isInGroup()) { + return; + } - if (groupId == null) { - throw new LcnException("Group ID is null"); - } + if (groupId == null) { + throw new LcnException("Group ID is null"); + } - LcnChannelGroup channelGroup = LcnChannelGroup.valueOf(groupId.toUpperCase()); - AbstractLcnModuleSubHandler subHandler = subHandlers.get(channelGroup); + LcnChannelGroup channelGroup = LcnChannelGroup.valueOf(groupId.toUpperCase()); + AbstractLcnModuleSubHandler subHandler = subHandlers.get(channelGroup); - if (subHandler == null) { - throw new LcnException("Sub Handler not found for: " + channelGroup); - } + if (subHandler == null) { + throw new LcnException("Sub Handler not found for: " + channelGroup); + } - Optional number = channelUidToChannelNumber(channelUid, channelGroup); - - if (command instanceof RefreshType) { - number.ifPresent(n -> subHandler.handleRefresh(channelGroup, n)); - subHandler.handleRefresh(channelUid.getIdWithoutGroup()); - } else if (command instanceof OnOffType) { - subHandler.handleCommandOnOff(castCommand(command), channelGroup, number.get()); - } else if (command instanceof DimmerOutputCommand) { - subHandler.handleCommandDimmerOutput(castCommand(command), number.get()); - } else if (command instanceof PercentType && number.isPresent()) { - subHandler.handleCommandPercent(castCommand(command), channelGroup, number.get()); - } else if (command instanceof HSBType) { - subHandler.handleCommandHsb(castCommand(command), channelUid.getIdWithoutGroup()); - } else if (command instanceof PercentType) { - subHandler.handleCommandPercent(castCommand(command), channelGroup, channelUid.getIdWithoutGroup()); - } else if (command instanceof StringType) { - subHandler.handleCommandString(castCommand(command), number.get()); - } else if (command instanceof DecimalType) { - DecimalType decimalType = castCommand(command); - DecimalType nativeValue = getConverter(channelUid).onCommandFromItem(decimalType.doubleValue()); - subHandler.handleCommandDecimal(nativeValue, channelGroup, number.get()); - } else if (command instanceof QuantityType) { - QuantityType quantityType = castCommand(command); - DecimalType nativeValue = getConverter(channelUid).onCommandFromItem(quantityType); - subHandler.handleCommandDecimal(nativeValue, channelGroup, number.get()); - } else if (command instanceof UpDownType) { - subHandler.handleCommandUpDown(castCommand(command), channelGroup, number.get()); - } else if (command instanceof StopMoveType) { - subHandler.handleCommandStopMove(castCommand(command), channelGroup, number.get()); - } else { - throw new LcnException("Unsupported command type"); - } + Optional number = channelUidToChannelNumber(channelUid, channelGroup); + + if (command instanceof RefreshType) { + number.ifPresent(n -> subHandler.handleRefresh(channelGroup, n)); + subHandler.handleRefresh(channelUid.getIdWithoutGroup()); + } else if (command instanceof OnOffType) { + subHandler.handleCommandOnOff(castCommand(command), channelGroup, number.get()); + } else if (command instanceof DimmerOutputCommand) { + subHandler.handleCommandDimmerOutput(castCommand(command), number.get()); + } else if (command instanceof PercentType && number.isPresent()) { + subHandler.handleCommandPercent(castCommand(command), channelGroup, number.get()); + } else if (command instanceof HSBType) { + subHandler.handleCommandHsb(castCommand(command), channelUid.getIdWithoutGroup()); + } else if (command instanceof PercentType) { + subHandler.handleCommandPercent(castCommand(command), channelGroup, channelUid.getIdWithoutGroup()); + } else if (command instanceof StringType) { + subHandler.handleCommandString(castCommand(command), number.get()); + } else if (command instanceof DecimalType) { + DecimalType decimalType = castCommand(command); + DecimalType nativeValue = getConverter(channelUid).onCommandFromItem(decimalType.doubleValue()); + subHandler.handleCommandDecimal(nativeValue, channelGroup, number.get()); + } else if (command instanceof QuantityType) { + QuantityType quantityType = castCommand(command); + DecimalType nativeValue = getConverter(channelUid).onCommandFromItem(quantityType); + subHandler.handleCommandDecimal(nativeValue, channelGroup, number.get()); + } else if (command instanceof UpDownType) { + subHandler.handleCommandUpDown(castCommand(command), channelGroup, number.get()); + } else if (command instanceof StopMoveType) { + subHandler.handleCommandStopMove(castCommand(command), channelGroup, number.get()); + } else { + throw new LcnException("Unsupported command type"); } } catch (IllegalArgumentException | NoSuchElementException | LcnException e) { logger.warn("{}: Failed to handle command {}: {}", channelUid, command.getClass().getSimpleName(), @@ -252,11 +244,6 @@ private T castCommand(Command command) throws LcnException { */ @SuppressWarnings("null") public void handleStatusMessage(String pck) { - // this method can be invoked, when initialize() has not finished, yet. - if (thing.getStatus() != ThingStatus.ONLINE) { - return; - } - synchronized (subHandlers) { for (AbstractLcnModuleSubHandler handler : subHandlers.values()) { if (handler.tryParse(pck)) { @@ -418,5 +405,7 @@ public LcnAddrMod getStatusMessageAddress() { @Override public void dispose() { metadataSubHandlers.clear(); + subHandlers.clear(); + converters.clear(); } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java index e73f973a56d0d..538eb2ea27c7a 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java @@ -42,8 +42,7 @@ import org.slf4j.LoggerFactory; /** - * The {@link PckGatewayHandler} is responsible for handling commands, which are - * sent to one of the channels. + * The {@link PckGatewayHandler} is responsible for the communication via a PCK gateway. * * @author Fabian Wolter - Initial contribution */ @@ -143,7 +142,7 @@ private LcnAddr getLcnAddrFromThing(Thing childThing) throws LcnException { } /** - * Enqueues a PCK command to be sent to an LCN module. + * Enqueues a PCK (String) command to be sent to an LCN module. * * @param addr the modules address * @param wantsAck true, if the module shall send an ACK upon successful processing @@ -159,7 +158,7 @@ public void queue(LcnAddr addr, boolean wantsAck, String pck) { } /** - * Enqueues a PCK command to be sent to an LCN module. + * Enqueues a PCK (ByteBuffer) command to be sent to an LCN module. * * @param addr the modules address * @param wantsAck true, if the module shall send an ACK upon successful processing @@ -175,7 +174,7 @@ public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer pck) { } /** - * Sends a broadcast message to all LCN modules. All LCN modules are requested to answer with an Ack. + * Sends a broadcast message to all LCN modules: All LCN modules are requested to answer with an Ack. */ void sendModuleDiscoveryCommand() { Connection localConnection = connection; 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 b78b02190b875..3c628bfdb34f4 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 @@ -93,38 +93,6 @@ public enum TimeUnit { MINUTES, HOURS, DAYS; - - /** - * Parses the given input into a time unit. - * It supports several alternative terms. - * - * @param input the text to parse - * @return the parsed {@link TimeUnit} - * @throws LcnException if input could not be parsed - */ - public static TimeUnit parse(String input) throws LcnException { - switch (input.toUpperCase()) { - case "SECONDS": - case "SECOND": // Allow singular too - case "SEC": - case "S": - return SECONDS; - case "MINUTES": - case "MINUTE": // Allow singular too - case "MIN": - case "M": - return MINUTES; - case "HOURS": - case "HOUR": // Allow singular too - case "H": - return HOURS; - case "DAYS": - case "DAY": // Allow singular too - case "D": - return DAYS; - } - throw new LcnException(); - } } /** Relay-state modifiers used in LCN commands. */ diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnException.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnException.java index d630469b4cef7..3731bf000b6a3 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnException.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/LcnException.java @@ -31,7 +31,7 @@ public LcnException(String message) { super(message); } - public LcnException(NumberFormatException e) { + public LcnException(Exception e) { super(e); } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java index 2df88475bf583..ddad2b1ca40bd 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java @@ -28,7 +28,7 @@ */ @NonNullByDefault public abstract class AbstractConnectionState extends AbstractState { - /** The PCk gateway's Connection */ + /** The PCK gateway's Connection */ protected Connection connection; /** An openHAB scheduler */ protected ScheduledExecutorService scheduler; diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java index bc58217ae5e33..befad75cb2ffe 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java @@ -13,6 +13,7 @@ package org.openhab.binding.lcn.internal.connection; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.ScheduledFuture; @@ -25,7 +26,7 @@ */ @NonNullByDefault public abstract class AbstractState { - private List> usedTimers = new ArrayList<>(); + private List> usedTimers = Collections.synchronizedList(new ArrayList<>()); protected StateContext context; public AbstractState(StateContext context) { @@ -41,9 +42,9 @@ public AbstractState(StateContext context) { * Stops all timers, the State has been started. */ void cancelAllTimers() { - List> copy = new ArrayList<>(usedTimers); - - copy.forEach(t -> t.cancel(true)); + synchronized (usedTimers) { + usedTimers.forEach(t -> t.cancel(true)); + } } /** diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java index 67f54b73cc8e9..cb14bdf261277 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; @@ -67,10 +68,10 @@ public class Connection { private int localSegId; private final ByteBuffer readBuffer = ByteBuffer.allocate(1024); private final ByteBuffer sendBuffer = ByteBuffer.allocate(MAX_PCK_STRING_LENGTH); - private final LinkedBlockingQueue<@Nullable SendData> sendQueue = new LinkedBlockingQueue<>(); - private final LinkedBlockingQueue<@Nullable PckQueueItem> offlineSendQueue = new LinkedBlockingQueue<>(); + private final Queue<@Nullable SendData> sendQueue = new LinkedBlockingQueue<>(); + private final Queue<@Nullable PckQueueItem> offlineSendQueue = new LinkedBlockingQueue<>(); private final Map modData = Collections.synchronizedMap(new HashMap<>()); - private boolean writeInProgress; + private volatile boolean writeInProgress; private ScheduledExecutorService scheduler; private StateMachine stateMachine; @@ -173,10 +174,10 @@ public void completed(@Nullable Integer transmittedByteCount, @Nullable Void att synchronized (Connection.this) { if (transmittedByteCount == null || transmittedByteCount == -1) { String msg = "Connection was closed by foreign host."; - logger.debug(msg); stateMachine.handleConnectionFailed(new LcnException(msg)); } else { try { + // read data chunks from socket and separate frames readBuffer.flip(); int aPos = readBuffer.position(); // 0 String s = new String(readBuffer.array(), aPos, transmittedByteCount, diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java index 2ae808988ea18..55221bd793f8a 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java @@ -50,7 +50,7 @@ public void startWorking() { try { // Open Channel by using the system-wide default AynchronousChannelGroup. - // So, Threads are used or re-used on demand. + // So, Threads are used or re-used on demand by the JVM. AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(); // Do not wait until some buffer is filled, send PCK commands immediately channel.setOption(StandardSocketOptions.TCP_NODELAY, true); diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/LightConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/LightConverter.java index 4f71a25ec579e..22209fa1e84d4 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/LightConverter.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/LightConverter.java @@ -39,7 +39,7 @@ public int toNative(double value) { public double toHumanReadable(long value) { // Max. value hardware can deliver is 100klx. Apply hard limit, because higher native values lead to very big // lux values. - if (value > 1152) { + if (value > toNative(100e3)) { return Double.NaN; } return Math.exp(value / 100d); diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java index 80582bb2d545e..a24260460c1a7 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java @@ -120,7 +120,7 @@ protected void startScan() { ServicesResponse deserialized = xmlToServiceResponse(response); - Map properties = new HashMap<>(5); + Map properties = new HashMap<>(); properties.put("hostname", addr.getHostAddress()); properties.put("port", deserialized.getExtServices().getExtService().getLocalPort()); @@ -132,7 +132,7 @@ protected void startScan() { + deserialized.getServer().getMachineName() + ")"); thingDiscovered(discoveryResult.build()); - } while (true); + } while (true); // left by SocketTimeoutException } catch (SocketTimeoutException e) { // nothing } From ce02f433e5d4071dbebb9ee68da0e22895887b6b Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Sat, 6 Jun 2020 21:17:33 +0200 Subject: [PATCH 06/16] Remove test project Signed-off-by: Fabian Wolter --- bundles/org.openhab.binding.test/.classpath | 32 ------ bundles/org.openhab.binding.test/.project | 23 ----- bundles/org.openhab.binding.test/NOTICE | 13 --- bundles/org.openhab.binding.test/README.md | 56 ----------- bundles/org.openhab.binding.test/pom.xml | 16 --- .../src/main/feature/feature.xml | 9 -- .../test/internal/testBindingConstants.java | 34 ------- .../test/internal/testConfiguration.java | 26 ----- .../binding/test/internal/testHandler.java | 98 ------------------- .../test/internal/testHandlerFactory.java | 56 ----------- .../resources/ESH-INF/binding/binding.xml | 10 -- .../ESH-INF/i18n/test_xx_XX.properties | 17 ---- .../resources/ESH-INF/thing/thing-types.xml | 32 ------ 13 files changed, 422 deletions(-) delete mode 100644 bundles/org.openhab.binding.test/.classpath delete mode 100644 bundles/org.openhab.binding.test/.project delete mode 100644 bundles/org.openhab.binding.test/NOTICE delete mode 100644 bundles/org.openhab.binding.test/README.md delete mode 100644 bundles/org.openhab.binding.test/pom.xml delete mode 100644 bundles/org.openhab.binding.test/src/main/feature/feature.xml delete mode 100644 bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testBindingConstants.java delete mode 100644 bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testConfiguration.java delete mode 100644 bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandler.java delete mode 100644 bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandlerFactory.java delete mode 100644 bundles/org.openhab.binding.test/src/main/resources/ESH-INF/binding/binding.xml delete mode 100644 bundles/org.openhab.binding.test/src/main/resources/ESH-INF/i18n/test_xx_XX.properties delete mode 100644 bundles/org.openhab.binding.test/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/bundles/org.openhab.binding.test/.classpath b/bundles/org.openhab.binding.test/.classpath deleted file mode 100644 index a5d95095ccaaf..0000000000000 --- a/bundles/org.openhab.binding.test/.classpath +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/bundles/org.openhab.binding.test/.project b/bundles/org.openhab.binding.test/.project deleted file mode 100644 index 6194f1b0b2bc5..0000000000000 --- a/bundles/org.openhab.binding.test/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - org.openhab.binding.test - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/bundles/org.openhab.binding.test/NOTICE b/bundles/org.openhab.binding.test/NOTICE deleted file mode 100644 index 38d625e349232..0000000000000 --- a/bundles/org.openhab.binding.test/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -This content is produced and maintained by the openHAB project. - -* Project home: https://www.openhab.org - -== Declared Project Licenses - -This program and the accompanying materials are made available under the terms -of the Eclipse Public License 2.0 which is available at -https://www.eclipse.org/legal/epl-2.0/. - -== Source Code - -https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.binding.test/README.md b/bundles/org.openhab.binding.test/README.md deleted file mode 100644 index 68627f1b47dce..0000000000000 --- a/bundles/org.openhab.binding.test/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# test Binding - -_Give some details about what this binding is meant for - a protocol, system, specific device._ - -_If possible, provide some resources like pictures, a YouTube video, etc. to give an impression of what can be done with this binding. You can place such resources into a `doc` folder next to this README.md._ - -## Supported Things - -_Please describe the different supported things / devices within this section._ -_Which different types are supported, which models were tested etc.?_ -_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/ESH-INF/thing``` of your binding._ - -## Discovery - -_Describe the available auto-discovery features here. Mention for what it works and what needs to be kept in mind when using it._ - -## Binding Configuration - -_If your binding requires or supports general configuration settings, please create a folder ```cfg``` and place the configuration file ```.cfg``` inside it. In this section, you should link to this file and provide some information about the options. The file could e.g. look like:_ - -``` -# Configuration for the Philips Hue Binding -# -# Default secret key for the pairing of the Philips Hue Bridge. -# It has to be between 10-40 (alphanumeric) characters -# This may be changed by the user for security reasons. -secret=openHABSecret -``` - -_Note that it is planned to generate some part of this based on the information that is available within ```src/main/resources/ESH-INF/binding``` of your binding._ - -_If your binding does not offer any generic configurations, you can remove this section completely._ - -## Thing Configuration - -_Describe what is needed to manually configure a thing, either through the (Paper) UI or via a thing-file. This should be mainly about its mandatory and optional configuration parameters. A short example entry for a thing file can help!_ - -_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/ESH-INF/thing``` of your binding._ - -## Channels - -_Here you should provide information about available channel types, what their meaning is and how they can be used._ - -_Note that it is planned to generate some part of this based on the XML files within ```src/main/resources/ESH-INF/thing``` of your binding._ - -| channel | type | description | -|----------|--------|------------------------------| -| control | Switch | This is the control channel | - -## Full Example - -_Provide a full usage example based on textual configuration files (*.things, *.items, *.sitemap)._ - -## Any custom content here! - -_Feel free to add additional sections for whatever you think should also be mentioned about your binding!_ diff --git a/bundles/org.openhab.binding.test/pom.xml b/bundles/org.openhab.binding.test/pom.xml deleted file mode 100644 index 9b71b550a3263..0000000000000 --- a/bundles/org.openhab.binding.test/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - 4.0.0 - - - org.openhab.addons.bundles - org.openhab.addons.reactor.bundles - 2.5.3-SNAPSHOT - - - org.openhab.binding.test - - openHAB Add-ons :: Bundles :: test Binding - - diff --git a/bundles/org.openhab.binding.test/src/main/feature/feature.xml b/bundles/org.openhab.binding.test/src/main/feature/feature.xml deleted file mode 100644 index c8deb162e6c40..0000000000000 --- a/bundles/org.openhab.binding.test/src/main/feature/feature.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features - - - openhab-runtime-base - mvn:org.openhab.addons.bundles/org.openhab.binding.test/${project.version} - - diff --git a/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testBindingConstants.java b/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testBindingConstants.java deleted file mode 100644 index 9716ca15780f8..0000000000000 --- a/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testBindingConstants.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * 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.test.internal; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.thing.ThingTypeUID; - -/** - * The {@link testBindingConstants} class defines common constants, which are - * used across the whole binding. - * - * @author ich - Initial contribution - */ -@NonNullByDefault -public class testBindingConstants { - - private static final String BINDING_ID = "test"; - - // List of all Thing Type UIDs - public static final ThingTypeUID THING_TYPE_SAMPLE = new ThingTypeUID(BINDING_ID, "sample"); - - // List of all Channel ids - public static final String CHANNEL_1 = "channel1"; -} diff --git a/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testConfiguration.java b/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testConfiguration.java deleted file mode 100644 index fcc0c3a0adedd..0000000000000 --- a/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testConfiguration.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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.test.internal; - -/** - * The {@link testConfiguration} class contains fields mapping thing configuration parameters. - * - * @author ich - Initial contribution - */ -public class testConfiguration { - - /** - * Sample configuration parameter. Replace with your own. - */ - public String config1; -} diff --git a/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandler.java b/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandler.java deleted file mode 100644 index d452eba8c20df..0000000000000 --- a/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandler.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * 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.test.internal; - -import static org.openhab.binding.test.internal.testBindingConstants.*; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingStatus; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; -import org.eclipse.smarthome.core.types.Command; -import org.eclipse.smarthome.core.types.RefreshType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@link testHandler} is responsible for handling commands, which are - * sent to one of the channels. - * - * @author ich - Initial contribution - */ -@NonNullByDefault -public class testHandler extends BaseThingHandler { - - private final Logger logger = LoggerFactory.getLogger(testHandler.class); - - private @Nullable testConfiguration config; - - public testHandler(Thing thing) { - super(thing); - } - - @Override - public void handleCommand(ChannelUID channelUID, Command command) { - if (CHANNEL_1.equals(channelUID.getId())) { - if (command instanceof RefreshType) { - // TODO: handle data refresh - } - - // TODO: handle command - - // Note: if communication with thing fails for some reason, - // indicate that by setting the status with detail information: - // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - // "Could not control device at IP address x.x.x.x"); - } - } - - @Override - public void initialize() { - // logger.debug("Start initializing!"); - config = getConfigAs(testConfiguration.class); - - // TODO: Initialize the handler. - // The framework requires you to return from this method quickly. Also, before leaving this method a thing - // status from one of ONLINE, OFFLINE or UNKNOWN must be set. This might already be the real thing status in - // case you can decide it directly. - // In case you can not decide the thing status directly (e.g. for long running connection handshake using WAN - // access or similar) you should set status UNKNOWN here and then decide the real status asynchronously in the - // background. - - // set the thing status to UNKNOWN temporarily and let the background task decide for the real status. - // the framework is then able to reuse the resources from the thing handler initialization. - // we set this upfront to reliably check status updates in unit tests. - updateStatus(ThingStatus.UNKNOWN); - - // Example for background initialization: - scheduler.execute(() -> { - boolean thingReachable = true; // - // when done do: - if (thingReachable) { - updateStatus(ThingStatus.ONLINE); - } else { - updateStatus(ThingStatus.OFFLINE); - } - }); - - // logger.debug("Finished initializing!"); - - // Note: When initialization can NOT be done set the status with more details for further - // analysis. See also class ThingStatusDetail for all available status details. - // Add a description to give user information to understand why thing does not work as expected. E.g. - // updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - // "Can not access device as username and/or password are invalid"); - } -} diff --git a/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandlerFactory.java b/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandlerFactory.java deleted file mode 100644 index d6bc82520613f..0000000000000 --- a/bundles/org.openhab.binding.test/src/main/java/org/openhab/binding/test/internal/testHandlerFactory.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * 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.test.internal; - -import static org.openhab.binding.test.internal.testBindingConstants.*; - -import java.util.Collections; -import java.util.Set; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; -import org.eclipse.smarthome.core.thing.binding.ThingHandler; -import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; -import org.osgi.service.component.annotations.Component; - -/** - * The {@link testHandlerFactory} is responsible for creating things and thing - * handlers. - * - * @author ich - Initial contribution - */ -@NonNullByDefault -@Component(configurationPid = "binding.test", service = ThingHandlerFactory.class) -public class testHandlerFactory extends BaseThingHandlerFactory { - - private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_SAMPLE); - - @Override - public boolean supportsThingType(ThingTypeUID thingTypeUID) { - return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); - } - - @Override - protected @Nullable ThingHandler createHandler(Thing thing) { - ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - - if (THING_TYPE_SAMPLE.equals(thingTypeUID)) { - return new testHandler(thing); - } - - return null; - } -} diff --git a/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/binding/binding.xml deleted file mode 100644 index 00139b390269a..0000000000000 --- a/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/binding/binding.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - test Binding - This is the binding for test. - ich - - diff --git a/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/i18n/test_xx_XX.properties b/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/i18n/test_xx_XX.properties deleted file mode 100644 index c51a1a75a053d..0000000000000 --- a/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/i18n/test_xx_XX.properties +++ /dev/null @@ -1,17 +0,0 @@ -# FIXME: please substitute the xx_XX with a proper locale, ie. de_DE -# FIXME: please do not add the file to the repo if you add or change no content -# binding -binding.test.name = -binding.test.description = - -# thing types -thing-type.test.sample.label = -thing-type.test.sample.description = - -# thing type config description -thing-type.config.test.sample.config1.label = -thing-type.config.test.sample.config1.description = - -# channel types -channel-type.test.sample-channel.label = -channel-type.test.sample-channel.description = diff --git a/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/thing/thing-types.xml deleted file mode 100644 index 4410c184a4860..0000000000000 --- a/bundles/org.openhab.binding.test/src/main/resources/ESH-INF/thing/thing-types.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - Sample thing for test Binding - - - - - - - - - This is a sample text configuration parameter - - - - - - - - testItem - - Sample channel for test Binding - - - From f286ec5e63678277ee6c0c42b0e316ffb3997d38 Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Tue, 9 Jun 2020 20:06:12 +0200 Subject: [PATCH 07/16] Incorporate review feedback Signed-off-by: Fabian Wolter --- bundles/org.openhab.binding.lcn/README.md | 91 ++++++----- .../doc/module_discovery.png | Bin 96808 -> 0 bytes .../doc/pck_discovery.png | Bin 66933 -> 0 bytes bundles/org.openhab.binding.lcn/doc/unit.png | Bin 94240 -> 0 bytes .../lcn/internal/DimmerOutputProfile.java | 2 +- .../lcn/internal/LcnModuleActions.java | 44 +++--- .../internal/LcnModuleDiscoveryService.java | 72 +++++---- .../lcn/internal/LcnModuleHandler.java | 97 ++++-------- .../lcn/internal/PckGatewayHandler.java | 12 +- .../internal/common/DimmerOutputCommand.java | 6 +- .../lcn/internal/common/LcnChannelGroup.java | 55 +++---- .../internal/common/NullScheduledFuture.java | 80 ---------- .../lcn/internal/common/PckGenerator.java | 87 +++++------ .../binding/lcn/internal/common/Variable.java | 8 +- .../lcn/internal/common/VariableValue.java | 2 +- .../connection/AbstractConnectionState.java | 7 +- ...bstractConnectionStateSendCredentials.java | 3 +- .../internal/connection/AbstractState.java | 4 +- .../lcn/internal/connection/Connection.java | 141 ++++++++---------- .../connection/ConnectionStateConnected.java | 3 +- .../connection/ConnectionStateConnecting.java | 3 +- ...ectionStateGracePeriodBeforeReconnect.java | 3 +- .../connection/ConnectionStateInit.java | 3 +- .../ConnectionStateSegmentScan.java | 3 +- .../ConnectionStateSendDimMode.java | 3 +- .../connection/ConnectionStateShutdown.java | 3 +- ...ConnectionStateWaitForLcnBusConnected.java | 21 ++- .../lcn/internal/connection/ModInfo.java | 90 +++++------ .../lcn/internal/connection/PckQueueItem.java | 15 +- .../internal/connection/RequestStatus.java | 2 +- .../lcn/internal/connection/SendData.java | 8 +- .../lcn/internal/connection/SendDataPck.java | 21 ++- .../connection/SendDataPlainText.java | 9 +- .../lcn/internal/connection/StateContext.java | 4 +- .../lcn/internal/connection/StateMachine.java | 7 +- .../internal/converter/AngleConverter.java | 41 ----- .../lcn/internal/converter/Co2Converter.java | 41 ----- ...ableValueConverter.java => Converter.java} | 55 +++---- .../lcn/internal/converter/Converters.java | 62 ++++++++ .../internal/converter/CurrentConverter.java | 41 ----- .../internal/converter/EnergyConverter.java | 36 ----- .../internal/converter/IdentityConverter.java | 62 -------- .../internal/converter/LightConverter.java | 47 ------ .../internal/converter/PowerConverter.java | 36 ----- ...tractS0Converter.java => S0Converter.java} | 15 +- .../converter/TemperatureConverter.java | 42 ------ .../internal/converter/VoltageConverter.java | 41 ----- .../converter/WindspeedConverter.java | 41 ----- .../internal/pchkdiscovery/ExtService.java | 4 +- .../internal/pchkdiscovery/ExtServices.java | 2 +- .../lcn/internal/pchkdiscovery/Server.java | 12 +- .../pchkdiscovery/ServicesResponse.java | 8 +- .../lcn/internal/pchkdiscovery/Version.java | 4 +- .../AbstractLcnModuleSubHandler.java | 8 +- .../subhandler/LcnModuleOutputSubHandler.java | 11 +- .../LcnModuleThresholdSubHandler.java | 3 +- .../main/resources/ESH-INF/config/config.xml | 1 + .../resources/ESH-INF/thing/thing-types.xml | 19 +-- .../lcn/internal/ModuleActionsTest.java | 23 ++- 59 files changed, 550 insertions(+), 1014 deletions(-) delete mode 100644 bundles/org.openhab.binding.lcn/doc/module_discovery.png delete mode 100644 bundles/org.openhab.binding.lcn/doc/pck_discovery.png delete mode 100644 bundles/org.openhab.binding.lcn/doc/unit.png delete mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/NullScheduledFuture.java delete mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AngleConverter.java delete mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Co2Converter.java rename bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/{AbstractVariableValueConverter.java => Converter.java} (69%) create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Converters.java delete mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/CurrentConverter.java delete mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/EnergyConverter.java delete mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/IdentityConverter.java delete mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/LightConverter.java delete mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/PowerConverter.java rename bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/{AbstractS0Converter.java => S0Converter.java} (72%) delete mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/TemperatureConverter.java delete mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/VoltageConverter.java delete mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/WindspeedConverter.java diff --git a/bundles/org.openhab.binding.lcn/README.md b/bundles/org.openhab.binding.lcn/README.md index f201154f033bb..6226424c7e662 100644 --- a/bundles/org.openhab.binding.lcn/README.md +++ b/bundles/org.openhab.binding.lcn/README.md @@ -15,9 +15,6 @@ Bus members are inter-connected via a free wire in the standard NYM cable. Wirel This binding uses TCP/IP to access the LCN bus via the software LCN-PCHK (Windows/Linux) or the DIN rail device LCN-PKE. **This means 1 unused LCN-PCHK license or a LCN-PKE is required** -It is recommended to configure *Thing*s in [Paper UI](https://www.openhab.org/docs/configuration/paperui.html) and *Item*s in text files. -*Channel*s and *Link*s are configured automatically, then. - ## LCN Overview LCN modules and their connecting peripherals are explained in the following. @@ -124,6 +121,16 @@ No known features/changes that need special handling were added until now (2020) Modules with older and newer firmware should work, too. The module hardware types (e.g. LCN-SH, LCN-HU, LCN-UPP, ...) are compatible to each other and can therefore be handled all in the same way. +Thing ID: `module` + +| Name | Description | Type | Required | +|-------------|----------------------------------------------------------------|---------|----------| +| `moduleId` | The module ID, configured in LCN-PRO | Integer | Yes | +| `segmentId` | The segment ID the module is in (0 if no segments are present) | Integer | Yes | + +openHAB's discovery function can be used to add LCN modules automatically. +See [Discover LCN Modules](#discover-lcn-modules). + ### Thing: LCN PCK Gateway PCK is the protocol spoken over TCP/IP with a PCK gateway to communicate with the LCN bus. @@ -136,6 +143,26 @@ Several PCK gateways can be added to openHAB to control multiple LCN busses in d The minimum recommended version is LCN-PCHK 2.8 (older versions will also work, but lack some functionality). Visit [https://www.lcn.eu](https://www.lcn.eu) for updates. +Thing ID: `pckGateway` + +| Name | Description | Type | Required | +|-------------|------------------------------------------------------------------------------------------------------------|---------|----------| +| `hostname` | Hostname or IP address of the LCN-PCHK gateway | String | Yes | +| `port` | TCP port of the LCN-PCHK gateway (default:4114) | Integer | Yes | +| `username` | Username configured within LCN-PCHK Monitor | String | Yes | +| `password` | Password configured within LCN-PCHK Monitor | String | Yes | +| `mode` | Dimmer resolution: `native50` or `native200` See below. | String | Yes | +| `timeoutMs` | Period after which an LCN command is resent, when no acknowledge has been received (in ms) (default: 3500) | Integer | Yes | + +> **IMPORTANT:** You need to configure the dimmer output resolution. This setting is valid for the **whole** LCN bus.
    +The setting is either 0-50 steps or 0-200 steps. +It **has to be the same** as in the parameterizing software **LCN-PRO** under Options/Settings/Expert Settings. +See the following screenshot. + +![LCN-PRO screenshot, showing the 50 or 200 steps for the dimmer outputs](doc/LCN-PRO_output_steps.png) + +When using a wrong dimmer output setting, dimming the outputs will result in unintended behavior. + ### Thing: LCN Group LCN modules can be assigned to groups with the programming software *LCN-PRO*. @@ -145,14 +172,22 @@ To send commands to an LCN group, the group needs to be added to openHAB as a *T One LCN module within the group is used to represent the status of the whole group. For example, when a Dimmer Output is controlled via a LCN group *Thing*, openHAB will always visualize the state of the Dimmer Output of the chosen module. The states of the other modules in the group are ignored for visualization. +Thing ID: `group` + +| Name | Description | Type | Required | +|-------------|----------------------------------------------------------------------------------------------------------------------------------------------|---------|----------| +| `groupId` | The group number, configured in LCN-PRO | Integer | Yes | +| `moduleId` | The module ID of any module in the group. The state of this module is used for visualization of the group as representative for all modules. | Integer | Yes | +| `segmentId` | The segment ID of all modules in this group (0 if no segments are present) | Integer | Yes | + +The `groupId` must match the previously configured group number in the programming software *LCN-PRO*. + ## Discovery ### Discover LCN Modules -Basic data of LCN modules can be read out automatically by openHAB. -Click on one of the check marks to add the LCN modules to openHAB: - -![Paper UI screenshot, showing the discovery result for LCN modules](doc/module_discovery.png) +Basic data of LCN modules can be read out by openHAB. +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. @@ -168,9 +203,6 @@ Unfortunately, *LCN-PCHK* listens only on the first network interface of the com If your PCK gateway has multiple network interfaces, *LCN-PCHK* may listen on the wrong interface and fails to respond to the discovery request. Discovery has successfully been tested with LCN-PCHK 3.2.2 running on a Raspberry Pi with Raspbian and openHAB running on Windows 10. -See the following screenshot: - -![Paper UI screenshot, showing the discovery result for PCK gateways](doc/pck_discovery.png) If discovery fails, you can add a PCK gateway manually. See [Thing: PCK Gateway](#thing-lcn-pck-gateway). @@ -179,42 +211,9 @@ See [Thing: PCK Gateway](#thing-lcn-pck-gateway). When adding a PCK gateway by discovery, the new *Thing*'s UID is the MAC address of the device, running the PCK gateway. -## Thing Configuration - -### Configure LCN-PCHK/-PKE Connection - -Click on the plus symbol under Configuration/Things in Paper UI. Select "Add manually". -When adding a PCK gateway manually, the *hostname/IP address*, *port* and *username* and *password* need to be configured. - -> **IMPORTANT:** You need to configure the dimmer output resolution. This setting is valid for the **whole** LCN bus.
    -The setting is either 0-50 steps or 0-200 steps. -It **has to be the same** as in the parameterizing software **LCN-PRO** under Options/Settings/Expert Settings. -See the following screenshot. - -![LCN-PRO screenshot, showing the 50 or 200 steps for the dimmer outputs](doc/LCN-PRO_output_steps.png) - -When using a wrong dimmer output setting, dimming the outputs will result in unintended behavior. - -### Add LCN Module Manually - -Click on the plus symbol under Configuration/Things in Paper UI. Select "Add manually". -Now you can configure the *module ID* and the *segment ID* (0 if no segments are used). -After confirming, the new module should show up under Configuration/Things. - -openHAB's discovery function can be used to add LCN modules automatically. -See [Discover LCN Modules](#discover-lcn-modules). - -### Add LCN Group - -Click on the plus symbol under Configuration/Things in Paper UI. Select "Add manually". -Now you can configure the *group number*, as previously configured in the programming software *LCN-PRO*. -If you use segments, you need to configure the segment ID of the modules within the group. Enter 0, if you have no segments. -Lastly, you have to configure the *module ID* of any module within the group. -This module will be used for visualization, as representative for all modules within the group. - ## Supported LCN Features and openHAB Channels -The following table lists all features of LCN and their mappings to openHAB *Channel*s. +The following table lists all features of LCN and their mappings to openHAB Channels. These Channels are available for the *Things* LCN module (`module`) and LCN group (`group`). The PCK gateway (`pckGateway`) has no Channels. Although, there are many **Not implemented** entries, the vast majority of LCN features can be used with openHAB:
    If a special command is needed, the [Hit Key](#hit-key) action (German: "Sende Taste") can be used to hit a module's key virtually and execute an arbitrary command. @@ -277,9 +276,7 @@ If a special command is needed, the [Hit Key](#hit-key) action (German: "Sende T **For some *Channel*s a unit should be configured for visualization.** By default the native LCN value is used. - - -![Screenshot, showing the Channel configuration](doc/unit.png) +S0 counter Channels need to be the pulses per kWh configured. If the value is left blank, a default value of 1000 pulses/kWh is set. ### Transponder diff --git a/bundles/org.openhab.binding.lcn/doc/module_discovery.png b/bundles/org.openhab.binding.lcn/doc/module_discovery.png deleted file mode 100644 index 0a6f79ad6cf89f85f2232137c0c80173a86c5a5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96808 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 P1K>|j+X#l%d=UOWj3EB3 diff --git a/bundles/org.openhab.binding.lcn/doc/pck_discovery.png b/bundles/org.openhab.binding.lcn/doc/pck_discovery.png deleted file mode 100644 index 058a96d19c10922c45f8dacd8d0869e3dea6147c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66933 zcmdpe1zX%uxAh>!i@UoNx8lX!-K{tjcc&D0*TG#1h2rk+THM{WIA8kSd*2`No#z=c zOeP^Y*=NgIYZI=dAo&pi9{~gceUz3GQvrdXp+O*sWH?CRlYQ8_M_>c*Af@dL0wJRR zdqK>s`MrQZq#$WAVKoo%X_h-!qrd%Ka3ftcr?wpmQdY9C0xm2NQ#^WrSpKWD+_YhW zw`yDX-{RFen~`>E)-_7%X|aOvd~$i^h%j=Qi(g$#-!2sSCh6Ea1t)%wYD8op;!Hdz zFG@^Iq^GkxXZv<+Jbq44At&8@$5)XdU;4(*|M=LJ96R(&q(@*a5drRh#)BXX-x|jM z{_=0bEdV_t^xx6NBa@2+{oesD`2Tiigi{NRgNhWLw;^Tz`)m4tZy6fQ37#GasD?sS z_IL8P`0+k(_2Iue!C3$HG4HijWh#{DFJg&E(S?Vx7Sl!)Ta}k*pYy={_nR+
    S_ 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%0De2eEvB-rEeh9aHpzqNxqpp@pLHk$~Tl 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 { + Method m = actions.getClass().getDeclaredMethod(method.getName(), + method.getParameterTypes()); + return m.invoke(actions, args); + }); + } + } + throw new IllegalArgumentException("Actions is not an instance of EcobeeActions"); + } + /** Static alias to support the old DSL rules engine and make the action available there. */ public static void hitKey(@Nullable ThingActions actions, @Nullable String table, int key, @Nullable String action) { - if (actions instanceof LcnModuleActions) { - ((LcnModuleActions) actions).hitKey(table, key, action); - } else { - throw new IllegalArgumentException( - "Instance is not a " + LcnModuleActions.class.getSimpleName() + " class."); - } + invokeMethodOf(actions).hitKey(table, key, action); } /** Static alias to support the old DSL rules engine and make the action available there. */ public static void flickerOutput(@Nullable ThingActions actions, int output, int depth, int ramp, int count) { - if (actions instanceof LcnModuleActions) { - ((LcnModuleActions) actions).flickerOutput(output, depth, ramp, count); - } else { - throw new IllegalArgumentException( - "Instance is not a " + LcnModuleActions.class.getSimpleName() + " class."); - } + invokeMethodOf(actions).flickerOutput(output, depth, ramp, count); } /** Static alias to support the old DSL rules engine and make the action available there. */ public static void sendDynamicText(@Nullable ThingActions actions, int row, @Nullable String text) { - if (actions instanceof LcnModuleActions) { - ((LcnModuleActions) actions).sendDynamicText(row, text); - } else { - throw new IllegalArgumentException( - "Instance is not a " + LcnModuleActions.class.getSimpleName() + " class."); - } + invokeMethodOf(actions).sendDynamicText(row, text); } private LcnModuleHandler getHandler() throws LcnException { 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 c0ff70c637e64..5d4bca686cad3 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 @@ -19,6 +19,7 @@ import java.util.Map; import java.util.Queue; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -37,7 +38,6 @@ import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; import org.openhab.binding.lcn.internal.common.LcnAddrMod; -import org.openhab.binding.lcn.internal.common.NullScheduledFuture; import org.openhab.binding.lcn.internal.connection.Connection; import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaAckSubHandler; import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaFirmwareSubHandler; @@ -69,14 +69,13 @@ public class LcnModuleDiscoveryService extends AbstractDiscoveryService private static final Set SUPPORTED_THING_TYPES_UIDS = Collections .unmodifiableSet(Stream.of(LcnBindingConstants.THING_TYPE_MODULE).collect(Collectors.toSet())); private @Nullable PckGatewayHandler bridgeHandler; - private Map> moduleNames = Collections.synchronizedMap(new HashMap<>()); - private Map discoveryResultBuilders = Collections - .synchronizedMap(new HashMap<>()); - private List successfullyDiscovered = new LinkedList<>(); - private Queue<@Nullable LcnAddrMod> serialNumberRequestQueue = new ConcurrentLinkedQueue<>(); - private Queue<@Nullable LcnAddrMod> moduleNameRequestQueue = new ConcurrentLinkedQueue<>(); - private volatile ScheduledFuture queueProcessor = NullScheduledFuture.getInstance(); - private ScheduledFuture builderTask = NullScheduledFuture.getInstance(); + private final Map> moduleNames = new HashMap<>(); + private final Map discoveryResultBuilders = new ConcurrentHashMap<>(); + private final List successfullyDiscovered = new LinkedList<>(); + private final Queue<@Nullable LcnAddrMod> serialNumberRequestQueue = new ConcurrentLinkedQueue<>(); + private final Queue<@Nullable LcnAddrMod> moduleNameRequestQueue = new ConcurrentLinkedQueue<>(); + private @Nullable volatile ScheduledFuture queueProcessor; + private @Nullable ScheduledFuture builderTask; public LcnModuleDiscoveryService() { super(SUPPORTED_THING_TYPES_UIDS, DISCOVERY_TIMEOUT_SEC, false); @@ -100,7 +99,6 @@ public void deactivate() { stopScan(); } - @SuppressWarnings({ "unused", "null" }) @Override protected void startScan() { synchronized (this) { @@ -110,8 +108,9 @@ protected void startScan() { return; } - if (localBridgeHandler.getConnection() == null) { - builderTask.cancel(true); + ScheduledFuture localBuilderTask = builderTask; + if (localBridgeHandler.getConnection() == null && localBuilderTask != null) { + localBuilderTask.cancel(true); } localBridgeHandler.registerPckListener(data -> { @@ -141,8 +140,8 @@ protected void startScan() { rescheduleQueueProcessor(); // delay request of serial until all modules finished ACKing } - if (!moduleNames.containsKey(addr) - || moduleNames.get(addr).size() != MODULE_NAME_PART_COUNT) { + Map localNameParts = moduleNames.get(addr); + if (localNameParts == null || localNameParts.size() != MODULE_NAME_PART_COUNT) { moduleNameRequestQueue.add(addr); rescheduleQueueProcessor(); // delay request of names until all modules finished ACKing } @@ -184,21 +183,25 @@ protected void startScan() { builderTask = scheduler.scheduleWithFixedDelay(() -> { synchronized (LcnModuleDiscoveryService.this) { - discoveryResultBuilders.entrySet().stream().filter(e -> moduleNames.containsKey(e.getKey())) - .filter(e -> moduleNames.get(e.getKey()).size() == MODULE_NAME_PART_COUNT) - .filter(e -> !successfullyDiscovered.contains(e.getKey())).forEach(e -> { - StringBuilder thingName = new StringBuilder(); - if (e.getKey().getSegmentId() != 0) { - thingName.append("Segment " + e.getKey().getSegmentId() + " "); - } + discoveryResultBuilders.entrySet().stream().filter(e -> { + Map localNameParts = moduleNames.get(e.getKey()); + return localNameParts != null && localNameParts.size() == MODULE_NAME_PART_COUNT; + }).filter(e -> !successfullyDiscovered.contains(e.getKey())).forEach(e -> { + StringBuilder thingName = new StringBuilder(); + if (e.getKey().getSegmentId() != 0) { + thingName.append("Segment " + e.getKey().getSegmentId() + " "); + } - thingName.append("Module " + e.getKey().getModuleId() + ": "); - thingName.append(moduleNames.get(e.getKey()).get(0)); - thingName.append(moduleNames.get(e.getKey()).get(1)); + thingName.append("Module " + e.getKey().getModuleId() + ": "); + Map localNameParts = moduleNames.get(e.getKey()); + if (localNameParts != null) { + thingName.append(localNameParts.get(0)); + thingName.append(localNameParts.get(1)); - thingDiscovered(e.getValue().withLabel(thingName.toString()).build()); - successfullyDiscovered.add(e.getKey()); - }); + thingDiscovered(e.getValue().withLabel(thingName.toString()).build()); + successfullyDiscovered.add(e.getKey()); + } + }); } }, 500, 500, TimeUnit.MILLISECONDS); @@ -208,7 +211,10 @@ protected void startScan() { private synchronized void rescheduleQueueProcessor() { // delay serial number and module name requests to not clog the bus - queueProcessor.cancel(true); + ScheduledFuture localQueueProcessor = queueProcessor; + if (localQueueProcessor != null) { + localQueueProcessor.cancel(true); + } queueProcessor = scheduler.scheduleWithFixedDelay(() -> { PckGatewayHandler localBridgeHandler = bridgeHandler; if (localBridgeHandler != null) { @@ -232,8 +238,14 @@ private synchronized void rescheduleQueueProcessor() { @Override public synchronized void stopScan() { - builderTask.cancel(true); - queueProcessor.cancel(true); + ScheduledFuture localBuilderTask = builderTask; + if (localBuilderTask != null) { + localBuilderTask.cancel(true); + } + ScheduledFuture localQueueProcessor = queueProcessor; + if (localQueueProcessor != null) { + localQueueProcessor.cancel(true); + } PckGatewayHandler localBridgeHandler = bridgeHandler; if (localBridgeHandler != null) { localBridgeHandler.removeAllPckListeners(); 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 d4932699ddff3..56f2829e1d055 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 @@ -12,12 +12,10 @@ */ package org.openhab.binding.lcn.internal; -import java.lang.reflect.InvocationTargetException; -import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -51,17 +49,9 @@ import org.openhab.binding.lcn.internal.common.LcnException; import org.openhab.binding.lcn.internal.connection.Connection; import org.openhab.binding.lcn.internal.connection.ModInfo; -import org.openhab.binding.lcn.internal.converter.AbstractVariableValueConverter; -import org.openhab.binding.lcn.internal.converter.AngleConverter; -import org.openhab.binding.lcn.internal.converter.Co2Converter; -import org.openhab.binding.lcn.internal.converter.CurrentConverter; -import org.openhab.binding.lcn.internal.converter.EnergyConverter; -import org.openhab.binding.lcn.internal.converter.IdentityConverter; -import org.openhab.binding.lcn.internal.converter.LightConverter; -import org.openhab.binding.lcn.internal.converter.PowerConverter; -import org.openhab.binding.lcn.internal.converter.TemperatureConverter; -import org.openhab.binding.lcn.internal.converter.VoltageConverter; -import org.openhab.binding.lcn.internal.converter.WindspeedConverter; +import org.openhab.binding.lcn.internal.converter.Converter; +import org.openhab.binding.lcn.internal.converter.Converters; +import org.openhab.binding.lcn.internal.converter.S0Converter; import org.openhab.binding.lcn.internal.subhandler.AbstractLcnModuleSubHandler; import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaAckSubHandler; import org.openhab.binding.lcn.internal.subhandler.LcnModuleMetaFirmwareSubHandler; @@ -77,17 +67,24 @@ @NonNullByDefault public class LcnModuleHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(LcnModuleHandler.class); + private static final Map CONVERTERS = new HashMap<>(); private @Nullable LcnAddrMod moduleAddress; - private Map subHandlers; - private List metadataSubHandlers; - private Map converters; + private final Map subHandlers = new HashMap<>(); + private final List metadataSubHandlers = new ArrayList<>(); + private final Map converters = new HashMap<>(); + + static { + CONVERTERS.put("temperature", Converters.TEMPERATURE); + CONVERTERS.put("light", Converters.LIGHT); + CONVERTERS.put("co2", Converters.CO2); + CONVERTERS.put("current", Converters.CURRENT); + CONVERTERS.put("voltage", Converters.VOLTAGE); + CONVERTERS.put("angle", Converters.ANGLE); + CONVERTERS.put("windspeed", Converters.WINDSPEED); + } public LcnModuleHandler(Thing thing) { super(thing); - - subHandlers = Collections.synchronizedMap(new HashMap<>()); - metadataSubHandlers = Collections.synchronizedList(new LinkedList<>()); - converters = Collections.synchronizedMap(new HashMap<>()); } @Override @@ -99,10 +96,7 @@ public void initialize() { // create sub handlers ModInfo info = getPckGatewayHandler().getModInfo(localModuleAddress); for (LcnChannelGroup type : LcnChannelGroup.values()) { - AbstractLcnModuleSubHandler newHandler = type.getSubHandlerClass() - .getDeclaredConstructor(LcnModuleHandler.class, ModInfo.class).newInstance(this, info); - - subHandlers.put(type, newHandler); + subHandlers.put(type, type.createSubHandler(this, info)); } // meta sub handlers, which are not assigned to a channel group @@ -116,32 +110,14 @@ public void initialize() { if (unitObject instanceof String) { switch ((String) unitObject) { - case "temperature": - converters.put(channel.getUID(), new TemperatureConverter()); - break; - case "light": - converters.put(channel.getUID(), new LightConverter()); - break; - case "co2": - converters.put(channel.getUID(), new Co2Converter()); - break; case "power": - converters.put(channel.getUID(), new PowerConverter(parameterObject)); - break; case "energy": - converters.put(channel.getUID(), new EnergyConverter(parameterObject)); + converters.put(channel.getUID(), new S0Converter(parameterObject)); break; - case "current": - converters.put(channel.getUID(), new CurrentConverter()); - break; - case "voltage": - converters.put(channel.getUID(), new VoltageConverter()); - break; - case "angle": - converters.put(channel.getUID(), new AngleConverter()); - break; - case "windspeed": - converters.put(channel.getUID(), new WindspeedConverter()); + default: + if (CONVERTERS.containsKey(unitObject)) { + converters.put(channel.getUID(), CONVERTERS.get(unitObject)); + } break; } } @@ -149,10 +125,7 @@ public void initialize() { // module is assumed as online, when the corresponding Bridge (PckGatewayHandler) is online. updateStatus(ThingStatus.ONLINE); - } catch (LcnException | InstantiationException | IllegalAccessException | IllegalArgumentException - | InvocationTargetException | NoSuchMethodException | SecurityException e) { - logger.warn("Failed to initialize handler: {}: {}: {}", localModuleAddress, e.getClass().getSimpleName(), - e.getMessage()); + } catch (LcnException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); } } @@ -216,8 +189,8 @@ public void handleCommand(ChannelUID channelUid, Command command) { } @NonNullByDefault({}) // getOrDefault() - private AbstractVariableValueConverter getConverter(ChannelUID channelUid) { - return converters.getOrDefault(channelUid, IdentityConverter.getInstance()); + private Converter getConverter(ChannelUID channelUid) { + return converters.getOrDefault(channelUid, Converters.IDENTITY); } /** @@ -244,17 +217,13 @@ private T castCommand(Command command) throws LcnException { */ @SuppressWarnings("null") public void handleStatusMessage(String pck) { - synchronized (subHandlers) { - for (AbstractLcnModuleSubHandler handler : subHandlers.values()) { - if (handler.tryParse(pck)) { - break; - } + for (AbstractLcnModuleSubHandler handler : subHandlers.values()) { + if (handler.tryParse(pck)) { + break; } } - synchronized (metadataSubHandlers) { - metadataSubHandlers.forEach(h -> h.tryParse(pck)); - } + metadataSubHandlers.forEach(h -> h.tryParse(pck)); } private Optional channelUidToChannelNumber(ChannelUID channelUid, LcnChannelGroup channelGroup) @@ -300,7 +269,7 @@ public void sendPck(String command) throws LcnException { * @param command without the address part * @throws LcnException when the module address is unknown */ - public void sendPck(ByteBuffer command) throws LcnException { + public void sendPck(byte[] command) throws LcnException { getPckGatewayHandler().queue(getCommandAddress(), true, command); } @@ -327,7 +296,7 @@ protected LcnAddr getCommandAddress() throws LcnException, LcnException { */ public void updateChannel(LcnChannelGroup channelGroup, String channelId, State state) { ChannelUID channelUid = createChannelUid(channelGroup, channelId); - AbstractVariableValueConverter converter = converters.get(channelUid); + Converter converter = converters.get(channelUid); State convertedState = state; if (converter != null) { diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java index 538eb2ea27c7a..5f63d8a36d224 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal; -import java.nio.ByteBuffer; import java.util.Collection; import java.util.Collections; import java.util.Optional; @@ -69,12 +68,13 @@ public synchronized void initialize() { try { OutputPortDimMode dimMode; - if (LcnDefs.OutputPortDimMode.NATIVE50.name().equalsIgnoreCase(config.getMode())) { + String mode = config.getMode(); + if (LcnDefs.OutputPortDimMode.NATIVE50.name().equalsIgnoreCase(mode)) { dimMode = LcnDefs.OutputPortDimMode.NATIVE50; - } else if (LcnDefs.OutputPortDimMode.NATIVE200.name().equalsIgnoreCase(config.getMode())) { + } else if (LcnDefs.OutputPortDimMode.NATIVE200.name().equalsIgnoreCase(mode)) { dimMode = LcnDefs.OutputPortDimMode.NATIVE200; } else { - throw new LcnException("DimMode " + config.getMode() + " is not supported"); + throw new LcnException("DimMode " + mode + " is not supported"); } ConnectionSettings settings = new ConnectionSettings("0", config.getHostname(), config.getPort(), @@ -164,12 +164,12 @@ public void queue(LcnAddr addr, boolean wantsAck, String pck) { * @param wantsAck true, if the module shall send an ACK upon successful processing * @param pck the command to send */ - public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer pck) { + public void queue(LcnAddr addr, boolean wantsAck, byte[] pck) { Connection localConnection = connection; if (localConnection != null) { localConnection.queue(addr, wantsAck, pck); } else { - logger.warn("Dropped PCK command of length: {}", pck.capacity()); + logger.warn("Dropped PCK command of length: {}", pck.length); } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/DimmerOutputCommand.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/DimmerOutputCommand.java index cd2a74b79f319..b54dd12367647 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/DimmerOutputCommand.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/DimmerOutputCommand.java @@ -25,9 +25,9 @@ @NonNullByDefault public class DimmerOutputCommand extends PercentType { private static final long serialVersionUID = 8147502412107723798L; - private boolean controlAllOutputs; - private boolean controlOutputs12; - private int rampMs; + private final boolean controlAllOutputs; + private final boolean controlOutputs12; + private final int rampMs; public DimmerOutputCommand(BigDecimal value, boolean controlAllOutputs, boolean controlOutputs12, int rampMs) { super(value); 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 34807dd055371..d9d6a6da4f38c 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 @@ -12,7 +12,11 @@ */ package org.openhab.binding.lcn.internal.common; +import java.util.function.BiFunction; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.lcn.internal.LcnModuleHandler; +import org.openhab.binding.lcn.internal.connection.ModInfo; import org.openhab.binding.lcn.internal.subhandler.AbstractLcnModuleSubHandler; import org.openhab.binding.lcn.internal.subhandler.LcnModuleBinarySensorSubHandler; import org.openhab.binding.lcn.internal.subhandler.LcnModuleCodeSubHandler; @@ -36,33 +40,34 @@ */ @NonNullByDefault public enum LcnChannelGroup { - OUTPUT(4, LcnModuleOutputSubHandler.class), - ROLLERSHUTTEROUTPUT(1, LcnModuleRollershutterOutputSubHandler.class), - RELAY(8, LcnModuleRelaySubHandler.class), - ROLLERSHUTTERRELAY(4, LcnModuleRollershutterRelaySubHandler.class), - LED(12, LcnModuleLedSubHandler.class), - LOGIC(4, LcnModuleLogicSubHandler.class), - BINARYSENSOR(8, LcnModuleBinarySensorSubHandler.class), - VARIABLE(12, LcnModuleVariableSubHandler.class), - RVARSETPOINT(2, LcnModuleRvarSetpointSubHandler.class), - RVARLOCK(2, LcnModuleRvarLockSubHandler.class), - THRESHOLDREGISTER1(5, LcnModuleThresholdSubHandler.class), - THRESHOLDREGISTER2(4, LcnModuleThresholdSubHandler.class), - THRESHOLDREGISTER3(4, LcnModuleThresholdSubHandler.class), - THRESHOLDREGISTER4(4, LcnModuleThresholdSubHandler.class), - S0INPUT(4, LcnModuleS0CounterSubHandler.class), - KEYLOCKTABLEA(8, LcnModuleKeyLockTableSubHandler.class), - KEYLOCKTABLEB(8, LcnModuleKeyLockTableSubHandler.class), - KEYLOCKTABLEC(8, LcnModuleKeyLockTableSubHandler.class), - KEYLOCKTABLED(8, LcnModuleKeyLockTableSubHandler.class), - CODE(0, LcnModuleCodeSubHandler.class); + OUTPUT(4, LcnModuleOutputSubHandler::new), + ROLLERSHUTTEROUTPUT(1, LcnModuleRollershutterOutputSubHandler::new), + RELAY(8, LcnModuleRelaySubHandler::new), + ROLLERSHUTTERRELAY(4, LcnModuleRollershutterRelaySubHandler::new), + LED(12, LcnModuleLedSubHandler::new), + LOGIC(4, LcnModuleLogicSubHandler::new), + BINARYSENSOR(8, LcnModuleBinarySensorSubHandler::new), + VARIABLE(12, LcnModuleVariableSubHandler::new), + RVARSETPOINT(2, LcnModuleRvarSetpointSubHandler::new), + RVARLOCK(2, LcnModuleRvarLockSubHandler::new), + THRESHOLDREGISTER1(5, LcnModuleThresholdSubHandler::new), + THRESHOLDREGISTER2(4, LcnModuleThresholdSubHandler::new), + THRESHOLDREGISTER3(4, LcnModuleThresholdSubHandler::new), + THRESHOLDREGISTER4(4, LcnModuleThresholdSubHandler::new), + S0INPUT(4, LcnModuleS0CounterSubHandler::new), + KEYLOCKTABLEA(8, LcnModuleKeyLockTableSubHandler::new), + KEYLOCKTABLEB(8, LcnModuleKeyLockTableSubHandler::new), + KEYLOCKTABLEC(8, LcnModuleKeyLockTableSubHandler::new), + KEYLOCKTABLED(8, LcnModuleKeyLockTableSubHandler::new), + CODE(0, LcnModuleCodeSubHandler::new); private int count; - private Class subHandlerClass; + private BiFunction handlerFactory; - private LcnChannelGroup(int count, Class subHandlerClass) { + private LcnChannelGroup(int count, + BiFunction handlerFactory) { this.count = count; - this.subHandlerClass = subHandlerClass; + this.handlerFactory = handlerFactory; } /** @@ -89,8 +94,8 @@ public boolean isValidId(int number) { * * @return the sub handler class */ - public Class getSubHandlerClass() { - return subHandlerClass; + public AbstractLcnModuleSubHandler createSubHandler(LcnModuleHandler handler, ModInfo info) { + return handlerFactory.apply(handler, info); } /** diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/NullScheduledFuture.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/NullScheduledFuture.java deleted file mode 100644 index 2f18ac19c359e..0000000000000 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/NullScheduledFuture.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * 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.common; - -import java.util.concurrent.Delayed; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * Empty ScheduledFuture, used for initialization. - * - * @author Fabian Wolter - Initial Contribution - */ -@NonNullByDefault -public class NullScheduledFuture implements ScheduledFuture { - @NonNullByDefault({}) - private static class LazyHolder { - static final NullScheduledFuture INSTANCE = new NullScheduledFuture(); - } - - private NullScheduledFuture() { - // nothing - } - - /** Gets the instance of this singleton. */ - public static NullScheduledFuture getInstance() { - return LazyHolder.INSTANCE; - } - - @Override - public long getDelay(@Nullable TimeUnit unit) { - return 0; - } - - @Override - public int compareTo(@Nullable Delayed o) { - return 0; - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - return true; - } - - @Override - public boolean isCancelled() { - return true; - } - - @Override - public boolean isDone() { - return true; - } - - @Override - public Object get() throws InterruptedException, ExecutionException { - return new Object(); - } - - @Override - public Object get(long timeout, @Nullable TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return new Object(); - } -} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/PckGenerator.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/PckGenerator.java index 0fcbcfea8d841..5c76e562639e3 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/PckGenerator.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/PckGenerator.java @@ -310,26 +310,26 @@ public static String controlRelays(LcnDefs.RelayStateModifier[] states) throws L if (states.length != 8) { throw new LcnException(); } - String ret = "R8"; + StringBuilder ret = new StringBuilder("R8"); for (int i = 0; i < 8; ++i) { switch (states[i]) { case ON: - ret += "1"; + ret.append("1"); break; case OFF: - ret += "0"; + ret.append("0"); break; case TOGGLE: - ret += "U"; + ret.append("U"); break; case NOCHANGE: - ret += "-"; + ret.append("-"); break; default: throw new LcnException(); } } - return ret; + return ret.toString(); } /** @@ -502,29 +502,29 @@ public static String controlLed(int ledId, LcnDefs.LedStatus state) throws LcnEx * @param cmds the 4 concrete commands to send for the tables (A-D) * @param keys the tables' 8 key-states (true means "send") * @return the PCK command (without address header) as text - * @throws IllegalArgumentException if out of range + * @throws LcnException if out of range */ public static String sendKeys(LcnDefs.SendKeyCommand[] cmds, boolean[] keys) throws LcnException { if (cmds.length != 4 || keys.length != 8) { throw new LcnException(); } - String ret = "TS"; + StringBuilder ret = new StringBuilder("TS"); for (int i = 0; i < 4; ++i) { switch (cmds[i]) { case HIT: - ret += "K"; + ret.append("K"); break; case MAKE: - ret += "L"; + ret.append("L"); break; case BREAK: - ret += "O"; + ret.append("O"); break; case DONTSEND: // By skipping table D (if it is not used), we use the old command // for table A-C which is compatible with older LCN modules if (i < 3) { - ret += "-"; + ret.append("-"); } break; default: @@ -532,9 +532,9 @@ public static String sendKeys(LcnDefs.SendKeyCommand[] cmds, boolean[] keys) thr } } for (int i = 0; i < 8; ++i) { - ret += keys[i] ? "1" : "0"; + ret.append(keys[i] ? "1" : "0"); } - return ret; + return ret.toString(); } /** @@ -550,58 +550,58 @@ public static String sendKeys(LcnDefs.SendKeyCommand[] cmds, boolean[] keys) thr public static String sendKeysHitDefered(int tableId, int time, LcnDefs.TimeUnit timeUnit, boolean[] keys) throws LcnException { if (tableId < 0 || tableId > 3 || keys.length != 8) { - throw new IllegalArgumentException(); + throw new LcnException(); } - String ret = "TV"; + StringBuilder ret = new StringBuilder("TV"); switch (tableId) { case 0: - ret += "A"; + ret.append("A"); break; case 1: - ret += "B"; + ret.append("B"); break; case 2: - ret += "C"; + ret.append("C"); break; case 3: - ret += "D"; + ret.append("D"); break; default: throw new LcnException(); } - ret += String.format("%03d", time); + ret.append(String.format("%03d", time)); switch (timeUnit) { case SECONDS: if (time < 1 || time > 60) { throw new LcnException(); } - ret += "S"; + ret.append("S"); break; case MINUTES: if (time < 1 || time > 90) { throw new LcnException(); } - ret += "M"; + ret.append("M"); break; case HOURS: if (time < 1 || time > 50) { throw new LcnException(); } - ret += "H"; + ret.append("H"); break; case DAYS: if (time < 1 || time > 45) { throw new LcnException(); } - ret += "D"; + ret.append("D"); break; default: throw new LcnException(); } for (int i = 0; i < 8; ++i) { - ret += keys[i] ? "1" : "0"; + ret.append(keys[i] ? "1" : "0"); } - return ret; + return ret.toString(); } /** @@ -626,26 +626,27 @@ public static String lockKeys(int tableId, LcnDefs.KeyLockStateModifier[] states if (tableId < 0 || tableId > 3 || states.length != 8) { throw new LcnException(); } - String ret = String.format("TX%s", tableId == 0 ? "A" : tableId == 1 ? "B" : tableId == 2 ? "C" : "D"); + StringBuilder ret = new StringBuilder( + String.format("TX%s", tableId == 0 ? "A" : tableId == 1 ? "B" : tableId == 2 ? "C" : "D")); for (int i = 0; i < 8; ++i) { switch (states[i]) { case ON: - ret += "1"; + ret.append("1"); break; case OFF: - ret += "0"; + ret.append("0"); break; case TOGGLE: - ret += "U"; + ret.append("U"); break; case NOCHANGE: - ret += "-"; + ret.append("-"); break; default: throw new LcnException(); } } - return ret; + return ret.toString(); } /** @@ -660,41 +661,41 @@ public static String lockKeys(int tableId, LcnDefs.KeyLockStateModifier[] states */ public static String lockKeyTabATemporary(int time, LcnDefs.TimeUnit timeUnit, boolean[] keys) throws LcnException { if (keys.length != 8) { - throw new IllegalArgumentException(); + throw new LcnException(); } - String ret = String.format("TXZA%03d", time); + StringBuilder ret = new StringBuilder(String.format("TXZA%03d", time)); switch (timeUnit) { case SECONDS: if (time < 1 || time > 60) { throw new LcnException(); } - ret += "S"; + ret.append("S"); break; case MINUTES: if (time < 1 || time > 90) { throw new LcnException(); } - ret += "M"; + ret.append("M"); break; case HOURS: if (time < 1 || time > 50) { throw new LcnException(); } - ret += "H"; + ret.append("H"); break; case DAYS: if (time < 1 || time > 45) { throw new LcnException(); } - ret += "D"; + ret.append("D"); break; default: throw new LcnException(); } for (int i = 0; i < 8; ++i) { - ret += keys[i] ? "1" : "0"; + ret.append(keys[i] ? "1" : "0"); } - return ret; + return ret.toString(); } /** @@ -721,7 +722,7 @@ public static String dynTextHeader(int row, int part) throws LcnException { * @param regId 0..1 * @param state the lock state * @return the PCK command (without address header) as text - * @throws IllegalArgumentException if out of range + * @throws LcnException if out of range */ public static String lockRegulator(int regId, boolean state) throws LcnException { if (regId < 0 || regId > 1) { @@ -769,7 +770,7 @@ private static int timeToRampValue(int timeMSec) { ret = 9; } else { ret = (timeMSec / 1000 - 6) / 2 + 10; - if (ret >= 250) { + if (ret > 250) { ret = 250; LOGGER.warn("Ramp value is too high. Limiting value to 486s."); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java index 51d43435d02ce..334392976c028 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java @@ -65,10 +65,10 @@ public enum Variable { S0INPUT3(2, Type.S0INPUT, LcnChannelGroup.S0INPUT), S0INPUT4(3, Type.S0INPUT, LcnChannelGroup.S0INPUT); // LCN-BU4L - private int number; + private final int number; private Optional thresholdNumber = Optional.empty(); - private Type type; - private LcnChannelGroup channelGroup; + private final Type type; + private final LcnChannelGroup channelGroup; /** * Defines the origin of an LCN variable. @@ -193,7 +193,7 @@ public static Variable s0IdToVar(int number) throws LcnException { private static Variable getVariableFromNumberAndType(int varId, Type type, Predicate filter) throws LcnException { return Stream.of(values()).filter(v -> v.type == type).filter(v -> v.number == varId).filter(filter).findAny() - .orElseThrow(() -> new LcnException()); + .orElseThrow(LcnException::new); } /** diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/VariableValue.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/VariableValue.java index ecced343c134b..90c6bf83c8cb6 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/VariableValue.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/VariableValue.java @@ -33,7 +33,7 @@ public class VariableValue { private static final String SENSOR_DEFECTIVE_STATE = "DEFECTIVE"; /** The absolute, native LCN value. */ - private long nativeValue; + private final long nativeValue; /** * Constructor with native LCN value. diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java index ddad2b1ca40bd..a5759e668eba7 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java @@ -13,7 +13,6 @@ package org.openhab.binding.lcn.internal.connection; import java.io.IOException; -import java.nio.ByteBuffer; import java.nio.channels.Channel; import java.util.concurrent.ScheduledExecutorService; @@ -29,9 +28,9 @@ @NonNullByDefault public abstract class AbstractConnectionState extends AbstractState { /** The PCK gateway's Connection */ - protected Connection connection; + protected final Connection connection; /** An openHAB scheduler */ - protected ScheduledExecutorService scheduler; + protected final ScheduledExecutorService scheduler; public AbstractConnectionState(StateContext context, ScheduledExecutorService scheduler) { super(context); @@ -55,7 +54,7 @@ public AbstractConnectionState(StateContext context, ScheduledExecutorService sc * @param wantsAck true, if the module shall respond with an Ack upon successful processing * @param data the PCK message to be sent */ - public abstract void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data); + public abstract void queue(LcnAddr addr, boolean wantsAck, byte[] data); /** * Shuts the Connection down finally. A shut-down connection cannot re-used. diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java index 595662e4f262e..fe594d055cb67 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.nio.ByteBuffer; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -50,7 +49,7 @@ protected void startTimeoutTimer() { } @Override - public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { connection.queueOffline(addr, wantsAck, data); } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java index befad75cb2ffe..591b3505582a5 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java @@ -26,8 +26,8 @@ */ @NonNullByDefault public abstract class AbstractState { - private List> usedTimers = Collections.synchronizedList(new ArrayList<>()); - protected StateContext context; + private final List> usedTimers = Collections.synchronizedList(new ArrayList<>()); + protected final StateContext context; public AbstractState(StateContext context) { this.context = context; diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java index cb14bdf261277..db59821c68852 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.lcn.internal.connection; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.BufferOverflowException; @@ -21,10 +22,13 @@ import java.nio.channels.CompletionHandler; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Queue; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; @@ -56,8 +60,6 @@ @NonNullByDefault public class Connection { private final Logger logger = LoggerFactory.getLogger(Connection.class); - /** Max. lengths of a PCK string including address and line feed. Currently dynamic text (GTDT) */ - private static final int MAX_PCK_STRING_LENGTH = 34; private static final int BROADCAST_MODULE_ID = 3; private static final int BROADCAST_SEGMENT_ID = 3; private final ConnectionSettings settings; @@ -67,13 +69,13 @@ public class Connection { /** The local segment id. -1 means "unknown". */ private int localSegId; private final ByteBuffer readBuffer = ByteBuffer.allocate(1024); - private final ByteBuffer sendBuffer = ByteBuffer.allocate(MAX_PCK_STRING_LENGTH); + private final ByteArrayOutputStream sendBuffer = new ByteArrayOutputStream(); private final Queue<@Nullable SendData> sendQueue = new LinkedBlockingQueue<>(); - private final Queue<@Nullable PckQueueItem> offlineSendQueue = new LinkedBlockingQueue<>(); - private final Map modData = Collections.synchronizedMap(new HashMap<>()); + private final BlockingQueue offlineSendQueue = new LinkedBlockingQueue<>(); + private final Map modData = Collections.synchronizedMap(new HashMap<>()); private volatile boolean writeInProgress; - private ScheduledExecutorService scheduler; - private StateMachine stateMachine; + private final ScheduledExecutorService scheduler; + private final StateMachine stateMachine; /** * Constructs a clean (disconnected) connection with the given settings. @@ -99,7 +101,7 @@ void clearRuntimeData() { this.localSegId = -1; this.readBuffer.clear(); this.sendQueue.clear(); - this.sendBuffer.clear(); + this.sendBuffer.reset(); } /** @@ -136,9 +138,10 @@ public void setLocalSegId(int localSegId) { * @param code the LCN internal code (-1 = "positive") */ public void onAck(LcnAddrMod addr, int code) { - ModInfo info = this.modData.get(addr); - if (info != null) { - info.onAck(code, this, this.settings.getTimeout(), System.nanoTime()); + synchronized (modData) { + if (modData.containsKey(addr)) { + modData.get(addr).onAck(code, this, this.settings.getTimeout(), System.nanoTime()); + } } } @@ -146,17 +149,10 @@ public void onAck(LcnAddrMod addr, int code) { * Creates and/or returns cached data for the given LCN module. * * @param addr the module's address - * @return the data (never null) + * @return the data */ public ModInfo updateModuleData(LcnAddrMod addr) { - synchronized (modData) { - ModInfo data = this.modData.get(addr); - if (data == null) { - data = new ModInfo(addr); - this.modData.put(addr, data); - } - return data; - } + return modData.computeIfAbsent(addr, ModInfo::new); } /** @@ -232,7 +228,7 @@ public synchronized void triggerWriteToSocket() { if (localChannel == null || !isSocketConnected() || writeInProgress) { return; } - sendBuffer.clear(); + sendBuffer.reset(); SendData item = sendQueue.poll(); if (item != null) { @@ -242,46 +238,47 @@ public synchronized void triggerWriteToSocket() { } writeInProgress = true; - sendBuffer.flip(); - localChannel.write(sendBuffer, null, new CompletionHandler<@Nullable Integer, @Nullable Void>() { - @Override - public void completed(@Nullable Integer result, @Nullable Void attachment) { - synchronized (Connection.this) { - if (result != sendBuffer.limit()) { - logger.warn("Data loss while writing to channel: {}", settings.getAddress()); - } else { - if (logger.isTraceEnabled()) { - logger.trace("Sent: {}", new String(sendBuffer.array(), 0, sendBuffer.limit())); - } - } + byte[] data = sendBuffer.toByteArray(); + localChannel.write(ByteBuffer.wrap(data), null, + new CompletionHandler<@Nullable Integer, @Nullable Void>() { + @Override + public void completed(@Nullable Integer result, @Nullable Void attachment) { + synchronized (Connection.this) { + if (result != data.length) { + logger.warn("Data loss while writing to channel: {}", settings.getAddress()); + } else { + if (logger.isTraceEnabled()) { + logger.trace("Sent: {}", new String(data, 0, data.length)); + } + } - writeInProgress = false; + writeInProgress = false; - if (sendQueue.size() > 0) { - /** - * This could lead to stack overflows, since the CompletionHandler may run in the same - * Thread as triggerWriteToSocket() is invoked (see - * {@link AsynchronousChannelGroup}/Threading), but we do not expect as much data - * in one chunk here, that the stack can be filled in a critical way. - */ - triggerWriteToSocket(); + if (sendQueue.size() > 0) { + /** + * This could lead to stack overflows, since the CompletionHandler may run in + * the same Thread as triggerWriteToSocket() is invoked (see + * {@link AsynchronousChannelGroup}/Threading), but we do not expect as much + * data in one chunk here, that the stack can be filled in a critical way. + */ + triggerWriteToSocket(); + } + } } - } - } - @Override - public void failed(@Nullable Throwable exc, @Nullable Void attachment) { - synchronized (Connection.this) { - if (exc != null) { - logger.warn("Writing to channel \"{}\" failed: {}", settings.getAddress(), - exc.getMessage()); + @Override + public void failed(@Nullable Throwable exc, @Nullable Void attachment) { + synchronized (Connection.this) { + if (exc != null) { + logger.warn("Writing to channel \"{}\" failed: {}", settings.getAddress(), + exc.getMessage()); + } + writeInProgress = false; + stateMachine.handleConnectionFailed(new LcnException("write() failed")); + } } - writeInProgress = false; - stateMachine.handleConnectionFailed(new LcnException("write() failed")); - } - } - }); - } catch (UnsupportedEncodingException | BufferOverflowException e) { + }); + } catch (BufferOverflowException | IOException e) { logger.warn("Sending failed: {}: {}: {}", item, e.getClass().getSimpleName(), e.getMessage()); } } @@ -306,7 +303,7 @@ public void queueDirectlyPlainText(String plainText) { */ void queueDirectly(LcnAddr addr, boolean wantsAck, String pck) { try { - this.queueDirectly(addr, wantsAck, ByteBuffer.wrap(pck.getBytes(LcnDefs.LCN_ENCODING))); + this.queueDirectly(addr, wantsAck, pck.getBytes(LcnDefs.LCN_ENCODING)); } catch (UnsupportedEncodingException ex) { logger.error("Failed to encode PCK command: {}", pck); } @@ -321,7 +318,7 @@ void queueDirectly(LcnAddr addr, boolean wantsAck, String pck) { * @param wantsAck true to wait for acknowledge on receipt (should be false for group addresses) * @param data the pure PCK command (without address header) */ - void queueDirectly(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + void queueDirectly(LcnAddr addr, boolean wantsAck, byte[] data) { if (!addr.isGroup() && wantsAck) { this.updateModuleData((LcnAddrMod) addr).queuePckCommandWithAck(data, this, this.settings.getTimeout(), System.nanoTime()); @@ -350,7 +347,7 @@ synchronized void queueAndSend(SendData data) { * @param wantsAck true, if the LCN module shall respond with an Ack on successful processing * @param data the pure PCK command (without address header) */ - void queueOffline(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + void queueOffline(LcnAddr addr, boolean wantsAck, byte[] data) { offlineSendQueue.add(new PckQueueItem(addr, wantsAck, data)); } @@ -365,7 +362,7 @@ void queueOffline(LcnAddr addr, boolean wantsAck, ByteBuffer data) { */ public void queue(LcnAddr addr, boolean wantsAck, String pck) { try { - this.queue(addr, wantsAck, ByteBuffer.wrap(pck.getBytes(LcnDefs.LCN_ENCODING))); + this.queue(addr, wantsAck, pck.getBytes(LcnDefs.LCN_ENCODING)); } catch (UnsupportedEncodingException ex) { logger.warn("Failed to encode PCK command: {}", pck); } @@ -380,7 +377,7 @@ public void queue(LcnAddr addr, boolean wantsAck, String pck) { * @param wantsAck true, if the LCN module shall respond with an Ack on successful processing * @param pck the pure PCK command (without address header) */ - public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer pck) { + public void queue(LcnAddr addr, boolean wantsAck, byte[] pck) { stateMachine.queue(addr, wantsAck, pck); } @@ -388,20 +385,16 @@ public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer pck) { * Process the offline PCK command queue. Does only send recently enqueued PCK commands, the rest is discarded. */ void sendOfflineQueue() { - // don't use forEach(), because elements can be added during iteration - while (!offlineSendQueue.isEmpty()) { - PckQueueItem item = offlineSendQueue.poll(); - - if (item == null) { - break; - } + List allItems = new ArrayList<>(offlineSendQueue.size()); + offlineSendQueue.drainTo(allItems); + allItems.forEach(item -> { // only send messages that were enqueued recently, discard older messages long timeout = settings.getTimeout(); if (item.getEnqueued().isAfter(Instant.now().minus(timeout * 4, ChronoUnit.MILLIS))) { queueDirectly(item.getAddr(), item.isWantsAck(), item.getData()); } - } + }); } /** @@ -446,11 +439,7 @@ public int getLocalSegId() { */ public void updateModInfos() { synchronized (modData) { - for (ModInfo info : modData.values()) { - if (info != null) { - info.update(this, settings.getTimeout(), System.nanoTime()); - } - } + modData.values().forEach(i -> i.update(this, settings.getTimeout(), System.nanoTime())); } } @@ -476,9 +465,9 @@ public void shutdown() { public void sendModuleDiscoveryCommand() { try { queueAndSend(new SendDataPck(new LcnAddrGrp(BROADCAST_SEGMENT_ID, BROADCAST_MODULE_ID), true, - ByteBuffer.wrap(PckGenerator.nullCommand().getBytes(LcnDefs.LCN_ENCODING)))); + PckGenerator.nullCommand().getBytes(LcnDefs.LCN_ENCODING))); queueAndSend(new SendDataPck(new LcnAddrGrp(0, BROADCAST_MODULE_ID), true, - ByteBuffer.wrap(PckGenerator.nullCommand().getBytes(LcnDefs.LCN_ENCODING)))); + PckGenerator.nullCommand().getBytes(LcnDefs.LCN_ENCODING))); } catch (UnsupportedEncodingException e) { logger.warn("Could not send discovery request: {}", e.getMessage()); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnected.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnected.java index aa0072e7840fa..b54c58bc34da6 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnected.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnected.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.nio.ByteBuffer; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -49,7 +48,7 @@ public void startWorking() { } @Override - public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { connection.queueDirectly(addr, wantsAck, data); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java index 55221bd793f8a..f3886f411ddf9 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java @@ -15,7 +15,6 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.net.StandardSocketOptions; -import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.concurrent.ScheduledExecutorService; @@ -94,7 +93,7 @@ private void handleConnectionFailure(@Nullable Throwable e) { } @Override - public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { connection.queueOffline(addr, wantsAck, data); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java index b8189be5a2d68..ecc5074fbe4b1 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.nio.ByteBuffer; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -42,7 +41,7 @@ public void startWorking() { } @Override - public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { connection.queueOffline(addr, wantsAck, data); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java index 93ce70b951fbe..b9c15ec91a467 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.nio.ByteBuffer; import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -35,7 +34,7 @@ public void startWorking() { } @Override - public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { connection.queueOffline(addr, wantsAck, data); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java index d1b8113d702ba..b886bb66dde8a 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.nio.ByteBuffer; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; @@ -68,7 +67,7 @@ private void update() { } @Override - public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { connection.queueOffline(addr, wantsAck, data); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java index 1c0e7b649b672..555858f307ec7 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.nio.ByteBuffer; import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -39,7 +38,7 @@ public void startWorking() { } @Override - public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { connection.queueOffline(addr, wantsAck, data); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateShutdown.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateShutdown.java index 7cba533d4a8ec..d00ce0bd12189 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateShutdown.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateShutdown.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.nio.ByteBuffer; import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -37,7 +36,7 @@ public void startWorking() { } @Override - public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { // nothing } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java index 820dc7a52e049..7ce5abb094ed6 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java @@ -12,16 +12,15 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.nio.ByteBuffer; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.lcn.internal.common.LcnAddr; import org.openhab.binding.lcn.internal.common.LcnDefs; import org.openhab.binding.lcn.internal.common.LcnException; -import org.openhab.binding.lcn.internal.common.NullScheduledFuture; /** * This state waits for the status answer of the LCN-PCK gateway after connection establishment, rather the LCN bus is @@ -31,7 +30,7 @@ */ @NonNullByDefault public class ConnectionStateWaitForLcnBusConnected extends AbstractConnectionState { - private ScheduledFuture legacyTimer = NullScheduledFuture.getInstance(); + private @Nullable ScheduledFuture legacyTimer; public ConnectionStateWaitForLcnBusConnected(StateContext context, ScheduledExecutorService scheduler) { super(context, scheduler); @@ -42,24 +41,30 @@ public void startWorking() { // Legacy support for LCN-PCHK 2.2 and earlier: // There was no explicit "LCN connected" notification after successful authentication. // Only "LCN disconnected" would be reported immediately. That means "LCN connected" used to be the default. - addTimer(legacyTimer = scheduler.schedule(() -> { + ScheduledFuture localLegacyTimer = legacyTimer = scheduler.schedule(() -> { connection.getCallback().onOnline(); nextState(ConnectionStateSendDimMode.class); - }, connection.getSettings().getTimeout(), TimeUnit.MILLISECONDS)); + }, connection.getSettings().getTimeout(), TimeUnit.MILLISECONDS); + addTimer(localLegacyTimer); } @Override - public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { connection.queueOffline(addr, wantsAck, data); } @Override public void onPckMessageReceived(String data) { + ScheduledFuture localLegacyTimer = legacyTimer; if (data.equals(LcnDefs.LCNCONNSTATE_DISCONNECTED)) { - legacyTimer.cancel(true); + if (localLegacyTimer != null) { + localLegacyTimer.cancel(true); + } connection.getCallback().onOffline("LCN bus not connected to LCN-PCHK/PKE"); } else if (data.equals(LcnDefs.LCNCONNSTATE_CONNECTED)) { - legacyTimer.cancel(true); + if (localLegacyTimer != null) { + localLegacyTimer.cancel(true); + } connection.getCallback().onOnline(); nextState(ConnectionStateSendDimMode.class); } else if (data.equals(LcnDefs.INSUFFICIENT_LICENSES)) { diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java index c5c446d48891b..4a04ef9aa8bb9 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java @@ -13,17 +13,17 @@ package org.openhab.binding.lcn.internal.connection; import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.LinkedList; +import java.util.Arrays; import java.util.Map; import java.util.Optional; +import java.util.Queue; import java.util.TreeMap; +import java.util.concurrent.ConcurrentLinkedQueue; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.lcn.internal.LcnBindingConstants; -import org.openhab.binding.lcn.internal.common.LcnAddrMod; +import org.openhab.binding.lcn.internal.common.LcnAddr; import org.openhab.binding.lcn.internal.common.LcnChannelGroup; import org.openhab.binding.lcn.internal.common.LcnDefs; import org.openhab.binding.lcn.internal.common.LcnException; @@ -60,7 +60,7 @@ public class ModInfo { private static final int STATUS_REQUEST_DELAY_AFTER_COMMAND_MSEC = 2000; /** The LCN module's address. */ - private final LcnAddrMod addr; + private final LcnAddr addr; /** Firmware date of the LCN module. -1 means "unknown". */ private int firmwareVersion = -1; @@ -69,7 +69,7 @@ public class ModInfo { private final RequestStatus requestFirmwareVersion = new RequestStatus(-1, NUM_TRIES, "Firmware Version"); /** Output-port request status (0..3). */ - private final ArrayList requestStatusOutputs = new ArrayList<>(); + private final RequestStatus[] requestStatusOutputs = new RequestStatus[LcnChannelGroup.OUTPUT.getCount()]; /** Relays request status (all 8). */ private final RequestStatus requestStatusRelays = new RequestStatus(MAX_STATUS_EVENTBASED_VALUEAGE_MSEC, NUM_TRIES, @@ -109,7 +109,7 @@ public class ModInfo { * Commands are always without address header. * Note that the first one might currently be "in progress". */ - private final LinkedList<@Nullable ByteBuffer> pckCommandsWithAck = new LinkedList<>(); + private final Queue pckCommandsWithAck = new ConcurrentLinkedQueue<>(); /** Status data for the currently processed {@link PckCommandWithAck}. */ private final RequestStatus requestCurrentPckCommandWithAck = new RequestStatus(-1, NUM_TRIES, "Commands with Ack"); @@ -119,11 +119,11 @@ public class ModInfo { * * @param addr the module's address */ - public ModInfo(LcnAddrMod addr) { + public ModInfo(LcnAddr addr) { this.addr = addr; for (int i = 0; i < LcnChannelGroup.OUTPUT.getCount(); ++i) { - this.requestStatusOutputs - .add(new RequestStatus(MAX_STATUS_EVENTBASED_VALUEAGE_MSEC, NUM_TRIES, "Output " + (i + 1))); + requestStatusOutputs[i] = new RequestStatus(MAX_STATUS_EVENTBASED_VALUEAGE_MSEC, NUM_TRIES, + "Output " + (i + 1)); } for (Variable var : Variable.values()) { @@ -161,7 +161,7 @@ public void setLastRequestedVarWithoutTypeInResponse(Variable var) { * @param timeoutMSec the time to wait for a response before retrying a request * @param currTime the current time stamp */ - public void queuePckCommandWithAck(ByteBuffer data, Connection conn, long timeoutMSec, long currTime) { + public void queuePckCommandWithAck(byte[] data, Connection conn, long timeoutMSec, long currTime) { this.pckCommandsWithAck.add(data); // Try to process the new acknowledged command. Will do nothing if another one is still in progress. this.tryProcessNextCommandWithAck(conn, timeoutMSec, currTime); @@ -176,7 +176,7 @@ public void queuePckCommandWithAck(ByteBuffer data, Connection conn, long timeou */ public void onAck(int code, Connection conn, long timeoutMSec, long currTime) { if (this.requestCurrentPckCommandWithAck.isActive()) { // Check if we wait for an ack. - this.pckCommandsWithAck.pollFirst(); + this.pckCommandsWithAck.poll(); this.requestCurrentPckCommandWithAck.reset(); // Try to process next acknowledged command this.tryProcessNextCommandWithAck(conn, timeoutMSec, currTime); @@ -195,13 +195,13 @@ public void onAck(int code, Connection conn, long timeoutMSec, long currTime) { private boolean tryProcessNextCommandWithAck(Connection conn, long timeoutMSec, long currTime) { // Use the chance to remove a failed command first if (this.requestCurrentPckCommandWithAck.isFailed(timeoutMSec, currTime)) { - ByteBuffer failedCommand = this.pckCommandsWithAck.pollFirst(); + byte[] failedCommand = this.pckCommandsWithAck.poll(); this.requestCurrentPckCommandWithAck.reset(); if (failedCommand != null) { try { logger.warn("{}: Module did not respond to command: {}", addr, - new String(failedCommand.array(), LcnDefs.LCN_ENCODING)); + new String(failedCommand, LcnDefs.LCN_ENCODING)); } catch (UnsupportedEncodingException e) { // ignore } @@ -211,7 +211,7 @@ private boolean tryProcessNextCommandWithAck(Connection conn, long timeoutMSec, if (!this.pckCommandsWithAck.isEmpty() && !this.requestCurrentPckCommandWithAck.isActive()) { this.requestCurrentPckCommandWithAck.nextRequestIn(0, currTime); } - ByteBuffer command = this.pckCommandsWithAck.peekFirst(); + byte[] command = this.pckCommandsWithAck.peek(); if (command == null) { return false; } @@ -222,8 +222,8 @@ private boolean tryProcessNextCommandWithAck(Connection conn, long timeoutMSec, } } catch (LcnException e) { try { - logger.warn("{}: Could not send command: {}: {}", addr, - new String(command.array(), LcnDefs.LCN_ENCODING), e.getMessage()); + logger.warn("{}: Could not send command: {}: {}", addr, new String(command, LcnDefs.LCN_ENCODING), + e.getMessage()); } catch (UnsupportedEncodingException e1) { // ignore } @@ -253,6 +253,17 @@ public boolean hasExtendedMeasurementProcessing() { return firmwareVersion >= LcnBindingConstants.FIRMWARE_2013; } + private boolean update(Connection conn, long timeoutMSec, long currTime, RequestStatus requestStatus, String pck) + throws LcnException { + RequestStatus r; + if ((r = requestStatus).shouldSendNextRequest(timeoutMSec, currTime)) { + conn.queue(this.addr, false, pck); + r.onRequestSent(currTime); + return true; + } + return false; + } + /** * Keeps the request logic active. * Must be called periodically. @@ -264,32 +275,23 @@ public boolean hasExtendedMeasurementProcessing() { void update(Connection conn, long timeoutMSec, long currTime) { RequestStatus r; try { - // Firmware request - if ((r = this.requestFirmwareVersion).shouldSendNextRequest(timeoutMSec, currTime)) { - conn.queue(this.addr, false, PckGenerator.requestSn()); - r.onRequestSent(currTime); + if (update(conn, timeoutMSec, currTime, requestFirmwareVersion, PckGenerator.requestSn())) { return; } - // Output-port requests - for (int i = 0; i < 4; ++i) { - if ((r = this.requestStatusOutputs.get(i)).shouldSendNextRequest(timeoutMSec, currTime)) { - conn.queue(this.addr, false, PckGenerator.requestOutputStatus(i)); - r.onRequestSent(currTime); + + for (int i = 0; i < LcnChannelGroup.OUTPUT.getCount(); ++i) { + if (update(conn, timeoutMSec, currTime, requestStatusOutputs[i], PckGenerator.requestOutputStatus(i))) { return; } } - // Relays request - if ((r = this.requestStatusRelays).shouldSendNextRequest(timeoutMSec, currTime)) { - conn.queue(this.addr, false, PckGenerator.requestRelaysStatus()); - r.onRequestSent(currTime); + + if (update(conn, timeoutMSec, currTime, requestStatusRelays, PckGenerator.requestRelaysStatus())) { return; } - // Binary-sensors request - if ((r = this.requestStatusBinSensors).shouldSendNextRequest(timeoutMSec, currTime)) { - conn.queue(this.addr, false, PckGenerator.requestBinSensorsStatus()); - r.onRequestSent(currTime); + if (update(conn, timeoutMSec, currTime, requestStatusBinSensors, PckGenerator.requestBinSensorsStatus())) { return; } + // Variable requests if (this.firmwareVersion != -1) { // Firmware version is required // Use the chance to remove a failed "typeless variable" request @@ -320,18 +322,16 @@ void update(Connection conn, long timeoutMSec, long currTime) { } } } - // LEDs and logic-operations request - if ((r = this.requestStatusLedsAndLogicOps).shouldSendNextRequest(timeoutMSec, currTime)) { - conn.queue(this.addr, false, PckGenerator.requestLedsAndLogicOpsStatus()); - r.onRequestSent(currTime); + + if (update(conn, timeoutMSec, currTime, requestStatusLedsAndLogicOps, + PckGenerator.requestLedsAndLogicOpsStatus())) { return; } - // Key-locks request - if ((r = this.requestStatusLockedKeys).shouldSendNextRequest(timeoutMSec, currTime)) { - conn.queue(this.addr, false, PckGenerator.requestKeyLocksStatus()); - r.onRequestSent(currTime); + + if (update(conn, timeoutMSec, currTime, requestStatusLockedKeys, PckGenerator.requestKeyLocksStatus())) { return; } + // Try to send next acknowledged command. Will also detect failed ones. this.tryProcessNextCommandWithAck(conn, timeoutMSec, currTime); } catch (LcnException e) { @@ -389,7 +389,7 @@ public long getVariableValue(Variable variable) throws LcnException { * Requests the current value of all dimmer outputs. */ public void refreshAllOutputs() { - requestStatusOutputs.forEach(RequestStatus::refresh); + Arrays.stream(requestStatusOutputs).forEach(RequestStatus::refresh); } /** @@ -398,7 +398,7 @@ public void refreshAllOutputs() { * @param number 0..3 */ public void refreshOutput(int number) { - requestStatusOutputs.get(number).refresh(); + requestStatusOutputs[number].refresh(); } /** @@ -458,7 +458,7 @@ public void refreshStatusStatusLockedKeysAfterChange() { * @param outputId 0..3 */ public void onOutputResponseReceived(int outputId) { - requestStatusOutputs.get(outputId).onResponseReceived(); + requestStatusOutputs[outputId].onResponseReceived(); } /** diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/PckQueueItem.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/PckQueueItem.java index 24305e6aefef1..f65ac8fa9ce9e 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/PckQueueItem.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/PckQueueItem.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.nio.ByteBuffer; import java.time.Instant; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -25,12 +24,12 @@ */ @NonNullByDefault public class PckQueueItem { - private Instant enqueued; - private LcnAddr addr; - private boolean wantsAck; - private ByteBuffer data; + private final Instant enqueued; + private final LcnAddr addr; + private final boolean wantsAck; + private final byte[] data; - public PckQueueItem(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + public PckQueueItem(LcnAddr addr, boolean wantsAck, byte[] data) { this.enqueued = Instant.now(); this.addr = addr; this.wantsAck = wantsAck; @@ -66,10 +65,10 @@ public boolean isWantsAck() { /** * Gets the raw PCK message to be sent. - * + * * @return message as ByteBuffer */ - public ByteBuffer getData() { + public byte[] getData() { return data; } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/RequestStatus.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/RequestStatus.java index bc233a7802329..e5b95e876bda8 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/RequestStatus.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/RequestStatus.java @@ -43,7 +43,7 @@ public class RequestStatus { /** Number of retries left until the request is marked as failed. */ private volatile int numRetriesLeft; - private String label; + private final String label; /** * Constructor. diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendData.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendData.java index 8bcd75d7e796e..62aadec36b2dc 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendData.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendData.java @@ -12,9 +12,10 @@ */ package org.openhab.binding.lcn.internal.connection; +import java.io.IOException; +import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -35,7 +36,8 @@ public abstract class SendData { * @return true if everything was set-up correctly and data was written * @throws UnsupportedEncodingException if text could not be encoded for LCN-PCHK * @throws BufferOverflowException if target buffer has not enough space left (buffer will not be altered) + * @throws IOException if an I/O error occurs */ - abstract boolean write(ByteBuffer buffer, int localSegId) - throws UnsupportedEncodingException, BufferOverflowException; + abstract boolean write(OutputStream buffer, int localSegId) + throws UnsupportedEncodingException, BufferOverflowException, IOException; } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPck.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPck.java index 7d43f488cd1f2..e517d33191b8f 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPck.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPck.java @@ -12,9 +12,9 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.io.UnsupportedEncodingException; +import java.io.IOException; +import java.io.OutputStream; import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lcn.internal.common.LcnAddr; @@ -37,7 +37,7 @@ class SendDataPck extends SendData { private final boolean wantsAck; /** PCK command (without address header) encoded as bytes. */ - private final ByteBuffer data; + private final byte[] data; /** * Constructor. @@ -46,7 +46,7 @@ class SendDataPck extends SendData { * @param wantsAck true to claim receipt * @param data the PCK command encoded as bytes */ - SendDataPck(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + SendDataPck(LcnAddr addr, boolean wantsAck, byte[] data) { this.addr = addr; this.wantsAck = wantsAck; this.data = data; @@ -57,22 +57,21 @@ class SendDataPck extends SendData { * * @return the PCK command encoded as bytes */ - ByteBuffer getData() { + byte[] getData() { return this.data; } @Override - boolean write(ByteBuffer buffer, int localSegId) throws UnsupportedEncodingException, BufferOverflowException { - buffer.put(PckGenerator.generateAddressHeader(this.addr, localSegId == -1 ? 0 : localSegId, this.wantsAck) + boolean write(OutputStream buffer, int localSegId) throws BufferOverflowException, IOException { + buffer.write(PckGenerator.generateAddressHeader(this.addr, localSegId == -1 ? 0 : localSegId, this.wantsAck) .getBytes(LcnDefs.LCN_ENCODING)); - data.rewind(); - buffer.put(this.data); - buffer.put(PckGenerator.TERMINATION.getBytes(LcnDefs.LCN_ENCODING)); + buffer.write(this.data); + buffer.write(PckGenerator.TERMINATION.getBytes(LcnDefs.LCN_ENCODING)); return true; } @Override public String toString() { - return "Addr: " + addr + ": " + new String(data.array(), 0, data.limit()); + return "Addr: " + addr + ": " + new String(data, 0, data.length); } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPlainText.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPlainText.java index c038190aad675..65e31e8f2d903 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPlainText.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPlainText.java @@ -12,9 +12,8 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.io.UnsupportedEncodingException; -import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; +import java.io.IOException; +import java.io.OutputStream; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lcn.internal.common.LcnDefs; @@ -50,8 +49,8 @@ String getText() { } @Override - boolean write(ByteBuffer buffer, int localSegId) throws UnsupportedEncodingException, BufferOverflowException { - buffer.put((this.text + PckGenerator.TERMINATION).getBytes(LcnDefs.LCN_ENCODING)); + boolean write(OutputStream buffer, int localSegId) throws IOException { + buffer.write((this.text + PckGenerator.TERMINATION).getBytes(LcnDefs.LCN_ENCODING)); return true; } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateContext.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateContext.java index 9164ef1160db6..ec7908b1f35d4 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateContext.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateContext.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.nio.ByteBuffer; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.lcn.internal.common.LcnAddr; @@ -62,5 +60,5 @@ public interface StateContext { * @param wantsAck true, if the module shall respond with an Ack * @param data the PCK message */ - void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data); + void queue(LcnAddr addr, boolean wantsAck, byte[] data); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateMachine.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateMachine.java index 365f0155f2d75..ae7d91d8ef897 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateMachine.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateMachine.java @@ -13,7 +13,6 @@ package org.openhab.binding.lcn.internal.connection; import java.lang.reflect.InvocationTargetException; -import java.nio.ByteBuffer; import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -32,8 +31,8 @@ public class StateMachine implements StateContext { private final Logger logger = LoggerFactory.getLogger(StateMachine.class); /** The StateMachine's current state */ protected volatile AbstractConnectionState state; - private Connection connection; - private ScheduledExecutorService scheduler; + private final Connection connection; + private final ScheduledExecutorService scheduler; public StateMachine(Connection connection, ScheduledExecutorService scheduler) { this.connection = connection; @@ -65,7 +64,7 @@ public void startWorking() { } @Override - public void queue(LcnAddr addr, boolean wantsAck, ByteBuffer data) { + public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { state.queue(addr, wantsAck, data); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AngleConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AngleConverter.java deleted file mode 100644 index 1bc0c3489ce03..0000000000000 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AngleConverter.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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.converter; - -import javax.measure.Unit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; - -/** - * Converts the native LCN value of LCN-WIH to the sun azimuth/elevation values. - * - * @author Fabian Wolter - Initial Contribution - */ -@NonNullByDefault -public class AngleConverter extends AbstractVariableValueConverter { - @Override - protected Unit getUnitType() { - return SmartHomeUnits.DEGREE_ANGLE; - } - - @Override - public int toNative(double value) { - return (int) (Math.round(value * 10) + 1000); - } - - @Override - public double toHumanReadable(long value) { - return (value - 1000) / 10f; - } -} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Co2Converter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Co2Converter.java deleted file mode 100644 index d7f39920496b4..0000000000000 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Co2Converter.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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.converter; - -import javax.measure.Unit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; - -/** - * Converts the native LCN value of LCN-CO2 to the ppm value. - * - * @author Fabian Wolter - Initial Contribution - */ -@NonNullByDefault -public class Co2Converter extends AbstractVariableValueConverter { - @Override - protected Unit getUnitType() { - return SmartHomeUnits.PARTS_PER_MILLION; - } - - @Override - public int toNative(double value) { - return (int) Math.round(value); - } - - @Override - public double toHumanReadable(long value) { - return value; - } -} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractVariableValueConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Converter.java similarity index 69% rename from bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractVariableValueConverter.java rename to bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Converter.java index cada6e8807086..456bbe847da48 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractVariableValueConverter.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Converter.java @@ -1,15 +1,3 @@ -/** - * 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 - */ /** * Copyright (c) 2010-2020 Contributors to the openHAB project * @@ -24,9 +12,12 @@ */ package org.openhab.binding.lcn.internal.converter; +import java.util.function.Function; + import javax.measure.Unit; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.QuantityType; import org.eclipse.smarthome.core.types.State; @@ -40,15 +31,17 @@ * @author Fabian Wolter - Initial Contribution */ @NonNullByDefault -public abstract class AbstractVariableValueConverter { - private final Logger logger = LoggerFactory.getLogger(AbstractVariableValueConverter.class); +public class Converter { + private final Logger logger = LoggerFactory.getLogger(Converter.class); + private @Nullable final Unit unit; + private final Function toHuman; + private final Function toNative; - /** - * Gets the Profile's Unit. - * - * @return the Unit - */ - protected abstract Unit getUnitType(); + public Converter(@Nullable Unit unit, Function toHuman, Function toNative) { + this.unit = unit; + this.toHuman = toHuman; + this.toNative = toNative; + } /** * Converts the given human readable value into the native LCN value. @@ -56,7 +49,9 @@ public abstract class AbstractVariableValueConverter { * @param humanReadableValue the value to convert * @return the native value */ - protected abstract int toNative(double humanReadableValue); + protected long toNative(double humanReadableValue) { + return toNative.apply(humanReadableValue); + } /** * Converts the given native LCN value into a human readable value. @@ -64,7 +59,9 @@ public abstract class AbstractVariableValueConverter { * @param nativeValue the value to convert * @return the human readable value */ - protected abstract double toHumanReadable(long nativeValue); + protected double toHumanReadable(long nativeValue) { + return toHuman.apply(nativeValue); + } /** * Converts a human readable value into LCN native value. @@ -84,12 +81,17 @@ public DecimalType onCommandFromItem(double humanReadable) { * @throws LcnException when the value could not be converted to the base unit */ public DecimalType onCommandFromItem(QuantityType quantityType) throws LcnException { - QuantityType quantityInBaseUnit = quantityType.toUnit(getUnitType()); + Unit localUnit = unit; + if (localUnit == null) { + return onCommandFromItem(quantityType.doubleValue()); + } + + QuantityType quantityInBaseUnit = quantityType.toUnit(localUnit); if (quantityInBaseUnit != null) { return onCommandFromItem(quantityInBaseUnit.doubleValue()); } else { - throw new LcnException(quantityType + ": Incompatible unit: " + getUnitType()); + throw new LcnException(quantityType + ": Incompatible Channel unit configured: " + localUnit); } } @@ -103,7 +105,10 @@ public State onStateUpdateFromHandler(State state) { State result = state; if (state instanceof DecimalType) { - result = QuantityType.valueOf(toHumanReadable(((DecimalType) state).longValue()), getUnitType()); + Unit localUnit = unit; + if (localUnit != null) { + result = QuantityType.valueOf(toHumanReadable(((DecimalType) state).longValue()), localUnit); + } } else { logger.warn("Unexpected state type: {}", state.getClass().getSimpleName()); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Converters.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Converters.java new file mode 100644 index 0000000000000..3a8c5fff7aa04 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/Converters.java @@ -0,0 +1,62 @@ +/** + * 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.converter; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.unit.SIUnits; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; + +/** + * Holds all Converter objects. + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public class Converters { + public static final Converter TEMPERATURE; + public static final Converter LIGHT; + public static final Converter CO2; + public static final Converter CURRENT; + public static final Converter VOLTAGE; + public static final Converter ANGLE; + public static final Converter WINDSPEED; + public static final Converter IDENTITY; + + static { + TEMPERATURE = new Converter(SIUnits.CELSIUS, n -> (n - 1000) / 10d, h -> Math.round(h * 10) + 1000); + LIGHT = new Converter(SmartHomeUnits.LUX, Converters::lightToHumanReadable, Converters::lightToNative); + CO2 = new Converter(SmartHomeUnits.PARTS_PER_MILLION, n -> (double) n, Math::round); + CURRENT = new Converter(SmartHomeUnits.AMPERE, n -> n / 100d, h -> Math.round(h * 100)); + VOLTAGE = new Converter(SmartHomeUnits.VOLT, n -> n / 400d, h -> Math.round(h * 400)); + ANGLE = new Converter(SmartHomeUnits.DEGREE_ANGLE, n -> (n - 1000) / 10d, Converters::angleToNative); + WINDSPEED = new Converter(SmartHomeUnits.METRE_PER_SECOND, n -> n / 10d, h -> Math.round(h * 10)); + IDENTITY = new Converter(null, n -> (double) n, Math::round); + } + + private static long lightToNative(double value) { + return Math.round(Math.log(value) * 100); + } + + private static double lightToHumanReadable(long value) { + // Max. value hardware can deliver is 100klx. Apply hard limit, because higher native values lead to very big + // lux values. + if (value > lightToNative(100e3)) { + return Double.NaN; + } + return Math.exp(value / 100d); + } + + private static long angleToNative(double h) { + return (Math.round(h * 10) + 1000); + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/CurrentConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/CurrentConverter.java deleted file mode 100644 index 87afb1f7aca27..0000000000000 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/CurrentConverter.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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.converter; - -import javax.measure.Unit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; - -/** - * Converts the native LCN value of a 4-20mA input of LCN-AD2 to current. - * - * @author Fabian Wolter - Initial Contribution - */ -@NonNullByDefault -public class CurrentConverter extends AbstractVariableValueConverter { - @Override - protected Unit getUnitType() { - return SmartHomeUnits.AMPERE; - } - - @Override - public int toNative(double value) { - return (int) Math.round(value * 100); - } - - @Override - public double toHumanReadable(long value) { - return value / 100d; - } -} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/EnergyConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/EnergyConverter.java deleted file mode 100644 index 3cbbc981ad61c..0000000000000 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/EnergyConverter.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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.converter; - -import javax.measure.Unit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; - -/** - * Converts the native LCN value of an S0 counter of LCN-BU4L to kWh. - * - * @author Fabian Wolter - Initial Contribution - */ -@NonNullByDefault -public class EnergyConverter extends AbstractS0Converter { - public EnergyConverter(@Nullable Object parameter) { - super(parameter); - } - - @Override - protected Unit getUnitType() { - return SmartHomeUnits.WATT_HOUR; - } -} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/IdentityConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/IdentityConverter.java deleted file mode 100644 index e64a0462e697e..0000000000000 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/IdentityConverter.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * 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.converter; - -import javax.measure.Unit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.library.types.DecimalType; -import org.eclipse.smarthome.core.library.types.QuantityType; -import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; -import org.openhab.binding.lcn.internal.common.LcnException; - -/** - * Converts the value 1:1. - * - * @author Fabian Wolter - Initial Contribution - */ -@NonNullByDefault -public class IdentityConverter extends AbstractVariableValueConverter { - private IdentityConverter() { - // nothing - } - - @NonNullByDefault({}) - private static class LazyHolder { - static final IdentityConverter INSTANCE = new IdentityConverter(); - } - - public static IdentityConverter getInstance() { - return LazyHolder.INSTANCE; - } - - @Override - protected Unit getUnitType() { - return SmartHomeUnits.VOLT; // not used - } - - @Override - public DecimalType onCommandFromItem(QuantityType quantityType) throws LcnException { - return onCommandFromItem(quantityType.doubleValue()); - } - - @Override - public int toNative(double value) { - return (int) Math.round(value); - } - - @Override - public double toHumanReadable(long value) { - return value; - } -} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/LightConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/LightConverter.java deleted file mode 100644 index 22209fa1e84d4..0000000000000 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/LightConverter.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 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.converter; - -import javax.measure.Unit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; - -/** - * Converts the native LCN value of I-Port periphery like LCN-GBL, LCN-GUS, LCN-WIH to the lux value. - * Profile doesn't support the LCN-LS on the T-Port. - * - * @author Fabian Wolter - Initial Contribution - */ -@NonNullByDefault -public class LightConverter extends AbstractVariableValueConverter { - @Override - protected Unit getUnitType() { - return SmartHomeUnits.LUX; - } - - @Override - public int toNative(double value) { - return (int) Math.round(Math.log(value) * 100); - } - - @Override - public double toHumanReadable(long value) { - // Max. value hardware can deliver is 100klx. Apply hard limit, because higher native values lead to very big - // lux values. - if (value > toNative(100e3)) { - return Double.NaN; - } - return Math.exp(value / 100d); - } -} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/PowerConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/PowerConverter.java deleted file mode 100644 index 167f5f6e7a2d0..0000000000000 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/PowerConverter.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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.converter; - -import javax.measure.Unit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; - -/** - * Converts the native LCN value of an S0 input power variable of LCN-BU4L to Watts. - * - * @author Fabian Wolter - Initial Contribution - */ -@NonNullByDefault -public class PowerConverter extends AbstractS0Converter { - public PowerConverter(@Nullable Object parameter) { - super(parameter); - } - - @Override - protected Unit getUnitType() { - return SmartHomeUnits.WATT; - } -} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractS0Converter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/S0Converter.java similarity index 72% rename from bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractS0Converter.java rename to bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/S0Converter.java index 9971af24102d8..b2b4989a668c8 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/AbstractS0Converter.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/S0Converter.java @@ -16,6 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,14 +26,16 @@ * @author Fabian Wolter - Initial Contribution */ @NonNullByDefault -public abstract class AbstractS0Converter extends AbstractVariableValueConverter { - private final Logger logger = LoggerFactory.getLogger(AbstractS0Converter.class); +public class S0Converter extends Converter { + private final Logger logger = LoggerFactory.getLogger(S0Converter.class); protected double pulsesPerKwh; - public AbstractS0Converter(@Nullable Object parameter) { + public S0Converter(@Nullable Object parameter) { + super(SmartHomeUnits.WATT, n -> 0d, h -> 0L); + if (parameter == null) { pulsesPerKwh = 1000; - logger.info("Pulses per kWh not set. Assuming 1000 imp./kWh."); + logger.debug("Pulses per kWh not set. Assuming 1000 imp./kWh."); } else if (parameter instanceof BigDecimal) { pulsesPerKwh = ((BigDecimal) parameter).doubleValue(); } else { @@ -41,8 +44,8 @@ public AbstractS0Converter(@Nullable Object parameter) { } @Override - public int toNative(double value) { - return (int) Math.round(value * pulsesPerKwh / 1000); + public long toNative(double value) { + return Math.round(value * pulsesPerKwh / 1000); } @Override diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/TemperatureConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/TemperatureConverter.java deleted file mode 100644 index 0fca1cc58cfdb..0000000000000 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/TemperatureConverter.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * 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.converter; - -import javax.measure.Unit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.library.unit.SIUnits; - -/** - * Converts the native LCN value to temperature. - * - * @author Fabian Wolter - Initial Contribution - */ -@NonNullByDefault -public class TemperatureConverter extends AbstractVariableValueConverter { - @Override - protected Unit getUnitType() { - // The user can also configure "°F" instead of "°C" in the item. The value will be converted accordingly. - return SIUnits.CELSIUS; - } - - @Override - public int toNative(double value) { - return (int) (Math.round(value * 10) + 1000); - } - - @Override - public double toHumanReadable(long value) { - return (value - 1000) / 10d; - } -} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/VoltageConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/VoltageConverter.java deleted file mode 100644 index ae2ffd22199d8..0000000000000 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/VoltageConverter.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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.converter; - -import javax.measure.Unit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; - -/** - * Converts the native LCN value of a 0-10V input of LCN-AD2 to voltage. - * - * @author Fabian Wolter - Initial Contribution - */ -@NonNullByDefault -public class VoltageConverter extends AbstractVariableValueConverter { - @Override - protected Unit getUnitType() { - return SmartHomeUnits.VOLT; - } - - @Override - public int toNative(double value) { - return (int) Math.round(value * 400); - } - - @Override - public double toHumanReadable(long value) { - return value / 400d; - } -} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/WindspeedConverter.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/WindspeedConverter.java deleted file mode 100644 index 39aa0cdb04f8e..0000000000000 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/converter/WindspeedConverter.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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.converter; - -import javax.measure.Unit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; - -/** - * Converts the native LCN value of LCN-WIH to the wind speed value. - * - * @author Fabian Wolter - Initial Contribution - */ -@NonNullByDefault -public class WindspeedConverter extends AbstractVariableValueConverter { - @Override - protected Unit getUnitType() { - return SmartHomeUnits.METRE_PER_SECOND; - } - - @Override - public int toNative(double value) { - return (int) Math.round(value * 10); - } - - @Override - public double toHumanReadable(long value) { - return value / 10d; - } -} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtService.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtService.java index 0a7add3995054..1cf6f92bd24f0 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtService.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtService.java @@ -25,9 +25,9 @@ @NonNullByDefault @XStreamConverter(value = ToAttributedValueConverter.class, strings = { "content" }) public class ExtService { - private int localPort; + private final int localPort; @SuppressWarnings("unused") - private String content = ""; + private final String content = ""; public ExtService(int localPort) { this.localPort = localPort; diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtServices.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtServices.java index 1e88d3145856f..da2ec561faa8c 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtServices.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ExtServices.java @@ -21,7 +21,7 @@ */ @NonNullByDefault public class ExtServices { - private ExtService ExtService; + private final ExtService ExtService; public ExtServices(ExtService extService) { ExtService = extService; diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Server.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Server.java index 0b965c5fac80a..ee39fa6ab7254 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Server.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Server.java @@ -27,16 +27,16 @@ @XStreamConverter(value = ToAttributedValueConverter.class, strings = { "content" }) public class Server { @XStreamAsAttribute - private int requestId; + private final int requestId; @XStreamAsAttribute - private String machineId; + private final String machineId; @XStreamAsAttribute - private String machineName; + private final String machineName; @XStreamAsAttribute - private String osShort; + private final String osShort; @XStreamAsAttribute - private String osLong; - private String content; + private final String osLong; + private final String content; public Server(int requestId, String machineId, String machineName, String osShort, String osLong, String content) { this.requestId = requestId; diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ServicesResponse.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ServicesResponse.java index ba5a0300f9dd1..e2a29e2434405 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ServicesResponse.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/ServicesResponse.java @@ -21,11 +21,11 @@ */ @NonNullByDefault public class ServicesResponse { - private Version Version; - private Server Server; - private ExtServices ExtServices; + private final Version Version; + private final Server Server; + private final ExtServices ExtServices; @SuppressWarnings("unused") - private Object Services = new Object(); + private final Object Services = new Object(); public ServicesResponse(Version version, Server server, ExtServices extServices) { this.Version = version; diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Version.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Version.java index 58f2697e046dc..6c406662474ae 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Version.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/Version.java @@ -24,9 +24,9 @@ @NonNullByDefault public class Version { @XStreamAsAttribute - private int major; + private final int major; @XStreamAsAttribute - private int minor; + private final int minor; public Version(int major, int minor) { this.major = major; diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleSubHandler.java index 7c0547f03be05..3988283d50305 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleSubHandler.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/AbstractLcnModuleSubHandler.java @@ -46,8 +46,8 @@ @NonNullByDefault public abstract class AbstractLcnModuleSubHandler implements ILcnModuleSubHandler { private final Logger logger = LoggerFactory.getLogger(AbstractLcnModuleSubHandler.class); - protected LcnModuleHandler handler; - protected ModInfo info; + protected final LcnModuleHandler handler; + protected final ModInfo info; public AbstractLcnModuleSubHandler(LcnModuleHandler handler, ModInfo info) { this.handler = handler; @@ -109,7 +109,7 @@ public void handleCommandHsb(HSBType command, String groupId) throws LcnExceptio } private void unsupportedCommand(Command command) { - logger.warn("Unsupported command: {}", command.getClass().getSimpleName()); + logger.warn("Unsupported command: {}: {}", getClass().getSimpleName(), command.getClass().getSimpleName()); } /** @@ -121,7 +121,7 @@ private void unsupportedCommand(Command command) { */ public boolean tryParse(String pck) { Optional firstSuccessfulMatcher = getPckStatusMessagePatterns().stream().map(p -> p.matcher(pck)) - .filter(m -> m.matches()).filter(m -> handler.isMyAddress(m.group("segId"), m.group("modId"))) + .filter(Matcher::matches).filter(m -> handler.isMyAddress(m.group("segId"), m.group("modId"))) .findAny(); firstSuccessfulMatcher.ifPresent(matcher -> { diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleOutputSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleOutputSubHandler.java index 9516b7d46b721..d7f5d1fcb179c 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleOutputSubHandler.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleOutputSubHandler.java @@ -159,19 +159,16 @@ public void handleStatusMessage(Matcher matcher) { logger.warn("Unexpected pattern: {}", matcher.pattern()); return; } - if (percent < 0 || percent > 100) { - logger.warn("Output value out of range: {}", percent); - return; - } + info.onOutputResponseReceived(outputId); + percent = Math.min(100, Math.max(0, percent)); + PercentType percentType = new PercentType((int) Math.round(percent)); fireUpdate(LcnChannelGroup.OUTPUT, outputId, percentType); if (outputId == 3) { - synchronized (this) { - output4 = percentType; - } + output4 = percentType; } if (percent > 0) { diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleThresholdSubHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleThresholdSubHandler.java index 9b7a0f7f57ea3..744b61db88f9f 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleThresholdSubHandler.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/subhandler/LcnModuleThresholdSubHandler.java @@ -90,7 +90,8 @@ public void handleStatusMessage(Matcher matcher) { stream.forEach(i -> { try { - fireUpdateAndReset(matcher, groupSuffix.orElse(i + ""), Variable.thrsIdToVar(registerNumber, i)); + fireUpdateAndReset(matcher, groupSuffix.orElse(String.valueOf(i)), + Variable.thrsIdToVar(registerNumber, i)); } catch (LcnException e) { logger.warn("Parse error: {}", e.getMessage()); } diff --git a/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/config/config.xml b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/config/config.xml index e4a52125a1948..439256829226b 100644 --- a/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/config/config.xml +++ b/bundles/org.openhab.binding.lcn/src/main/resources/ESH-INF/config/config.xml @@ -96,6 +96,7 @@ Only for S0 counters (power or energy) + 1000 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 1623b4cf0c066..7921cd5534715 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 @@ -148,7 +148,7 @@ - Roller Shutter + Rollershutter veto @@ -157,16 +157,16 @@ - + - + - + - + @@ -175,7 +175,7 @@ - + @@ -191,7 +191,6 @@ - veto @@ -246,7 +245,6 @@ - veto @@ -306,7 +304,6 @@ Number - veto @@ -398,7 +395,8 @@ - + + Only before Feb. 2013 @@ -478,7 +476,6 @@ Switch - veto diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/ModuleActionsTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/ModuleActionsTest.java index 04d588e78bef3..747bf3079cb69 100644 --- a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/ModuleActionsTest.java +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/ModuleActionsTest.java @@ -37,9 +37,9 @@ @NonNullByDefault public class ModuleActionsTest { private LcnModuleActions a = new LcnModuleActions(); - private LcnModuleHandler handler = mock(LcnModuleHandler.class); + private final LcnModuleHandler handler = mock(LcnModuleHandler.class); @Captor - private @NonNullByDefault({}) ArgumentCaptor byteBufferCaptor; + private @NonNullByDefault({}) ArgumentCaptor byteBufferCaptor; @Before public void setUp() { @@ -48,10 +48,8 @@ public void setUp() { a.setThingHandler(handler); } - private ByteBuffer stringToByteBuffer(String string) throws UnsupportedEncodingException { - ByteBuffer bb = ByteBuffer.wrap(string.getBytes(LcnDefs.LCN_ENCODING)); - bb.position(bb.capacity()); - return bb; + private byte[] stringToByteBuffer(String string) throws UnsupportedEncodingException { + return string.getBytes(LcnDefs.LCN_ENCODING); } @Test @@ -130,8 +128,17 @@ public void testSendDynamicTextSplitInCharacter() throws LcnException, Unsupport verify(handler, times(2)).sendPck(byteBufferCaptor.capture()); - assertThat(byteBufferCaptor.getAllValues(), - contains(stringToByteBuffer("GTDT41Test 123 ö\303"), stringToByteBuffer("GTDT42\244üß\0\0\0\0\0\0"))); + String string1 = "GTDT41Test 123 ö"; + ByteBuffer chunk1 = ByteBuffer.allocate(stringToByteBuffer(string1).length + 1); + chunk1.put(stringToByteBuffer(string1)); + chunk1.put((byte) -61); // first byte of ä + + ByteBuffer chunk2 = ByteBuffer.allocate(18); + chunk2.put(stringToByteBuffer("GTDT42")); + chunk2.put((byte) -92); // second byte of ä + chunk2.put(stringToByteBuffer("üß\0\0\0\0\0\0")); + + assertThat(byteBufferCaptor.getAllValues(), contains(chunk1.array(), chunk2.array())); } @Test From ec291ce4f40624263177049a28d70762a64e8c9c Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Wed, 10 Jun 2020 10:47:30 +0200 Subject: [PATCH 08/16] Fix DimmerOutputProfile Signed-off-by: Fabian Wolter --- .../org/openhab/binding/lcn/internal/DimmerOutputProfile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/DimmerOutputProfile.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/DimmerOutputProfile.java index 9f5ce014a8e83..5c7feb50d2c78 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/DimmerOutputProfile.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/DimmerOutputProfile.java @@ -94,7 +94,7 @@ public void onCommandFromItem(Command command) { logger.warn("Unsupported type: {}", command.toFullString()); return; } - callback.sendUpdate(new DimmerOutputCommand(value, controlAllOutputs, controlOutputs12, rampMs)); + callback.handleCommand(new DimmerOutputCommand(value, controlAllOutputs, controlOutputs12, rampMs)); } @Override From aa746b45c1b7e84de15259f8d41ff931d670d355 Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Wed, 10 Jun 2020 11:05:03 +0200 Subject: [PATCH 09/16] Fix NPE Signed-off-by: Fabian Wolter --- .../lcn/internal/connection/ModInfo.java | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java index 4a04ef9aa8bb9..63fbd5a334d58 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java @@ -14,10 +14,10 @@ import java.io.UnsupportedEncodingException; import java.util.Arrays; +import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Queue; -import java.util.TreeMap; import java.util.concurrent.ConcurrentLinkedQueue; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -83,12 +83,12 @@ public class ModInfo { * Variables request status. * Lazy initialization: Will be filled once the firmware version is known. */ - private final Map requestStatusVars = new TreeMap<>(); + private final Map requestStatusVars = new HashMap<>(); /** * Caches the values of the variables, needed for changing the values. */ - private final Map variableValue = new TreeMap<>(); + private final Map variableValue = new HashMap<>(); /** LEDs and logic-operations request status (all 12+4). */ private final RequestStatus requestStatusLedsAndLogicOps = new RequestStatus(MAX_STATUS_POLLED_VALUEAGE_MSEC, @@ -295,15 +295,16 @@ void update(Connection conn, long timeoutMSec, long currTime) { // Variable requests if (this.firmwareVersion != -1) { // Firmware version is required // Use the chance to remove a failed "typeless variable" request - if (this.lastRequestedVarWithoutTypeInResponse != Variable.UNKNOWN) { - if (this.requestStatusVars.get(this.lastRequestedVarWithoutTypeInResponse).isTimeout(timeoutMSec, - currTime)) { - this.lastRequestedVarWithoutTypeInResponse = Variable.UNKNOWN; + if (lastRequestedVarWithoutTypeInResponse != Variable.UNKNOWN) { + RequestStatus requestStatus = requestStatusVars.get(lastRequestedVarWithoutTypeInResponse); + if (requestStatus != null && requestStatus.isTimeout(timeoutMSec, currTime)) { + lastRequestedVarWithoutTypeInResponse = Variable.UNKNOWN; } } // Variables - for (Map.Entry kv : this.requestStatusVars.entrySet()) { - if ((r = kv.getValue()).shouldSendNextRequest(timeoutMSec, currTime)) { + for (Map.Entry kv : this.requestStatusVars.entrySet()) { + r = kv.getValue(); + if (r != null && r.shouldSendNextRequest(timeoutMSec, currTime)) { // Detect if we can send immediately or if we have to wait for a "typeless" request first boolean hasTypeInResponse = kv.getKey().hasTypeInResponse(this.firmwareVersion); if (hasTypeInResponse || this.lastRequestedVarWithoutTypeInResponse == Variable.UNKNOWN) { @@ -359,8 +360,12 @@ public void setFirmwareVersion(int firmwareVersion) { requestFirmwareVersion.onResponseReceived(); // increase poll interval, if the LCN module sends status updates of a variable event-based - requestStatusVars.entrySet().stream().filter(e -> e.getKey().isEventBased(firmwareVersion)) - .forEach(e -> e.getValue().setMaxAgeMSec(MAX_STATUS_EVENTBASED_VALUEAGE_MSEC)); + requestStatusVars.entrySet().stream().filter(e -> e.getKey().isEventBased(firmwareVersion)).forEach(e -> { + RequestStatus value = e.getValue(); + if (value != null) { + value.setMaxAgeMSec(MAX_STATUS_EVENTBASED_VALUEAGE_MSEC); + } + }); } /** @@ -421,7 +426,10 @@ public void refreshBinarySensors() { * @param variable the variable to request */ public void refreshVariable(Variable variable) { - requestStatusVars.get(variable).refresh(); + RequestStatus requestStatus = requestStatusVars.get(variable); + if (requestStatus != null) { + requestStatus.refresh(); + } } /** @@ -481,7 +489,10 @@ public void onBinarySensorsResponseReceived() { * @param variable the received variable type */ public void onVariableResponseReceived(Variable variable) { - requestStatusVars.get(variable).onResponseReceived(); + RequestStatus requestStatus = requestStatusVars.get(variable); + if (requestStatus != null) { + requestStatus.onResponseReceived(); + } } /** From 7384be9252bcb0aeb89efeada0f9fa255c15f424 Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Wed, 10 Jun 2020 15:09:59 +0200 Subject: [PATCH 10/16] Refactor Signed-off-by: Fabian Wolter --- .../binding/lcn/internal/connection/ModInfo.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java index 63fbd5a334d58..b8cecd733fa0f 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java @@ -255,10 +255,9 @@ public boolean hasExtendedMeasurementProcessing() { private boolean update(Connection conn, long timeoutMSec, long currTime, RequestStatus requestStatus, String pck) throws LcnException { - RequestStatus r; - if ((r = requestStatus).shouldSendNextRequest(timeoutMSec, currTime)) { + if (requestStatus.shouldSendNextRequest(timeoutMSec, currTime)) { conn.queue(this.addr, false, pck); - r.onRequestSent(currTime); + requestStatus.onRequestSent(currTime); return true; } return false; @@ -273,7 +272,6 @@ private boolean update(Connection conn, long timeoutMSec, long currTime, Request * @param currTime the current time stamp */ void update(Connection conn, long timeoutMSec, long currTime) { - RequestStatus r; try { if (update(conn, timeoutMSec, currTime, requestFirmwareVersion, PckGenerator.requestSn())) { return; @@ -303,21 +301,21 @@ void update(Connection conn, long timeoutMSec, long currTime) { } // Variables for (Map.Entry kv : this.requestStatusVars.entrySet()) { - r = kv.getValue(); - if (r != null && r.shouldSendNextRequest(timeoutMSec, currTime)) { + RequestStatus requestStatus = kv.getValue(); + if (requestStatus != null && requestStatus.shouldSendNextRequest(timeoutMSec, currTime)) { // Detect if we can send immediately or if we have to wait for a "typeless" request first boolean hasTypeInResponse = kv.getKey().hasTypeInResponse(this.firmwareVersion); if (hasTypeInResponse || this.lastRequestedVarWithoutTypeInResponse == Variable.UNKNOWN) { try { conn.queue(this.addr, false, PckGenerator.requestVarStatus(kv.getKey(), this.firmwareVersion)); - r.onRequestSent(currTime); + requestStatus.onRequestSent(currTime); if (!hasTypeInResponse) { this.lastRequestedVarWithoutTypeInResponse = kv.getKey(); } return; } catch (LcnException ex) { - r.reset(); + requestStatus.reset(); } } } From 9e249fde574174c45a06de38d391391f0c42920a Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Wed, 10 Jun 2020 22:17:56 +0200 Subject: [PATCH 11/16] Add representation property to Discovery Services Signed-off-by: Fabian Wolter --- .../internal/LcnModuleDiscoveryService.java | 20 ++++++++++++------- .../LcnPchkDiscoveryService.java | 17 ++++++++++------ 2 files changed, 24 insertions(+), 13 deletions(-) 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 5d4bca686cad3..ba1db4b67fe4f 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 @@ -63,6 +63,9 @@ public class LcnModuleDiscoveryService extends AbstractDiscoveryService private final Logger logger = LoggerFactory.getLogger(LcnModuleDiscoveryService.class); private static final Pattern NAME_PATTERN = Pattern .compile("=M(?\\d{3})(?\\d{3}).N(?[1-2]{1})(?.*)"); + private static final String SEGMENT_ID = "segmentId"; + private static final String MODULE_ID = "moduleId"; + private static final String SERIAL_NUMBER = "serialNumber"; private static final int MODULE_NAME_PART_COUNT = 2; private static final int DISCOVERY_TIMEOUT_SEC = 90; private static final int ACK_TIMEOUT_MS = 1000; @@ -148,16 +151,19 @@ protected void startScan() { } else if (matcher.pattern() == LcnModuleMetaFirmwareSubHandler.PATTERN) { // Received a firmware version info frame - Map properties = new HashMap<>(5); - properties.put("segmentId", addr.getSegmentId()); - properties.put("moduleId", addr.getModuleId()); - ThingUID bridgeUid = localBridgeHandler.getThing().getUID(); - String thingId = matcher.group("sn"); - ThingUID thingUid = new ThingUID(LcnBindingConstants.THING_TYPE_MODULE, bridgeUid, thingId); + String serialNumber = matcher.group("sn"); + ThingUID thingUid = new ThingUID(LcnBindingConstants.THING_TYPE_MODULE, bridgeUid, + serialNumber); + + Map properties = new HashMap<>(3); + properties.put(SEGMENT_ID, addr.getSegmentId()); + properties.put(MODULE_ID, addr.getModuleId()); + properties.put(SERIAL_NUMBER, serialNumber); DiscoveryResultBuilder discoveryResult = DiscoveryResultBuilder.create(thingUid) - .withProperties(properties).withBridge(bridgeUid); + .withProperties(properties).withRepresentationProperty(SERIAL_NUMBER) + .withBridge(bridgeUid); discoveryResultBuilders.put(addr, discoveryResult); } else if (matcher.pattern() == NAME_PATTERN) { diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java index a24260460c1a7..8b5dadaf27c6e 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java @@ -57,6 +57,9 @@ @Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.lcn") public class LcnPchkDiscoveryService extends AbstractDiscoveryService { private final Logger logger = LoggerFactory.getLogger(LcnPchkDiscoveryService.class); + private static final String HOSTNAME = "hostname"; + private static final String PORT = "port"; + private static final String MAC_ADDRESS = "macAddress"; private static final String PCHK_DISCOVERY_MULTICAST_ADDRESS = "234.5.6.7"; private static final int PCHK_DISCOVERY_PORT = 4220; private static final int INTERFACE_TIMEOUT_SEC = 2; @@ -120,15 +123,17 @@ protected void startScan() { ServicesResponse deserialized = xmlToServiceResponse(response); - Map properties = new HashMap<>(); - properties.put("hostname", addr.getHostAddress()); - properties.put("port", deserialized.getExtServices().getExtService().getLocalPort()); + String macAddress = deserialized.getServer().getMachineId().replace(":", ""); + ThingUID thingUid = new ThingUID(LcnBindingConstants.THING_TYPE_PCK_GATEWAY, macAddress); - String thingId = deserialized.getServer().getMachineId().replace(":", ""); - ThingUID thingUid = new ThingUID(LcnBindingConstants.THING_TYPE_PCK_GATEWAY, thingId); + Map properties = new HashMap<>(3); + properties.put(HOSTNAME, addr.getHostAddress()); + properties.put(PORT, deserialized.getExtServices().getExtService().getLocalPort()); + properties.put(MAC_ADDRESS, macAddress); DiscoveryResultBuilder discoveryResult = DiscoveryResultBuilder.create(thingUid) - .withProperties(properties).withLabel(deserialized.getServer().getContent() + " (" + .withProperties(properties).withRepresentationProperty(MAC_ADDRESS) + .withLabel(deserialized.getServer().getContent() + " (" + deserialized.getServer().getMachineName() + ")"); thingDiscovered(discoveryResult.build()); From 55f98964ceda94d860f5904b336419ce182f88d8 Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Wed, 10 Jun 2020 23:02:59 +0200 Subject: [PATCH 12/16] Incorporate review feedback Signed-off-by: Fabian Wolter --- .../lcn/internal/ILcnModuleActions.java | 31 +++++++++++++++++++ .../lcn/internal/LcnModuleActions.java | 13 +++++--- .../lcn/internal/PckGatewayHandler.java | 17 +++++----- .../binding/lcn/internal/common/Variable.java | 16 ++++++---- .../connection/AbstractConnectionState.java | 4 ++- ...bstractConnectionStateSendCredentials.java | 6 ---- .../connection/ConnectionStateConnecting.java | 6 ---- ...ectionStateGracePeriodBeforeReconnect.java | 6 ---- .../connection/ConnectionStateInit.java | 6 ---- .../ConnectionStateSegmentScan.java | 6 ---- .../ConnectionStateSendDimMode.java | 6 ---- ...ConnectionStateWaitForLcnBusConnected.java | 6 ---- 12 files changed, 62 insertions(+), 61 deletions(-) create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/ILcnModuleActions.java diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/ILcnModuleActions.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/ILcnModuleActions.java new file mode 100644 index 0000000000000..a6f2871e44321 --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/ILcnModuleActions.java @@ -0,0 +1,31 @@ +/** + * 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; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link ILcnModuleActions} defines the interface for all thing actions supported by the binding. + * These methods, parameters, and return types are explained in {@link LcnModuleActions}. + * + * @author Fabian Wolter - Initial contribution + */ +@NonNullByDefault +public interface ILcnModuleActions { + void hitKey(@Nullable String table, int key, @Nullable String action); + + void flickerOutput(int output, int depth, int ramp, int count); + + void sendDynamicText(int row, @Nullable String textInput); +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java index 2dde6b422ff3c..8da7ea7067b5b 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java @@ -41,7 +41,7 @@ */ @ThingActionsScope(name = "lcn") @NonNullByDefault -public class LcnModuleActions implements ThingActions { +public class LcnModuleActions implements ThingActions, ILcnModuleActions { private final Logger logger = LoggerFactory.getLogger(LcnModuleActions.class); private static final int DYN_TEXT_CHUNK_COUNT = 5; private static final int DYN_TEXT_HEADER_LENGTH = 6; @@ -58,6 +58,7 @@ public void setThingHandler(@Nullable ThingHandler handler) { return moduleHandler; } + @Override @RuleAction(label = "LCN Hit Key", description = "Sends a \"hit key\" command to an LCN module") public void hitKey( @ActionInput(name = "table", required = true, type = "java.lang.String", label = "Table", description = "The key table (A-D)") @Nullable String table, @@ -104,6 +105,7 @@ public void hitKey( } } + @Override @RuleAction(label = "LCN Flicker Output", description = "Let a dimmer output flicker for a given count of flashes") public void flickerOutput( @ActionInput(name = "output", type = "java.lang.Integer", required = true, label = "Output", description = "The output number (1-4)") int output, @@ -117,6 +119,7 @@ public void flickerOutput( } } + @Override @RuleAction(label = "LCN Dynamic Text", description = "Send custom text to an LCN-GTxD display") public void sendDynamicText( @ActionInput(name = "row", type = "java.lang.Integer", required = true, label = "Row", description = "Display the text on the LCN-GTxD in the given row number (1-4)") int row, @@ -154,16 +157,16 @@ public void sendDynamicText( } } - private static LcnModuleActions invokeMethodOf(@Nullable ThingActions actions) { + private static ILcnModuleActions invokeMethodOf(@Nullable ThingActions actions) { if (actions == null) { throw new IllegalArgumentException("actions cannot be null"); } if (actions.getClass().getName().equals(LcnModuleActions.class.getName())) { if (actions instanceof LcnModuleActions) { - return (LcnModuleActions) actions; + return (ILcnModuleActions) actions; } else { - return (LcnModuleActions) Proxy.newProxyInstance(LcnModuleActions.class.getClassLoader(), - new Class[] { LcnModuleActions.class }, (Object proxy, Method method, Object[] args) -> { + return (ILcnModuleActions) Proxy.newProxyInstance(ILcnModuleActions.class.getClassLoader(), + new Class[] { ILcnModuleActions.class }, (Object proxy, Method method, Object[] args) -> { Method m = actions.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes()); return m.invoke(actions, args); diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java index 5f63d8a36d224..ebf7d3c4e946e 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java @@ -50,6 +50,8 @@ public class PckGatewayHandler extends BaseBridgeHandler { private final Logger logger = LoggerFactory.getLogger(PckGatewayHandler.class); private @Nullable Connection connection; private Optional> pckListener = Optional.empty(); + @Nullable + private PckGatewayConfiguration config; public PckGatewayHandler(Bridge bridge) { super(bridge); @@ -62,13 +64,13 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public synchronized void initialize() { - PckGatewayConfiguration config = getConfigAs(PckGatewayConfiguration.class); + PckGatewayConfiguration localConfig = config = getConfigAs(PckGatewayConfiguration.class); - String errorMessage = "Could not connect to LCN-PCHK/PKE: " + config.getHostname() + ": "; + String errorMessage = "Could not connect to LCN-PCHK/PKE: " + localConfig.getHostname() + ": "; try { OutputPortDimMode dimMode; - String mode = config.getMode(); + String mode = localConfig.getMode(); if (LcnDefs.OutputPortDimMode.NATIVE50.name().equalsIgnoreCase(mode)) { dimMode = LcnDefs.OutputPortDimMode.NATIVE50; } else if (LcnDefs.OutputPortDimMode.NATIVE200.name().equalsIgnoreCase(mode)) { @@ -77,9 +79,9 @@ public synchronized void initialize() { throw new LcnException("DimMode " + mode + " is not supported"); } - ConnectionSettings settings = new ConnectionSettings("0", config.getHostname(), config.getPort(), - config.getUsername(), config.getPassword(), dimMode, LcnDefs.OutputPortStatusMode.PERCENT, - config.getTimeoutMs()); + ConnectionSettings settings = new ConnectionSettings("0", localConfig.getHostname(), localConfig.getPort(), + localConfig.getUsername(), localConfig.getPassword(), dimMode, LcnDefs.OutputPortStatusMode.PERCENT, + localConfig.getTimeoutMs()); connection = new Connection(settings, scheduler, new ConnectionCallback() { @Override @@ -296,6 +298,7 @@ public void dispose() { * @return the timeout in ms */ public long getTimeoutMs() { - return getConfigAs(PckGatewayConfiguration.class).getTimeoutMs(); + PckGatewayConfiguration localConfig = config; + return localConfig != null ? localConfig.getTimeoutMs() : 3500; } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java index 334392976c028..c32573988d5ee 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/common/Variable.java @@ -66,7 +66,7 @@ public enum Variable { S0INPUT4(3, Type.S0INPUT, LcnChannelGroup.S0INPUT); // LCN-BU4L private final int number; - private Optional thresholdNumber = Optional.empty(); + private final Optional thresholdNumber; private final Type type; private final LcnChannelGroup channelGroup; @@ -82,14 +82,18 @@ public enum Type { } Variable(int number, Type type, LcnChannelGroup channelGroup) { - this.number = number; - this.type = type; - this.channelGroup = channelGroup; + this(number, Optional.empty(), type, channelGroup); } Variable(int number, int thresholdNumber, Type type, LcnChannelGroup channelGroup) { - this(number, type, channelGroup); - this.thresholdNumber = Optional.of(thresholdNumber); + this(number, Optional.of(thresholdNumber), type, channelGroup); + } + + Variable(int number, Optional thresholdNumber, Type type, LcnChannelGroup channelGroup) { + this.number = number; + this.type = type; + this.channelGroup = channelGroup; + this.thresholdNumber = thresholdNumber; } /** diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java index a5759e668eba7..008772aa0b55b 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java @@ -54,7 +54,9 @@ public AbstractConnectionState(StateContext context, ScheduledExecutorService sc * @param wantsAck true, if the module shall respond with an Ack upon successful processing * @param data the PCK message to be sent */ - public abstract void queue(LcnAddr addr, boolean wantsAck, byte[] data); + public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { + connection.queueOffline(addr, wantsAck, data); + } /** * Shuts the Connection down finally. A shut-down connection cannot re-used. diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java index fe594d055cb67..9c6fa2083f88f 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java @@ -16,7 +16,6 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.lcn.internal.common.LcnAddr; import org.openhab.binding.lcn.internal.common.LcnException; /** @@ -47,9 +46,4 @@ protected void startTimeoutTimer() { new LcnException("Network timeout in state " + getClass().getSimpleName())), connection.getSettings().getTimeout(), TimeUnit.MILLISECONDS)); } - - @Override - public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { - connection.queueOffline(addr, wantsAck, data); - } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java index f3886f411ddf9..3482f75345759 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java @@ -21,7 +21,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.lcn.internal.common.LcnAddr; import org.openhab.binding.lcn.internal.common.LcnException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -92,11 +91,6 @@ private void handleConnectionFailure(@Nullable Throwable e) { context.handleConnectionFailed(e); } - @Override - public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { - connection.queueOffline(addr, wantsAck, data); - } - @Override public void onPckMessageReceived(String data) { // nothing diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java index ecc5074fbe4b1..06c3b2bc8c6d7 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java @@ -16,7 +16,6 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.lcn.internal.common.LcnAddr; /** * This state is active when the connection failed. A grace period is enforced to prevent fast cycling through the @@ -40,11 +39,6 @@ public void startWorking() { TimeUnit.SECONDS)); } - @Override - public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { - connection.queueOffline(addr, wantsAck, data); - } - @Override public void onPckMessageReceived(String data) { // nothing diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java index b9c15ec91a467..9aa2e12e53784 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java @@ -15,7 +15,6 @@ import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.lcn.internal.common.LcnAddr; /** * This is the starting state of the {@link Connection} {@link StateMachine}. @@ -33,11 +32,6 @@ public void startWorking() { nextState(ConnectionStateConnecting.class); } - @Override - public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { - connection.queueOffline(addr, wantsAck, data); - } - @Override public void onPckMessageReceived(String data) { // nothing diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java index b886bb66dde8a..c723fd6c9bba6 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java @@ -18,7 +18,6 @@ import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.lcn.internal.common.LcnAddr; import org.openhab.binding.lcn.internal.common.LcnAddrGrp; import org.openhab.binding.lcn.internal.common.LcnException; import org.openhab.binding.lcn.internal.common.PckGenerator; @@ -66,11 +65,6 @@ private void update() { } } - @Override - public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { - connection.queueOffline(addr, wantsAck, data); - } - @Override public void onPckMessageReceived(String data) { Matcher matcher = PATTERN_SK_RESPONSE.matcher(data); diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java index 555858f307ec7..dad1f79d3c0ef 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java @@ -15,7 +15,6 @@ import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.lcn.internal.common.LcnAddr; import org.openhab.binding.lcn.internal.common.PckGenerator; /** @@ -37,11 +36,6 @@ public void startWorking() { nextState(ConnectionStateSegmentScan.class); } - @Override - public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { - connection.queueOffline(addr, wantsAck, data); - } - @Override public void onPckMessageReceived(String data) { // nothing diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java index 7ce5abb094ed6..e9979999a4492 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java @@ -18,7 +18,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.lcn.internal.common.LcnAddr; import org.openhab.binding.lcn.internal.common.LcnDefs; import org.openhab.binding.lcn.internal.common.LcnException; @@ -48,11 +47,6 @@ public void startWorking() { addTimer(localLegacyTimer); } - @Override - public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { - connection.queueOffline(addr, wantsAck, data); - } - @Override public void onPckMessageReceived(String data) { ScheduledFuture localLegacyTimer = legacyTimer; From 8d692ad7724548be4563c7b5309e1215ed73ca83 Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Wed, 10 Jun 2020 23:27:25 +0200 Subject: [PATCH 13/16] Incorporate review feedback #3 Signed-off-by: Fabian Wolter --- .../lcn/internal/LcnModuleHandler.java | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) 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 56f2829e1d055..32ea98c7e35b3 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 @@ -156,29 +156,29 @@ public void handleCommand(ChannelUID channelUid, Command command) { number.ifPresent(n -> subHandler.handleRefresh(channelGroup, n)); subHandler.handleRefresh(channelUid.getIdWithoutGroup()); } else if (command instanceof OnOffType) { - subHandler.handleCommandOnOff(castCommand(command), channelGroup, number.get()); + subHandler.handleCommandOnOff((OnOffType) command, channelGroup, number.get()); } else if (command instanceof DimmerOutputCommand) { - subHandler.handleCommandDimmerOutput(castCommand(command), number.get()); + subHandler.handleCommandDimmerOutput((DimmerOutputCommand) command, number.get()); } else if (command instanceof PercentType && number.isPresent()) { - subHandler.handleCommandPercent(castCommand(command), channelGroup, number.get()); + subHandler.handleCommandPercent((PercentType) command, channelGroup, number.get()); } else if (command instanceof HSBType) { - subHandler.handleCommandHsb(castCommand(command), channelUid.getIdWithoutGroup()); + subHandler.handleCommandHsb((HSBType) command, channelUid.getIdWithoutGroup()); } else if (command instanceof PercentType) { - subHandler.handleCommandPercent(castCommand(command), channelGroup, channelUid.getIdWithoutGroup()); + subHandler.handleCommandPercent((PercentType) command, channelGroup, channelUid.getIdWithoutGroup()); } else if (command instanceof StringType) { - subHandler.handleCommandString(castCommand(command), number.get()); + subHandler.handleCommandString((StringType) command, number.get()); } else if (command instanceof DecimalType) { - DecimalType decimalType = castCommand(command); + DecimalType decimalType = (DecimalType) command; DecimalType nativeValue = getConverter(channelUid).onCommandFromItem(decimalType.doubleValue()); subHandler.handleCommandDecimal(nativeValue, channelGroup, number.get()); } else if (command instanceof QuantityType) { - QuantityType quantityType = castCommand(command); + QuantityType quantityType = (QuantityType) command; DecimalType nativeValue = getConverter(channelUid).onCommandFromItem(quantityType); subHandler.handleCommandDecimal(nativeValue, channelGroup, number.get()); } else if (command instanceof UpDownType) { - subHandler.handleCommandUpDown(castCommand(command), channelGroup, number.get()); + subHandler.handleCommandUpDown((UpDownType) command, channelGroup, number.get()); } else if (command instanceof StopMoveType) { - subHandler.handleCommandStopMove(castCommand(command), channelGroup, number.get()); + subHandler.handleCommandStopMove((StopMoveType) command, channelGroup, number.get()); } else { throw new LcnException("Unsupported command type"); } @@ -193,23 +193,6 @@ private Converter getConverter(ChannelUID channelUid) { return converters.getOrDefault(channelUid, Converters.IDENTITY); } - /** - * Convenience method to cast a command. - * - * @param the concrete type to be casted to - * @param command the command to be casted - * @return the concrete command - * @throws LcnException when the command cannot be casted - */ - @SuppressWarnings("unchecked") - private T castCommand(Command command) throws LcnException { - try { - return (T) command; - } catch (ClassCastException e) { - throw new LcnException("Unexpected command type"); - } - } - /** * Invoked when a PCK messages arrives from the PCK gateway * From b66904ef02f89b7ecfb46e341c6a3aeb27e85c39 Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Wed, 10 Jun 2020 23:44:03 +0200 Subject: [PATCH 14/16] Incorporate review feedback No.4 Signed-off-by: Fabian Wolter --- .../binding/lcn/internal/LcnModuleDiscoveryService.java | 2 +- .../org/openhab/binding/lcn/internal/PckGatewayHandler.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) 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 ba1db4b67fe4f..8ab89ce32297b 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 @@ -98,8 +98,8 @@ public void setThingHandler(@Nullable ThingHandler handler) { @Override public void deactivate() { - super.deactivate(); stopScan(); + super.deactivate(); } @Override diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java index ebf7d3c4e946e..9d239c5936a02 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/PckGatewayHandler.java @@ -50,8 +50,7 @@ public class PckGatewayHandler extends BaseBridgeHandler { private final Logger logger = LoggerFactory.getLogger(PckGatewayHandler.class); private @Nullable Connection connection; private Optional> pckListener = Optional.empty(); - @Nullable - private PckGatewayConfiguration config; + private @Nullable PckGatewayConfiguration config; public PckGatewayHandler(Bridge bridge) { super(bridge); From 67c50cbdbf636b589880d1f623187c9033c2d9e8 Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Thu, 11 Jun 2020 00:00:36 +0200 Subject: [PATCH 15/16] Incorporate review feedback No.5 Signed-off-by: Fabian Wolter --- .../lcn/internal/LcnModuleActions.java | 3 +- .../binding/lcn/internal/common/LcnDefs.java | 5 +- .../lcn/internal/connection/Connection.java | 71 +++++++------------ .../lcn/internal/connection/ModInfo.java | 17 ++--- .../lcn/internal/connection/SendData.java | 7 +- .../lcn/internal/connection/SendDataPck.java | 2 +- .../lcn/internal/ModuleActionsTest.java | 19 +++-- 7 files changed, 46 insertions(+), 78 deletions(-) diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java index 8da7ea7067b5b..2dd7524a44f8f 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal; -import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.nio.ByteBuffer; @@ -152,7 +151,7 @@ public void sendDynamicText( getHandler().sendPck(command.array()); } - } catch (UnsupportedEncodingException | IllegalArgumentException | LcnException e) { + } catch (IllegalArgumentException | LcnException e) { logger.warn("Could not send dynamic text: {}", e.getMessage()); } } 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 3c628bfdb34f4..6ad123bc8affe 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 @@ -12,6 +12,9 @@ */ package org.openhab.binding.lcn.internal.common; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + import org.eclipse.jdt.annotation.NonNullByDefault; /** @@ -23,7 +26,7 @@ @NonNullByDefault public final class LcnDefs { /** Text encoding used by LCN-PCHK. */ - public static final String LCN_ENCODING = "UTF-8"; + public static final Charset LCN_ENCODING = StandardCharsets.UTF_8; /** Number of thresholds registers of an LCN module */ public static final int THRESHOLD_REGISTER_COUNT = 4; /** Number of key tables of an LCN module. */ diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java index db59821c68852..7266b2e19a6ef 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java @@ -14,7 +14,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; @@ -172,34 +171,28 @@ public void completed(@Nullable Integer transmittedByteCount, @Nullable Void att String msg = "Connection was closed by foreign host."; stateMachine.handleConnectionFailed(new LcnException(msg)); } else { - try { - // read data chunks from socket and separate frames - readBuffer.flip(); - int aPos = readBuffer.position(); // 0 - String s = new String(readBuffer.array(), aPos, transmittedByteCount, - LcnDefs.LCN_ENCODING); - int pos1 = 0, pos2 = s.indexOf(PckGenerator.TERMINATION, pos1); - while (pos2 != -1) { - String data = s.substring(pos1, pos2); - if (logger.isTraceEnabled()) { - logger.trace("Received: '{}'", data); - } - scheduler.submit(() -> { - stateMachine.onInputReceived(data); - callback.onPckMessageReceived(data); - }); - // Seek position in input array - aPos += s.substring(pos1, pos2 + 1).getBytes(LcnDefs.LCN_ENCODING).length; - // Next input - pos1 = pos2 + 1; - pos2 = s.indexOf(PckGenerator.TERMINATION, pos1); + // read data chunks from socket and separate frames + readBuffer.flip(); + int aPos = readBuffer.position(); // 0 + String s = new String(readBuffer.array(), aPos, transmittedByteCount, LcnDefs.LCN_ENCODING); + int pos1 = 0, pos2 = s.indexOf(PckGenerator.TERMINATION, pos1); + while (pos2 != -1) { + String data = s.substring(pos1, pos2); + if (logger.isTraceEnabled()) { + logger.trace("Received: '{}'", data); } - readBuffer.limit(readBuffer.capacity()); - readBuffer.position(transmittedByteCount - aPos); // Keeps fragments for the next call - } catch (UnsupportedEncodingException ex) { - logger.warn("Unable to decode input from channel \"{}\": {}", settings.getId(), - ex.getMessage()); + scheduler.submit(() -> { + stateMachine.onInputReceived(data); + callback.onPckMessageReceived(data); + }); + // Seek position in input array + aPos += s.substring(pos1, pos2 + 1).getBytes(LcnDefs.LCN_ENCODING).length; + // Next input + pos1 = pos2 + 1; + pos2 = s.indexOf(PckGenerator.TERMINATION, pos1); } + readBuffer.limit(readBuffer.capacity()); + readBuffer.position(transmittedByteCount - aPos); // Keeps fragments for the next call if (isSocketConnected()) { readAndProcess(); @@ -302,11 +295,7 @@ public void queueDirectlyPlainText(String plainText) { * @param pck the pure PCK command (without address header) */ void queueDirectly(LcnAddr addr, boolean wantsAck, String pck) { - try { - this.queueDirectly(addr, wantsAck, pck.getBytes(LcnDefs.LCN_ENCODING)); - } catch (UnsupportedEncodingException ex) { - logger.error("Failed to encode PCK command: {}", pck); - } + this.queueDirectly(addr, wantsAck, pck.getBytes(LcnDefs.LCN_ENCODING)); } /** @@ -361,11 +350,7 @@ void queueOffline(LcnAddr addr, boolean wantsAck, byte[] data) { * @param pck the pure PCK command (without address header) */ public void queue(LcnAddr addr, boolean wantsAck, String pck) { - try { - this.queue(addr, wantsAck, pck.getBytes(LcnDefs.LCN_ENCODING)); - } catch (UnsupportedEncodingException ex) { - logger.warn("Failed to encode PCK command: {}", pck); - } + this.queue(addr, wantsAck, pck.getBytes(LcnDefs.LCN_ENCODING)); } /** @@ -463,14 +448,10 @@ public void shutdown() { * Sends a broadcast to all LCN modules with a reuqest to respond with an Ack. */ public void sendModuleDiscoveryCommand() { - try { - queueAndSend(new SendDataPck(new LcnAddrGrp(BROADCAST_SEGMENT_ID, BROADCAST_MODULE_ID), true, - PckGenerator.nullCommand().getBytes(LcnDefs.LCN_ENCODING))); - queueAndSend(new SendDataPck(new LcnAddrGrp(0, BROADCAST_MODULE_ID), true, - PckGenerator.nullCommand().getBytes(LcnDefs.LCN_ENCODING))); - } catch (UnsupportedEncodingException e) { - logger.warn("Could not send discovery request: {}", e.getMessage()); - } + queueAndSend(new SendDataPck(new LcnAddrGrp(BROADCAST_SEGMENT_ID, BROADCAST_MODULE_ID), true, + PckGenerator.nullCommand().getBytes(LcnDefs.LCN_ENCODING))); + queueAndSend(new SendDataPck(new LcnAddrGrp(0, BROADCAST_MODULE_ID), true, + PckGenerator.nullCommand().getBytes(LcnDefs.LCN_ENCODING))); } /** diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java index b8cecd733fa0f..a7799240c2e3f 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ModInfo.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -199,12 +198,8 @@ private boolean tryProcessNextCommandWithAck(Connection conn, long timeoutMSec, this.requestCurrentPckCommandWithAck.reset(); if (failedCommand != null) { - try { - logger.warn("{}: Module did not respond to command: {}", addr, - new String(failedCommand, LcnDefs.LCN_ENCODING)); - } catch (UnsupportedEncodingException e) { - // ignore - } + logger.warn("{}: Module did not respond to command: {}", addr, + new String(failedCommand, LcnDefs.LCN_ENCODING)); } } // Peek new command @@ -221,12 +216,8 @@ private boolean tryProcessNextCommandWithAck(Connection conn, long timeoutMSec, this.requestCurrentPckCommandWithAck.onRequestSent(currTime); } } catch (LcnException e) { - try { - logger.warn("{}: Could not send command: {}: {}", addr, new String(command, LcnDefs.LCN_ENCODING), - e.getMessage()); - } catch (UnsupportedEncodingException e1) { - // ignore - } + logger.warn("{}: Could not send command: {}: {}", addr, new String(command, LcnDefs.LCN_ENCODING), + e.getMessage()); } return true; } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendData.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendData.java index 62aadec36b2dc..a271f6a7902c8 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendData.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendData.java @@ -14,8 +14,6 @@ import java.io.IOException; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.nio.BufferOverflowException; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -34,10 +32,7 @@ public abstract class SendData { * @param buffer the target buffer * @param localSegId the local segment id * @return true if everything was set-up correctly and data was written - * @throws UnsupportedEncodingException if text could not be encoded for LCN-PCHK - * @throws BufferOverflowException if target buffer has not enough space left (buffer will not be altered) * @throws IOException if an I/O error occurs */ - abstract boolean write(OutputStream buffer, int localSegId) - throws UnsupportedEncodingException, BufferOverflowException, IOException; + abstract boolean write(OutputStream buffer, int localSegId) throws IOException; } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPck.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPck.java index e517d33191b8f..f04b9688d8489 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPck.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/SendDataPck.java @@ -72,6 +72,6 @@ boolean write(OutputStream buffer, int localSegId) throws BufferOverflowExceptio @Override public String toString() { - return "Addr: " + addr + ": " + new String(data, 0, data.length); + return "Addr: " + addr + ": " + new String(data, 0, data.length, LcnDefs.LCN_ENCODING); } } diff --git a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/ModuleActionsTest.java b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/ModuleActionsTest.java index 747bf3079cb69..2867607cc6d90 100644 --- a/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/ModuleActionsTest.java +++ b/bundles/org.openhab.binding.lcn/src/test/java/org/openhab/binding/lcn/internal/ModuleActionsTest.java @@ -17,7 +17,6 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; -import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -48,26 +47,26 @@ public void setUp() { a.setThingHandler(handler); } - private byte[] stringToByteBuffer(String string) throws UnsupportedEncodingException { + private byte[] stringToByteBuffer(String string) { return string.getBytes(LcnDefs.LCN_ENCODING); } @Test - public void testSendDynamicText1CharRow1() throws LcnException, UnsupportedEncodingException { + public void testSendDynamicText1CharRow1() throws LcnException { a.sendDynamicText(1, "a"); verify(handler).sendPck(stringToByteBuffer("GTDT11a\0\0\0\0\0\0\0\0\0\0\0")); } @Test - public void testSendDynamicText1ChunkRow1() throws LcnException, UnsupportedEncodingException { + public void testSendDynamicText1ChunkRow1() throws LcnException { a.sendDynamicText(1, "abcdfghijklm"); verify(handler).sendPck(stringToByteBuffer("GTDT11abcdfghijklm")); } @Test - public void testSendDynamicText1Chunk1CharRow1() throws LcnException, UnsupportedEncodingException { + public void testSendDynamicText1Chunk1CharRow1() throws LcnException { a.sendDynamicText(1, "abcdfghijklmn"); verify(handler, times(2)).sendPck(byteBufferCaptor.capture()); @@ -77,7 +76,7 @@ public void testSendDynamicText1Chunk1CharRow1() throws LcnException, Unsupporte } @Test - public void testSendDynamicText5ChunksRow1() throws LcnException, UnsupportedEncodingException { + public void testSendDynamicText5ChunksRow1() throws LcnException { a.sendDynamicText(1, "abcdfghijklmnopqrstuvwxyzabcdfghijklmnopqrstuvwxyzabcdfghijk"); verify(handler, times(5)).sendPck(byteBufferCaptor.capture()); @@ -89,7 +88,7 @@ public void testSendDynamicText5ChunksRow1() throws LcnException, UnsupportedEnc } @Test - public void testSendDynamicText5Chunks1CharRow1Truncated() throws LcnException, UnsupportedEncodingException { + public void testSendDynamicText5Chunks1CharRow1Truncated() throws LcnException { a.sendDynamicText(1, "abcdfghijklmnopqrstuvwxyzabcdfghijklmnopqrstuvwxyzabcdfghijkl"); verify(handler, times(5)).sendPck(byteBufferCaptor.capture()); @@ -101,7 +100,7 @@ public void testSendDynamicText5Chunks1CharRow1Truncated() throws LcnException, } @Test - public void testSendDynamicText5Chunks1UmlautRow1Truncated() throws LcnException, UnsupportedEncodingException { + public void testSendDynamicText5Chunks1UmlautRow1Truncated() throws LcnException { a.sendDynamicText(1, "äcdfghijklmnopqrstuvwxyzabcdfghijklmnopqrstuvwxyzabcdfghijkl"); verify(handler, times(5)).sendPck(byteBufferCaptor.capture()); @@ -113,7 +112,7 @@ public void testSendDynamicText5Chunks1UmlautRow1Truncated() throws LcnException } @Test - public void testSendDynamicTextRow4() throws LcnException, UnsupportedEncodingException { + public void testSendDynamicTextRow4() throws LcnException { a.sendDynamicText(4, "abcdfghijklmn"); verify(handler, times(2)).sendPck(byteBufferCaptor.capture()); @@ -123,7 +122,7 @@ public void testSendDynamicTextRow4() throws LcnException, UnsupportedEncodingEx } @Test - public void testSendDynamicTextSplitInCharacter() throws LcnException, UnsupportedEncodingException { + public void testSendDynamicTextSplitInCharacter() throws LcnException { a.sendDynamicText(4, "Test 123 öäüß"); verify(handler, times(2)).sendPck(byteBufferCaptor.capture()); From 2024f87f82fafa8f69eb3e84fe885d3b062c1f32 Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Sat, 13 Jun 2020 18:03:27 +0200 Subject: [PATCH 16/16] Incorporate review feedback No.6 Signed-off-by: Fabian Wolter --- .../connection/AbstractConnectionState.java | 22 +++-- ...bstractConnectionStateSendCredentials.java | 11 ++- .../internal/connection/AbstractState.java | 34 +++++--- .../connection/AbstractStateMachine.java | 64 ++++++++++++++ .../lcn/internal/connection/Connection.java | 19 ++--- .../connection/ConnectionStateConnected.java | 9 +- .../connection/ConnectionStateConnecting.java | 7 +- ...ectionStateGracePeriodBeforeReconnect.java | 7 +- .../connection/ConnectionStateInit.java | 10 +-- ...chine.java => ConnectionStateMachine.java} | 85 +++++++++---------- .../ConnectionStateSegmentScan.java | 11 ++- .../ConnectionStateSendDimMode.java | 8 +- .../ConnectionStateSendPassword.java | 8 +- .../ConnectionStateSendUsername.java | 8 +- .../connection/ConnectionStateShutdown.java | 6 +- ...ConnectionStateWaitForLcnBusConnected.java | 11 ++- ...itForLcnBusConnectedAfterDisconnected.java | 7 +- .../lcn/internal/connection/StateContext.java | 64 -------------- .../LcnPchkDiscoveryService.java | 54 ++++++------ 19 files changed, 216 insertions(+), 229 deletions(-) create mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractStateMachine.java rename bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/{StateMachine.java => ConnectionStateMachine.java} (50%) delete mode 100644 bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateContext.java diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java index 008772aa0b55b..13d001868090d 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionState.java @@ -21,21 +21,18 @@ import org.openhab.binding.lcn.internal.common.LcnDefs; /** - * Base class for representing LCN-PCK gateway connection states + * Base class representing LCN-PCK gateway connection states. * * @author Fabian Wolter - Initial Contribution */ @NonNullByDefault -public abstract class AbstractConnectionState extends AbstractState { +public abstract class AbstractConnectionState extends AbstractState { /** The PCK gateway's Connection */ protected final Connection connection; - /** An openHAB scheduler */ - protected final ScheduledExecutorService scheduler; - public AbstractConnectionState(StateContext context, ScheduledExecutorService scheduler) { + public AbstractConnectionState(ConnectionStateMachine context) { super(context); this.connection = context.getConnection(); - this.scheduler = scheduler; } /** @@ -45,6 +42,15 @@ public AbstractConnectionState(StateContext context, ScheduledExecutorService sc */ public abstract void onPckMessageReceived(String data); + /** + * Gets the framework's scheduler. + * + * @return the scheduler + */ + public ScheduledExecutorService getScheduler() { + return context.getScheduler(); + } + /** * Enqueues a PCK message to be sent. When the connection is offline, the message will be buffered and sent when the * connection is established. When the enqueued PCK message is too old, it will be discarded before a new connection @@ -62,7 +68,7 @@ public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { * Shuts the Connection down finally. A shut-down connection cannot re-used. */ public void shutdownFinally() { - nextState(ConnectionStateShutdown.class); + nextState(ConnectionStateShutdown::new); } /** @@ -74,7 +80,7 @@ public void shutdownFinally() { protected void parseLcnBusDiconnectMessage(String pck) { if (pck.equals(LcnDefs.LCNCONNSTATE_DISCONNECTED)) { connection.getCallback().onOffline("LCN bus not connected to LCN-PCHK/PKE"); - nextState(ConnectionStateWaitForLcnBusConnectedAfterDisconnected.class); + nextState(ConnectionStateWaitForLcnBusConnectedAfterDisconnected::new); } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java index 9c6fa2083f88f..4037bf47e29ca 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractConnectionStateSendCredentials.java @@ -12,14 +12,13 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lcn.internal.common.LcnException; /** - * Base class for sends username or password. + * Base class for sending username or password. * * @author Fabian Wolter - Initial Contribution */ @@ -27,13 +26,13 @@ public abstract class AbstractConnectionStateSendCredentials extends AbstractConnectionState { private static final int AUTH_TIMEOUT_SEC = 10; - public AbstractConnectionStateSendCredentials(StateContext context, ScheduledExecutorService scheduler) { - super(context, scheduler); + public AbstractConnectionStateSendCredentials(ConnectionStateMachine context) { + super(context); } @Override public void startWorking() { - addTimer(scheduler.schedule(() -> nextState(ConnectionStateConnecting.class), AUTH_TIMEOUT_SEC, + addTimer(getScheduler().schedule(() -> nextState(ConnectionStateConnecting::new), AUTH_TIMEOUT_SEC, TimeUnit.SECONDS)); } @@ -41,7 +40,7 @@ public void startWorking() { * Starts a timeout when the PCK gateway does not answer to the credentials. */ protected void startTimeoutTimer() { - addTimer(scheduler.schedule( + addTimer(getScheduler().schedule( () -> context.handleConnectionFailed( new LcnException("Network timeout in state " + getClass().getSimpleName())), connection.getSettings().getTimeout(), TimeUnit.MILLISECONDS)); diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java index 591b3505582a5..0b8bbd5a5205b 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractState.java @@ -16,55 +16,61 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.ScheduledFuture; +import java.util.function.Function; import org.eclipse.jdt.annotation.NonNullByDefault; /** - * Base class for usage for states with {@link StateMachine}. + * Base class for all states used with {@link AbstractStateMachine}. + * + * @param type of the state machine implementation + * @param type of the state implementation * * @author Fabian Wolter - Initial Contribution */ @NonNullByDefault -public abstract class AbstractState { +public abstract class AbstractState, U extends AbstractState> { private final List> usedTimers = Collections.synchronizedList(new ArrayList<>()); - protected final StateContext context; + protected final T context; - public AbstractState(StateContext context) { + public AbstractState(T context) { this.context = context; } /** - * Must be invoked when the State shall start its actions. + * Invoked when the State shall start its operation. */ - abstract void startWorking(); + protected abstract void startWorking(); /** * Stops all timers, the State has been started. */ - void cancelAllTimers() { + protected void cancelAllTimers() { synchronized (usedTimers) { usedTimers.forEach(t -> t.cancel(true)); } } /** - * When a state starts a timer, its ScheduledFuture must be added by this method. All timers added by this method, - * are canceled when the StateMachine leaves this State. + * When a state starts a timer, its ScheduledFuture must be registered by this method. All timers added by this + * method, are canceled when the StateMachine leaves this State. * * @param timer the new timer */ - void addTimer(ScheduledFuture timer) { + protected void addTimer(ScheduledFuture timer) { usedTimers.add(timer); } /** * Sets a new State. The current state is torn down gracefully. * - * @param newStateClass the class of the new State + * @param newStateFactory the lambda returning the new State */ - synchronized void nextState(Class newStateClass) { - if (context.isStateActive(this)) { - context.setState(newStateClass); + protected void nextState(Function newStateFactory) { + synchronized (context) { + if (context.isStateActive(this)) { + context.setState(newStateFactory); + } } } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractStateMachine.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractStateMachine.java new file mode 100644 index 0000000000000..fbf44830c440e --- /dev/null +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/AbstractStateMachine.java @@ -0,0 +1,64 @@ +/** + * 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.connection; + +import java.util.function.Function; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Base class for state machines. + * + * @param type of the state machine implementation + * @param type of the state implementation + * + * @author Fabian Wolter - Initial Contribution + */ +@NonNullByDefault +public abstract class AbstractStateMachine, U extends AbstractState> { + private final Logger logger = LoggerFactory.getLogger(AbstractStateMachine.class); + /** The StateMachine's current state */ + protected @Nullable volatile U state; + + /** + * Sets the current state. + * + * @param newStateFactory the new state's factory + */ + protected synchronized void setState(Function newStateFactory) { + @Nullable + U localState = state; + if (localState != null) { + localState.cancelAllTimers(); + } + + @SuppressWarnings("unchecked") + U newState = newStateFactory.apply((T) this); + + if (localState != null) { + logger.debug("Changing state {} -> {}", localState.getClass().getSimpleName(), + newState.getClass().getSimpleName()); + } + + state = newState; + + state.startWorking(); + } + + protected boolean isStateActive(AbstractState otherState) { + return state == otherState; // compare by identity + } +} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java index 7266b2e19a6ef..f89a7051c1e8c 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/Connection.java @@ -74,7 +74,7 @@ public class Connection { private final Map modData = Collections.synchronizedMap(new HashMap<>()); private volatile boolean writeInProgress; private final ScheduledExecutorService scheduler; - private final StateMachine stateMachine; + private final ConnectionStateMachine connectionStateMachine; /** * Constructs a clean (disconnected) connection with the given settings. @@ -90,8 +90,7 @@ public Connection(ConnectionSettings sets, ScheduledExecutorService scheduler, C this.scheduler = scheduler; this.clearRuntimeData(); - stateMachine = new StateMachine(this, scheduler); - stateMachine.startWorking(); + connectionStateMachine = new ConnectionStateMachine(this, scheduler); } /** Clears all runtime data. */ @@ -169,7 +168,7 @@ public void completed(@Nullable Integer transmittedByteCount, @Nullable Void att synchronized (Connection.this) { if (transmittedByteCount == null || transmittedByteCount == -1) { String msg = "Connection was closed by foreign host."; - stateMachine.handleConnectionFailed(new LcnException(msg)); + connectionStateMachine.handleConnectionFailed(new LcnException(msg)); } else { // read data chunks from socket and separate frames readBuffer.flip(); @@ -182,7 +181,7 @@ public void completed(@Nullable Integer transmittedByteCount, @Nullable Void att logger.trace("Received: '{}'", data); } scheduler.submit(() -> { - stateMachine.onInputReceived(data); + connectionStateMachine.onInputReceived(data); callback.onPckMessageReceived(data); }); // Seek position in input array @@ -204,11 +203,11 @@ public void completed(@Nullable Integer transmittedByteCount, @Nullable Void att @Override public void failed(@Nullable Throwable e, @Nullable Void attachment) { logger.debug("Lost connection"); - stateMachine.handleConnectionFailed(e); + connectionStateMachine.handleConnectionFailed(e); } }); } else { - stateMachine.handleConnectionFailed(new LcnException("Socket not open")); + connectionStateMachine.handleConnectionFailed(new LcnException("Socket not open")); } } @@ -267,7 +266,7 @@ public void failed(@Nullable Throwable exc, @Nullable Void attachment) { exc.getMessage()); } writeInProgress = false; - stateMachine.handleConnectionFailed(new LcnException("write() failed")); + connectionStateMachine.handleConnectionFailed(new LcnException("write() failed")); } } }); @@ -363,7 +362,7 @@ public void queue(LcnAddr addr, boolean wantsAck, String pck) { * @param pck the pure PCK command (without address header) */ public void queue(LcnAddr addr, boolean wantsAck, byte[] pck) { - stateMachine.queue(addr, wantsAck, pck); + connectionStateMachine.queue(addr, wantsAck, pck); } /** @@ -441,7 +440,7 @@ public void removeLcnModule(LcnAddr addr) { * Invoked when this Connection shall be shut-down finally. */ public void shutdown() { - stateMachine.shutdownFinally(); + connectionStateMachine.shutdownFinally(); } /** diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnected.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnected.java index b54c58bc34da6..55acca37974b0 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnected.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnected.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -30,19 +29,19 @@ public class ConnectionStateConnected extends AbstractConnectionState { private static final int PING_INTERVAL_SEC = 60; private int pingCounter; - public ConnectionStateConnected(StateContext context, ScheduledExecutorService scheduler) { - super(context, scheduler); + public ConnectionStateConnected(ConnectionStateMachine context) { + super(context); } @Override public void startWorking() { // send periodic keep-alives to keep the connection open - addTimer(scheduler.scheduleWithFixedDelay( + addTimer(getScheduler().scheduleWithFixedDelay( () -> connection.queueDirectlyPlainText(PckGenerator.ping(++pingCounter)), PING_INTERVAL_SEC, PING_INTERVAL_SEC, TimeUnit.SECONDS)); // run ModInfo.update() for every LCN module - addTimer(scheduler.scheduleWithFixedDelay(connection::updateModInfos, 0, 1, TimeUnit.SECONDS)); + addTimer(getScheduler().scheduleWithFixedDelay(connection::updateModInfos, 0, 1, TimeUnit.SECONDS)); connection.sendOfflineQueue(); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java index 3482f75345759..35f54d8b22795 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateConnecting.java @@ -17,7 +17,6 @@ import java.net.StandardSocketOptions; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; -import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -35,8 +34,8 @@ public class ConnectionStateConnecting extends AbstractConnectionState { private final Logger logger = LoggerFactory.getLogger(ConnectionStateConnecting.class); - public ConnectionStateConnecting(StateContext context, ScheduledExecutorService scheduler) { - super(context, scheduler); + public ConnectionStateConnecting(ConnectionStateMachine context) { + super(context); } @Override @@ -65,7 +64,7 @@ public void startWorking() { @Override public void completed(@Nullable Void result, @Nullable Void attachment) { connection.readAndProcess(); - nextState(ConnectionStateSendUsername.class); + nextState(ConnectionStateSendUsername::new); } @Override diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java index 06c3b2bc8c6d7..e6d05cba3ff49 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateGracePeriodBeforeReconnect.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -27,15 +26,15 @@ public class ConnectionStateGracePeriodBeforeReconnect extends AbstractConnectionState { private static final int RECONNECT_GRACE_PERIOD_SEC = 5; - public ConnectionStateGracePeriodBeforeReconnect(StateContext context, ScheduledExecutorService scheduler) { - super(context, scheduler); + public ConnectionStateGracePeriodBeforeReconnect(ConnectionStateMachine context) { + super(context); } @Override public void startWorking() { closeSocketChannel(); - addTimer(scheduler.schedule(() -> nextState(ConnectionStateConnecting.class), RECONNECT_GRACE_PERIOD_SEC, + addTimer(getScheduler().schedule(() -> nextState(ConnectionStateConnecting::new), RECONNECT_GRACE_PERIOD_SEC, TimeUnit.SECONDS)); } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java index 9aa2e12e53784..a4c3790ee96a2 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateInit.java @@ -12,24 +12,22 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.util.concurrent.ScheduledExecutorService; - import org.eclipse.jdt.annotation.NonNullByDefault; /** - * This is the starting state of the {@link Connection} {@link StateMachine}. + * This is the initial state of the {@link ConnectionStateMachine}. * * @author Fabian Wolter - Initial Contribution */ @NonNullByDefault public class ConnectionStateInit extends AbstractConnectionState { - public ConnectionStateInit(StateContext context, ScheduledExecutorService scheduler) { - super(context, scheduler); + public ConnectionStateInit(ConnectionStateMachine context) { + super(context); } @Override public void startWorking() { - nextState(ConnectionStateConnecting.class); + nextState(ConnectionStateConnecting::new); } @Override diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateMachine.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateMachine.java similarity index 50% rename from bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateMachine.java rename to bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateMachine.java index ae7d91d8ef897..9e83f7ffeaa3b 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateMachine.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateMachine.java @@ -12,14 +12,11 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.lang.reflect.InvocationTargetException; import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.lcn.internal.common.LcnAddr; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Implements a state machine for managing the connection to the LCN-PCK gateway. Setting states is thread-safe. @@ -27,53 +24,54 @@ * @author Fabian Wolter - Initial Contribution */ @NonNullByDefault -public class StateMachine implements StateContext { - private final Logger logger = LoggerFactory.getLogger(StateMachine.class); - /** The StateMachine's current state */ - protected volatile AbstractConnectionState state; +public class ConnectionStateMachine extends AbstractStateMachine { private final Connection connection; - private final ScheduledExecutorService scheduler; + final ScheduledExecutorService scheduler; - public StateMachine(Connection connection, ScheduledExecutorService scheduler) { + public ConnectionStateMachine(Connection connection, ScheduledExecutorService scheduler) { this.connection = connection; this.scheduler = scheduler; - this.state = new ConnectionStateInit(this, scheduler); - } - - @Override - public synchronized void setState(Class newStateClass) { - logger.debug("Changing state {} -> {}", state.getClass().getSimpleName(), newStateClass.getSimpleName()); - - state.cancelAllTimers(); - try { - state = newStateClass.getDeclaredConstructor(StateContext.class, ScheduledExecutorService.class) - .newInstance(this, scheduler); - state.startWorking(); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException - | NoSuchMethodException | SecurityException e) { - logger.warn("Could not change state: {}", e.getMessage()); - } + setState(ConnectionStateInit::new); } /** - * Starts the operation of the StateMachine by starting the initial State. + * Gets the framework's scheduler. + * + * @return the scheduler */ - public void startWorking() { - state.startWorking(); + protected ScheduledExecutorService getScheduler() { + return scheduler; } - @Override - public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { - state.queue(addr, wantsAck, data); + /** + * Gets the PCHK Connection object. + * + * @return the connection + */ + public Connection getConnection() { + return connection; } - @Override - public boolean isStateActive(AbstractState otherState) { - return state == otherState; // compare by identity + /** + * Enqueues a PCK command. Implementation is state dependent. + * + * @param addr the destination address + * @param wantsAck true, if the module shall respond with an Ack + * @param data the data + */ + public void queue(LcnAddr addr, boolean wantsAck, byte[] data) { + AbstractConnectionState localState = state; + if (localState != null) { + localState.queue(addr, wantsAck, data); + } } - @Override + /** + * Invoked by any state, if the connection fails. + * + * @param e the cause + */ public void handleConnectionFailed(@Nullable Throwable e) { if (!(state instanceof ConnectionStateShutdown)) { if (e != null) { @@ -81,28 +79,29 @@ public void handleConnectionFailed(@Nullable Throwable e) { } else { connection.getCallback().onOffline(""); } - setState(ConnectionStateGracePeriodBeforeReconnect.class); + setState(ConnectionStateGracePeriodBeforeReconnect::new); } } - @Override - public Connection getConnection() { - return connection; - } - /** * Processes a received PCK message by passing it to the current State. * * @param data the PCK message */ public void onInputReceived(String data) { - state.onPckMessageReceived(data); + AbstractConnectionState localState = state; + if (localState != null) { + localState.onPckMessageReceived(data); + } } /** * Shuts the StateMachine down finally. A shut-down StateMachine cannot be re-used. */ public void shutdownFinally() { - state.shutdownFinally(); + AbstractConnectionState localState = state; + if (localState != null) { + localState.shutdownFinally(); + } } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java index c723fd6c9bba6..0942f777d85b9 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSegmentScan.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -40,14 +39,14 @@ public class ConnectionStateSegmentScan extends AbstractConnectionState { .compile("=M(?\\d{3})(?\\d{3})\\.SK(?\\d+)"); private final RequestStatus statusSegmentScan = new RequestStatus(-1, 3, "Segment Scan"); - public ConnectionStateSegmentScan(StateContext context, ScheduledExecutorService scheduler) { - super(context, scheduler); + public ConnectionStateSegmentScan(ConnectionStateMachine context) { + super(context); } @Override public void startWorking() { statusSegmentScan.refresh(); - addTimer(scheduler.scheduleWithFixedDelay(this::update, 0, 500, TimeUnit.MILLISECONDS)); + addTimer(getScheduler().scheduleWithFixedDelay(this::update, 0, 500, TimeUnit.MILLISECONDS)); } private void update() { @@ -61,7 +60,7 @@ private void update() { // Give up. Probably no segments available. connection.setLocalSegId(0); logger.debug("No segment couplers detected"); - nextState(ConnectionStateConnected.class); + nextState(ConnectionStateConnected::new); } } @@ -75,7 +74,7 @@ public void onPckMessageReceived(String data) { // local segment coupler answered connection.setLocalSegId(Integer.parseInt(matcher.group("id"))); logger.debug("Local segment ID is {}", connection.getLocalSegId()); - nextState(ConnectionStateConnected.class); + nextState(ConnectionStateConnected::new); } } parseLcnBusDiconnectMessage(data); diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java index dad1f79d3c0ef..bfcf89f37a766 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendDimMode.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.util.concurrent.ScheduledExecutorService; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lcn.internal.common.PckGenerator; @@ -24,8 +22,8 @@ */ @NonNullByDefault public class ConnectionStateSendDimMode extends AbstractConnectionState { - public ConnectionStateSendDimMode(StateContext context, ScheduledExecutorService scheduler) { - super(context, scheduler); + public ConnectionStateSendDimMode(ConnectionStateMachine context) { + super(context); } @Override @@ -33,7 +31,7 @@ public void startWorking() { connection.queueDirectlyPlainText(PckGenerator.setOperationMode(connection.getSettings().getDimMode(), connection.getSettings().getStatusMode())); - nextState(ConnectionStateSegmentScan.class); + nextState(ConnectionStateSegmentScan::new); } @Override diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendPassword.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendPassword.java index 5a18071bec7db..01ae13bd3fe72 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendPassword.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendPassword.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.util.concurrent.ScheduledExecutorService; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lcn.internal.common.LcnDefs; @@ -24,8 +22,8 @@ */ @NonNullByDefault public class ConnectionStateSendPassword extends AbstractConnectionStateSendCredentials { - public ConnectionStateSendPassword(StateContext context, ScheduledExecutorService scheduler) { - super(context, scheduler); + public ConnectionStateSendPassword(ConnectionStateMachine context) { + super(context); } @Override @@ -37,7 +35,7 @@ public void startWorking() { public void onPckMessageReceived(String data) { if (data.equals(LcnDefs.AUTH_PASSWORD)) { connection.queueDirectlyPlainText(connection.getSettings().getPassword()); - nextState(ConnectionStateWaitForLcnBusConnected.class); + nextState(ConnectionStateWaitForLcnBusConnected::new); } } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendUsername.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendUsername.java index 0999f088fe48c..a04f1d341a3c3 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendUsername.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateSendUsername.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.util.concurrent.ScheduledExecutorService; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lcn.internal.common.LcnDefs; @@ -24,8 +22,8 @@ */ @NonNullByDefault public class ConnectionStateSendUsername extends AbstractConnectionStateSendCredentials { - public ConnectionStateSendUsername(StateContext context, ScheduledExecutorService scheduler) { - super(context, scheduler); + public ConnectionStateSendUsername(ConnectionStateMachine context) { + super(context); } @Override @@ -37,7 +35,7 @@ public void startWorking() { public void onPckMessageReceived(String data) { if (data.equals(LcnDefs.AUTH_USERNAME)) { connection.queueDirectlyPlainText(connection.getSettings().getUsername()); - nextState(ConnectionStateSendPassword.class); + nextState(ConnectionStateSendPassword::new); } } } diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateShutdown.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateShutdown.java index d00ce0bd12189..67c7ff2100e25 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateShutdown.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateShutdown.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.util.concurrent.ScheduledExecutorService; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.lcn.internal.common.LcnAddr; @@ -24,8 +22,8 @@ */ @NonNullByDefault public class ConnectionStateShutdown extends AbstractConnectionState { - public ConnectionStateShutdown(StateContext context, ScheduledExecutorService scheduler) { - super(context, scheduler); + public ConnectionStateShutdown(ConnectionStateMachine context) { + super(context); } @Override diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java index e9979999a4492..8882042cebed4 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnected.java @@ -12,7 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -31,8 +30,8 @@ public class ConnectionStateWaitForLcnBusConnected extends AbstractConnectionState { private @Nullable ScheduledFuture legacyTimer; - public ConnectionStateWaitForLcnBusConnected(StateContext context, ScheduledExecutorService scheduler) { - super(context, scheduler); + public ConnectionStateWaitForLcnBusConnected(ConnectionStateMachine context) { + super(context); } @Override @@ -40,9 +39,9 @@ public void startWorking() { // Legacy support for LCN-PCHK 2.2 and earlier: // There was no explicit "LCN connected" notification after successful authentication. // Only "LCN disconnected" would be reported immediately. That means "LCN connected" used to be the default. - ScheduledFuture localLegacyTimer = legacyTimer = scheduler.schedule(() -> { + ScheduledFuture localLegacyTimer = legacyTimer = getScheduler().schedule(() -> { connection.getCallback().onOnline(); - nextState(ConnectionStateSendDimMode.class); + nextState(ConnectionStateSendDimMode::new); }, connection.getSettings().getTimeout(), TimeUnit.MILLISECONDS); addTimer(localLegacyTimer); } @@ -60,7 +59,7 @@ public void onPckMessageReceived(String data) { localLegacyTimer.cancel(true); } connection.getCallback().onOnline(); - nextState(ConnectionStateSendDimMode.class); + nextState(ConnectionStateSendDimMode::new); } else if (data.equals(LcnDefs.INSUFFICIENT_LICENSES)) { context.handleConnectionFailed( new LcnException("LCN-PCHK/PKE has not enough licenses to handle this connection")); diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnectedAfterDisconnected.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnectedAfterDisconnected.java index 5a7d7c3a5c767..8174ada6eecb7 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnectedAfterDisconnected.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/ConnectionStateWaitForLcnBusConnectedAfterDisconnected.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.lcn.internal.connection; -import java.util.concurrent.ScheduledExecutorService; - import org.eclipse.jdt.annotation.NonNullByDefault; /** @@ -24,9 +22,8 @@ */ @NonNullByDefault public class ConnectionStateWaitForLcnBusConnectedAfterDisconnected extends ConnectionStateWaitForLcnBusConnected { - public ConnectionStateWaitForLcnBusConnectedAfterDisconnected(StateContext context, - ScheduledExecutorService scheduler) { - super(context, scheduler); + public ConnectionStateWaitForLcnBusConnectedAfterDisconnected(ConnectionStateMachine context) { + super(context); } @Override diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateContext.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateContext.java deleted file mode 100644 index ec7908b1f35d4..0000000000000 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/connection/StateContext.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * 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.connection; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.lcn.internal.common.LcnAddr; - -/** - * Interface for a {@link StateMachine}. These methods are visible to the states, used by the {@link StateMachine}. - * - * @author Fabian Wolter - Initial Contribution - */ -@NonNullByDefault -public interface StateContext { - /** - * Sets the new state of the StateMachine. - * - * @param newStateClass the class of the new State - */ - void setState(Class newStateClass); - - /** - * Checks if the given State is active by comparing the identity. - * - * @param otherState the stat to check - * @return true, if the given state is active - */ - boolean isStateActive(AbstractState otherState); - - /** - * Gets the Connection to the PCK gateway. - * - * @return the Connection - */ - Connection getConnection(); - - /** - * Notifies openHAB the Connection has failed and enters the necessary State to handle the situation. - * - * @param e the reason why the Connection has been failed - */ - void handleConnectionFailed(@Nullable Throwable e); - - /** - * Enqueues a PCK message. When the Connection is offline, the message will be buffered and sent out as soon as the - * Connection recovers. The message is discarded if it is too old, when the Connection recovers. - * - * @param addr the destination address - * @param wantsAck true, if the module shall respond with an Ack - * @param data the PCK message - */ - void queue(LcnAddr addr, boolean wantsAck, byte[] data); -} diff --git a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java index 8b5dadaf27c6e..be8bb3fbc0925 100644 --- a/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java +++ b/bundles/org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/pchkdiscovery/LcnPchkDiscoveryService.java @@ -18,7 +18,6 @@ import java.net.MulticastSocket; import java.net.NetworkInterface; import java.net.SocketException; -import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.Collections; import java.util.HashMap; @@ -36,6 +35,7 @@ import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.openhab.binding.lcn.internal.LcnBindingConstants; +import org.openhab.binding.lcn.internal.common.LcnDefs; import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -103,46 +103,42 @@ protected void startScan() { socket.setSoTimeout(INTERFACE_TIMEOUT_SEC * 1000); socket.joinGroup(multicastAddress); - byte[] requestData = DISCOVER_REQUEST.getBytes("UTF-8"); + byte[] requestData = DISCOVER_REQUEST.getBytes(LcnDefs.LCN_ENCODING); DatagramPacket request = new DatagramPacket(requestData, requestData.length, multicastAddress, PCHK_DISCOVERY_PORT); socket.send(request); - try { - do { - byte[] rxbuf = new byte[8192]; - DatagramPacket packet = new DatagramPacket(rxbuf, rxbuf.length); - socket.receive(packet); + do { + byte[] rxbuf = new byte[8192]; + DatagramPacket packet = new DatagramPacket(rxbuf, rxbuf.length); + socket.receive(packet); - InetAddress addr = packet.getAddress(); - String response = new String(packet.getData()); + InetAddress addr = packet.getAddress(); + String response = new String(packet.getData(), LcnDefs.LCN_ENCODING); - if (response.contains("ServicesRequest")) { - continue; - } + if (response.contains("ServicesRequest")) { + continue; + } - ServicesResponse deserialized = xmlToServiceResponse(response); + ServicesResponse deserialized = xmlToServiceResponse(response); - String macAddress = deserialized.getServer().getMachineId().replace(":", ""); - ThingUID thingUid = new ThingUID(LcnBindingConstants.THING_TYPE_PCK_GATEWAY, macAddress); + String macAddress = deserialized.getServer().getMachineId().replace(":", ""); + ThingUID thingUid = new ThingUID(LcnBindingConstants.THING_TYPE_PCK_GATEWAY, macAddress); - Map properties = new HashMap<>(3); - properties.put(HOSTNAME, addr.getHostAddress()); - properties.put(PORT, deserialized.getExtServices().getExtService().getLocalPort()); - properties.put(MAC_ADDRESS, macAddress); + Map properties = new HashMap<>(3); + properties.put(HOSTNAME, addr.getHostAddress()); + properties.put(PORT, deserialized.getExtServices().getExtService().getLocalPort()); + properties.put(MAC_ADDRESS, macAddress); - DiscoveryResultBuilder discoveryResult = DiscoveryResultBuilder.create(thingUid) - .withProperties(properties).withRepresentationProperty(MAC_ADDRESS) - .withLabel(deserialized.getServer().getContent() + " (" - + deserialized.getServer().getMachineName() + ")"); + DiscoveryResultBuilder discoveryResult = DiscoveryResultBuilder.create(thingUid) + .withProperties(properties).withRepresentationProperty(MAC_ADDRESS) + .withLabel(deserialized.getServer().getContent() + " (" + + deserialized.getServer().getMachineName() + ")"); - thingDiscovered(discoveryResult.build()); - } while (true); // left by SocketTimeoutException - } catch (SocketTimeoutException e) { - // nothing - } + thingDiscovered(discoveryResult.build()); + } while (true); // left by SocketTimeoutException } catch (IOException e) { - logger.warn("Discovery failed for {}: {}", localInterfaceAddress, e.getMessage()); + logger.debug("Discovery failed for {}: {}", localInterfaceAddress, e.getMessage()); } }); } catch (UnknownHostException e) {