diff --git a/CODEOWNERS b/CODEOWNERS index 9b49e0334d5b6..473444aab258e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -186,6 +186,7 @@ /bundles/org.openhab.binding.valloxmv/ @bjoernbrings /bundles/org.openhab.binding.vektiva/ @octa22 /bundles/org.openhab.binding.velbus/ @cedricboon +/bundles/org.openhab.binding.velux/ @gs4711 /bundles/org.openhab.binding.vitotronic/ @steand /bundles/org.openhab.binding.volvooncall/ @clinique /bundles/org.openhab.binding.weathercompany/ @mhilbush diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 548ce73885108..addd13c3d7bd9 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -924,6 +924,11 @@ org.openhab.binding.velbus ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.velux + ${project.version} + org.openhab.addons.bundles org.openhab.binding.vitotronic diff --git a/bundles/org.openhab.binding.velux/NOTICE b/bundles/org.openhab.binding.velux/NOTICE new file mode 100644 index 0000000000000..4c20ef446c1e4 --- /dev/null +++ b/bundles/org.openhab.binding.velux/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/openhab2-addons diff --git a/bundles/org.openhab.binding.velux/README.md b/bundles/org.openhab.binding.velux/README.md new file mode 100644 index 0000000000000..35b7ed8978576 --- /dev/null +++ b/bundles/org.openhab.binding.velux/README.md @@ -0,0 +1,397 @@ + +# Velux Binding + +This binding integrates the Velux devices with help of a gateway, the Velux Bridge KLF200, which is able to control 200 actuators. +The Velux Binding interacts via the Velux Bridge with any [io-homecontrol](http://www.io-homecontrol.com/)-based +devices like window openers, shutters and others. + +Based on the VELUX API this binding integrates Velux and other io-homecontrol devices directly into the openHAB, avoiding the necessity of any cloud-based mediation infrastructures. The complete home-automation will work even without any Internet connectivity. + +For details about the features, see the following websites: + +- [Velux](http://www.velux.com) +- [Velux API](http://www.velux.com/api/klf200) + +## Overview + +As the API is widely open, there are several use cases which are supported by the Bridge: +From the complete configuration of a set of io-homecontrol devices including registration, naming, grouping, crypto key setup and exchange, the definition of intended settings, so called scenes, up to the control of single devices, i.e. ```open window of bathroom up to 45%```. + +The following areas are covered: + +| Topic | Details | +|-------------------------|------------------------------------------------------------------------------------------------------------| +| General bridge commands | SW version(\*), Gateway state(\*), Learn state, Clock, Reboot, FactoryReset, Network Setup(+) | +| Configuration Services | Node Discovery, Node Removal, Controller Copy, Crypto Key Generation, Crypto Key Exchange, Actuator config | +| Information Services | House Monitoring Service(\*), Node information(+), Group information | +| Activation Logging | | +| Command Handling | Command activation(\*), Command interruption(\*), Status Request(\*), Actuator Identification(\*), Limitations | +| Scene Handling | Scene definition, Scene execution(\*), Scene deletion, Scene renaming, Scene Overview(\*) | +| Physical I/O Handling | I/O Port setup | + +Items marked with (\*) are fully implemented. Items marked with (+) have only partial support. + +## Binding Configuration + +To simplify the initial provisioning, the binding provides one thing which can be found by autodiscovery: +This thing named Velux Binding Information establishes one channel named information describing +the current binding state in a (hopefully) human understandable fashion. Additionally it will give three +properties with the version number of the bundle, the number of registered bridges and number of overall +things attached to the bridge(s). + +The Velux KLF200 bridge has to be configured with some parameters, at least with the IP address of the bridge. + +| Property | Default | Required | Description | +|------------------------|------------------|:--------:|--------------------------------------------------------------| +| ipAddress | | Yes | Hostname or address for accessing the Velux Bridge. | +| protocol | slip | No | Underlying communication protocol (http/https/slip). | +| tcpPort | 51200 | No | TCP port (80 or 51200) for accessing the Velux Bridge. | +| password | velux123 | No | Password for authentication against the Velux Bridge.(\*\*) | +| timeoutMsecs | 1000 | No | Initial Connection timeout in milliseconds. | +| retries | 5 | No | Number of retries during I/O. | +| refreshMsecs | 10000 | No | Refresh interval in milliseconds. | +| isBulkRetrievalEnabled | yes | No | Load all scenes and actuators in one step. | +| isSequentialEnforced | no | No | Enforce Sequential Actuator Control even for long operations.| +| isProtocolTraceEnabled | no | No | Show any protocol interaction (loglevel INFO). | + +(\*\*) Note: This password is the API password that is printed on the back of the unit. Normally it differs from the password of the web frontend. + +Advise: if you see a significant number of messages per day like + +``` + communicate(): socket I/O failed continuously (x times). +``` + +please increase the parameters retries or/and timeoutMsecs. + +For your convenience you'll see a log entry for the recognized configuration within the log file i.e. + +``` +2018-07-23 20:40:24.746 [INFO ] [.b.velux.internal.VeluxBinding] - veluxConfig[ipAddress=192.168.42.1,tcpPort=80,password=********,timeoutMsecs=2000,retries=10] +``` + +The Velux Things (beside the mentioned bridge) are Velux Window, Velux Rollershutter, and a generic Velux Actuator and Velux Scene. The 1st three Things have to be configured with an identification by their serial number. + +| Property | Default | Required | Description | +|----------------|------------------------|:--------:|-----------------------------------------------------------| +| serial | | Yes | Serial number of the io-homecontrol device. | +| name | | No | (Optional) name of the io-homecontrol device. | +| inverted | false | No | Inverts any device values. | + +The fourth Thing, the Velux Scene, has to be configured with an identification by their scenename. + +| Property | Default | Required | Description | +|----------------|------------------------|:--------:|-----------------------------------------------------------| +| sceneName | | Yes | Name of the io-homecontrol configuration. | + +The fifth Thing, the Velux Virtual Shutter, has to be configured with pairs of level combined with the appropriate scenenames. + +| Property | Default | Required | Description | +|----------------|------------------------|:--------:|-----------------------------------------------------------| +| sceneLevels | | Yes | ,,,,.... | +| currentLevel | 0 | No | Inverts any device values. | + + +## Discovery + +Unfortunately there is no way to discover the Velux bridge itself within the local network. But after configuring the Velux Bridge, it is possible to discover all scenes and actuators like windows and rollershutters by the binding. + +## Item Configuration + +The Items of a Velux Bridge consists in general of a pair of mastertype and subtype definition. +In the appropriate items file, i.e. velux.items, this looks like + +``` +{ velux="thing=;channel=" } +``` + +Optionally the subtype is enhanced with parameters like the appropriate name of the scene. + +``` +{ velux="thing=;channel=#" } +``` + +| Mastertype | Description | +|---------------|----------------------------------------------------------------------------------| +| binding | Provides informations for easier configuration of this binding. | +| bridge | The Velux KLF200 represents a gateway to all Velux devices. | +| scene | Named ordered set of product states which can be activated for execution. | +| actuator | Generic IO-home controlled device which can be maintained by parameter settings. | +| window | IO-home controlled device of type window. | +| rollershutter | IO-home controlled device of type rollershutter. | +| vshutter | IO-home controlled device of type rollershutter. | + + +### Subtype + + +| Subtype | Item Type | Description | Mastertype | Parameter | +|--------------|---------------|-----------------------------------------------------------------|--------------|-----------| +| information | String | Describes the current state of the binding | binding | N/A | +| status | String | Current Bridge State (\*\*\*) | bridge | N/A | +| reload | Switch | Reload information from bridge into binding | bridge | N/A | +| timestamp | Number | Timestamp of last successful device interaction | bridge | N/A | +| doDetection | Switch | Start of the product detection mode | bridge | N/A | +| firmware | String | Software version of the Bridge | bridge | N/A | +| ipAddress | String | IP address of the Bridge | bridge | N/A | +| subnetMask | String | IP subnetmask of the Bridge | bridge | N/A | +| defaultGW | String | IP address of the Default Gateway of the Bridge | bridge | N/A | +| DHCP | Switch | Flag whether automatic IP configuration is enabled | bridge | N/A | +| WLANSSID | String | Name of the wireless network | bridge | N/A | +| WLANPassword | String | WLAN Authentication Password | bridge | N/A | +| products | String | List of all recognized products | bridge | N/A | +| scenes | String | List of all defined scenes | bridge | N/A | +| check | String | Result of the check of current item configuration | bridge | N/A | +| shutter | Rollershutter | Virtual rollershutter as combination of different scenes | bridge | required | +|--------------|---------------|-----------------------------------------------------------------|--------------|-----------| +| serial | String | IO-Homecontrol'ed device (\*\*\*\*) (\*\*\*\*\*) | actuator | required | +| position | Rollershutter | Position of the IO-Homecontrol'ed device (\*\*\*\*) | actuator | optional | +| state | Switch | State of the IO-Homecontrol'ed device | actuator | optional | +| limitMinimum | Rollershutter | Minimum position of the IO-Homecontrol'ed device (\*\*\*\*) | actuator | optional | +| limitMaximum | Rollershutter | Maximum position of the IO-Homecontrol'ed device (\*\*\*\*) | actuator | optional | +|--------------|---------------|-----------------------------------------------------------------|--------------|-----------| +| serial | String | IO-Homecontrol'ed device (\*\*\*\*) (\*\*\*\*\*) | window | required | +| position | Rollershutter | Position of the IO-Homecontrol'ed device (\*\*\*\*) | window | optional | +| limitMinimum | Rollershutter | Minimum position of the IO-Homecontrol'ed device (\*\*\*\*) | window | optional | +| limitMaximum | Rollershutter | Maximum position of the IO-Homecontrol'ed device (\*\*\*\*) | window | optional | +|--------------|---------------|-----------------------------------------------------------------|--------------|-----------| +| serial | String | IO-Homecontrol'ed device (\*\*\*\*) (\*\*\*\*\*) | rollershutter| required | +| position | Rollershutter | Position of the IO-Homecontrol'ed device (\*\*\*\*) | rollershutter| optional | +| limitMinimum | Rollershutter | Minimum position of the IO-Homecontrol'ed device (\*\*\*\*) | rollershutter| optional | +| limitMaximum | Rollershutter | Maximum position of the IO-Homecontrol'ed device (\*\*\*\*) | rollershutter| optional | +|--------------|---------------|-----------------------------------------------------------------|--------------|-----------| +| sceneName | String | Defines the scene by name according to registration in KLF200 | scene | required | +| action | Switch | Activates a set of predefined product settings | scene | optional | +| silentMode | Switch | Modification of the silent mode of the defined product settings | scene | optional | + +Notes: +(\*\*\*) The existence of this item triggers the continuous realtime status updates of any Velux item like shutters even if they are manually controlled by other controllers. + +(\*\*\*\*) To enable a complete invertion of all parameter values (i.e. for Velux windows), use the property `inverted` or add a trailing star to the eight-byte serial number. For an example, see below at item `Velux DG Window Bathroom`. + +(\*\*\*\*\*) Somfy devices does not provides a valid serial number to the Velux KLF200 gateway: The bridge reports a registration of the serial number 00:00:00:00:00:00:00:00. Therefore the binding implements a fallback to allow an item specification with a actuator name instead of actuator serial number whenever such an invalid serial number occurs. For an example, see below at item `Velux OG Somfy Shutter`. + + +### Subtype Parameters + +In case of the scene-related subtypes, action and silentMode, the specification of the related scene as parameters is necessary; + +``` +{ velux="thing=scene;channel=#" } +``` + +The subtype shutter requires an even pair of parameters, each defining the shutter level and the related scene: + +``` +{ velux="thing=brigde;channel=shutter#,,," } +``` + +### Rain Sensor + +Unfortunately Velux has decided to closely integrate the rain sensor into the window device. The rain sensor is therefore not displayed in the device list. On the other hand, the 'limitMinimum' channel of a roof window now provides information about rainy weather: if it is set internally by the Velux control unit to a value other than zero, it rains. + +### Virtual shutter + +As the bridge with firmware version one does not support a real rollershutter interaction, this binding provides a virtual rollershutter consisting of different scenes which set a specific shutter level. Therefore the item definition contains multiple pairs of rollershutter levels each followed by a scene name, which leads to this setting. + + +### Items + +[Sample items file for textual configuration](doc/conf/items/velux.items) + +### Sitemap + +[Sample sitemaps file for textual configuration](doc/conf/sitemaps/velux.sitemap) + +### Rules + +[Sample rules file for textual configuration](doc/conf/rules/velux.rules) + +### Things + +[Sample things file for textual configuration](doc/conf/things/velux.things) + +## More automation samples + +At this point some interesting automation rules are included to demonstrate the power of this gateway to the io-homecontrol world. + + +### Closing windows after a period of time + +Especially in the colder months, it is advisable to close the window after adequate ventilation. Therefore, automatic closing after one minute is good to save on heating costs. +However, to allow the case of intentional prolonged opening, an automatic closure is made only with the window fully open. + +``` +/* + * Start of imports + */ + +import org.openhab.core.library.types.* + +/* + * Start of rules + */ + +rule "V_WINDOW_changed" +when + Item V_WINDOW changed +then + logInfo("rules.V_WINDOW", "V_WINDOW_changes() called.") + // + // Get the sensor value + // + val Number windowState = V_WINDOW.state as DecimalType + logWarn("rules.V_WINDOW", "Window state is "+windowState+".") + if (windowState < 80) { + if (windowState == 0) { + logWarn("rules.V_WINDOW", "V-WINDOW changed to fully open.") + + var int interval = 1 + + createTimer(now.plusMinutes(interval)) [| + logWarn("rules.V_WINDOW:event", "event-V_WINDOW(): setting V-WINDOW to 100.") + sendCommand(V_WINDOW,100) + V_WINDOW.postUpdate(100) + logWarn("rules.V_WINDOW:event", "event-V_WINDOW done.") + ] + } else { + logWarn("rules.V_WINDOW", "V-WINDOW changed to partially open.") + } + } + // + // Check type of item + // + logDebug("rules.V_WINDOW", "V_WINDOW_changes finished.") +end + +/* + * end-of-rules/V_WINDOW.rules + */ +``` + +## Debugging + +For those who are interested in more detailed insight of the processing of this binding, a deeper look can be achieved by increased loglevel. + +With Karaf you can use the following command sequence: + +``` +log:set TRACE org.openhab.binding.velux +log:tail +``` + +This, of course, is possible on command line with the commands: + +``` +% openhab-cli console log:set TRACE org.openhab.binding.velux +% openhab-cli console log:tail org.openhab.binding.velux +``` + +On the other hand, if you prefer a textual configuration, you can append the logging definition with: + +``` + + + +``` + +During startup of normal operations, there should be only some few messages within the logfile, like: + +``` +[INFO ] [nal.VeluxValidatedBridgeConfiguration] - veluxConfig[protocol=slip,ipAddress=192.168.45.9,tcpPort=51200,password=********,timeoutMsecs=1000,retries=5,refreshMsecs=15000,isBulkRetrievalEnabled=true] +[INFO ] [ng.velux.bridge.slip.io.SSLconnection] - Starting velux bridge connection. +[INFO ] [hab.binding.velux.bridge.slip.SClogin] - velux bridge connection successfully established (login succeeded). +[INFO ] [ding.velux.handler.VeluxBridgeHandler] - Found velux scenes: + Scene "V_Shutter_West_100" (index 5) with non-silent mode and 0 actions + Scene "V_Shutter_West_000" (index 4) with non-silent mode and 0 actions + Scene "V_Shutter_Ost_090" (index 10) with non-silent mode and 0 actions + Scene "V_Window_Mitte_005" (index 3) with non-silent mode and 0 actions + Scene "V_Window_Mitte_000" (index 1) with non-silent mode and 0 actions + Scene "V_Window_Mitte_100" (index 2) with non-silent mode and 0 actions + Scene "V_Shutter_West_090" (index 7) with non-silent mode and 0 actions + Scene "V_Window_Mitte_010" (index 0) with non-silent mode and 0 actions + Scene "V_Shutter_Ost_000" (index 8) with non-silent mode and 0 actions + Scene "V_Shutter_Ost_100" (index 9) with non-silent mode and 0 actions . +[INFO ] [ding.velux.handler.VeluxBridgeHandler] - Found velux actuators: + Product "M_Rollershutter" / ROLLER_SHUTTER (bridgeIndex=4,serial=43:12:14:5A:12:1C:05:5F,position=0010) + Product "O_Rollershutter" / ROLLER_SHUTTER (bridgeIndex=3,serial=43:12:40:5A:0C:23:0A:6E,position=0000) + Product "M_Window" / WINDOW_OPENER (bridgeIndex=0,serial=43:12:3E:26:0C:1B:00:10,position=C800) + Product "W-Rollershutter" / ROLLER_SHUTTER (bridgeIndex=1,serial=43:12:40:5A:0C:2A:05:64,position=0000) . +[INFO ] [ding.velux.handler.VeluxBridgeHandler] - velux Bridge is online with 10 scenes and 4 actuators, now. +``` + +However if you have set the configuration parameter isProtocolTraceEnabled to true, you'll see the complete sequence of exchanged messages: + +``` +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_PASSWORD_ENTER_REQ. +[INFO ] [nternal.bridge.slip.io.SSLconnection] - Starting velux bridge connection. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_PASSWORD_ENTER_CFM. +[INFO ] [g.velux.internal.bridge.slip.SClogin] - velux bridge connection successfully established (login succeeded). +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_COMMAND_SEND_REQ. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_COMMAND_SEND_CFM. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_GET_LIMITATION_STATUS_REQ. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_NODE_STATE_POSITION_CHANGED_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_COMMAND_RUN_STATUS_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_COMMAND_RUN_STATUS_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_SESSION_FINISHED_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_NODE_STATE_POSITION_CHANGED_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_LIMITATION_STATUS_CFM. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_LIMITATION_STATUS_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_GET_NODE_INFORMATION_REQ. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_COMMAND_RUN_STATUS_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_SESSION_FINISHED_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_CFM. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_GET_NODE_INFORMATION_REQ. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_CFM. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_GET_NODE_INFORMATION_REQ. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_CFM. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_GET_NODE_INFORMATION_REQ. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_CFM. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Received answer GW_GET_NODE_INFORMATION_NTF. +[INFO ] [internal.bridge.slip.SlipVeluxBridge] - Sending command GW_GET_LIMITATION_STATUS_REQ. +... +``` + + +## Supported/Tested Firmware Revisions + +The Velux Bridge in API version one (firmware version 0.1.1.*) allows activating a set of predefined actions, so called scenes. Therefore beside the bridge, only one main thing exists, the scene element. The next-generation firmware version two is not backward compatible, and does not provide a public web frontend, but version two does provide full access to any IO-Home compatible devices not limited to Velux and includes many different features. + +| Firmware revision | Release date | Description | +|:-----------------:|:------------:|-------------------------------------------------------------------------| +| 0.1.1.0.41.0 | 2016-06-01 | Default factory shipping revision. | +| 0.1.1.0.42.0 | 2017-07-01 | Public Web Frontend w/ JSON-API. | +| 0.1.1.0.44.0 | 2017-12-14 | Public Web Frontend w/ JSON-API. | +| 2.0.0.71 | 2018-09-27 | Public SLIP-API w/ private-only WLAN-based Web Frontend w/ JSON-API. | + +Notes: + +- Velux bridges cannot be returned to version one of the firmware after being upgraded to version two. +- Firmware updates are currently provided at [Velux download area](https://updates2.velux.com/). + + +## Is it possible to run the both communication methods in parallel? + +For environments with the firmware version 0.1.* on the gateway, the interaction with the bridge is limited to the HTTP/JSON based communication, of course. On the other hand, after upgrading the gateway firmware to version 2, it is possible to run the binding either using HTTP/JSON if there is a permanent connectivity towards the WLAN interface of the KLF200 or using SLIP towards the LAN interface of the gateway. For example the Raspberry PI can directly be connected via WLAN to the Velux gateway and providing the other services via the LAN interface (but not vice versa). + + +## Known Limitations + +The communication based on HTTP/JSON is limited to one connection: If the binding is operational, you won't get access to the Web Frontend in parallel. + +The SLIP communication is limited to two connections in parallel, i.e. two different openHAB bindings - or - one openHAB binding and another platform connection. + +Both interfacing methods, HTTP/JSON and SLIP, can be run in parallel. Therefore, on the one hand you can use the Web Frontend for manual control and on the other hand a binding can do all automatic jobs. + + +## Unknown Velux devices + +All known Velux devices can be handled by this binding. However, there might be some new ones which will be reported within the logfiles. Therefore, error messages like the one below should be reported to the maintainers so that the new Velux device type can be incorporated." + +``` +[ERROR] [g.velux.things.VeluxProductReference] - PLEASE REPORT THIS TO MAINTAINER: VeluxProductReference(3) has found an unregistered ProductTypeId. +``` + diff --git a/bundles/org.openhab.binding.velux/doc/conf/items/velux.items b/bundles/org.openhab.binding.velux/doc/conf/items/velux.items new file mode 100644 index 0000000000000..781feb191b9f1 --- /dev/null +++ b/bundles/org.openhab.binding.velux/doc/conf/items/velux.items @@ -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 + */ + +/** + * OpenHAB item defintion for velux binding: + * Velux Bridge and Devices + * + * @author Guenther Schreiner - Initial contribution + */ + +// Group for simulating push buttons + +Group:Switch:OR(ON, OFF) gV "PushButton" + +// Velux Bridge channels + +String V_BRIDGE_STATUS "Velux Bridge Status [%s]" { channel="velux:klf200:home:status" } +Switch V_BRIDGE_RELOAD "Velux Bridge Reload" (gV) { channel="velux:klf200:home:reload" } +String V_BRIDGE_TIMESTAMP "Velux Bridge Timestamp [%d]" { channel="velux:klf200:home:timestamp" } + +String V_BRIDGE_FIRMWARE "Velux Bridge Firmware version [%s]" { channel="velux:klf200:home:firmware" } +String V_BRIDGE_IPADDRESS "Velux Bridge LAN IP Address" { channel="velux:klf200:home:ipAddress" } +String V_BRIDGE_SUBNETMASK "Velux Bridge LAN IP Subnet Mask" { channel="velux:klf200:home:subnetMask" } +String V_BRIDGE_DEFAULTGW "Velux Bridge LAN Default Gateway" { channel="velux:klf200:home:defaultGW" } +String V_BRIDGE_DHCP "Velux Bridge LAN DHCP Enabled" { channel="velux:klf200:home:DHCP" } +String V_BRIDGE_WLANSSID "Velux Bridge WLAN SSID" { channel="velux:klf200:home:WLANSSID" } +String V_BRIDGE_WLANPASSWD "Velux Bridge WLAN Password" { channel="velux:klf200:home:WLANPassword" } + +Switch V_BRIDGE_DETECTION "Velux Bridge Detection mode" (gV) { channel="velux:klf200:home:doDetection" } +String V_BRIDGE_CHECK "Velux Bridge Check" { channel="velux:klf200:home:check" } +String V_BRIDGE_SCENES "Velux Bridge Scenes" { channel="velux:klf200:home:scenes" } +String V_BRIDGE_PRODUCTS "Velux Bridge Products" { channel="velux:klf200:home:products" } + +// Velux Scene channels + +Switch V_DG_M_W_OPEN "Velux DG Window open" (gV) { channel="velux:scene:home:windowOpened:action" } +Switch V_DG_M_W_UNLOCKED "Velux DG Window a little open" (gV) { channel="velux:scene:home:windowUnlocked:action" } +Switch V_DG_M_W_CLOSED "Velux DG Window closed" (gV) { channel="velux:scene:home:windowClosed:action" } + +// Velux Bridge channel + +Rollershutter RS2 "Velux Rolladen 2 [%d]" { channel="velux:klf200:home:shutter#0,V_DG_Shutter_Ost_000,100,V_DG_Shutter_Ost_100", channel="knx:device:bridge:control:VeluxFenster" } + + +// Velux Actuator channels + +Rollershutter V_DG_M_W "DG Fenster Bad [%d]" { channel="velux:klf200:home:V_DG_M_W" } +Rollershutter V_DG_M_W2 "DG Fenster Bad [%d]" { channel="velux:klf200:home:V_DG_M_W2" } +Rollershutter V_DG_M_S "DG Bad [%d]" { channel="velux:klf200:home:V_DG_M_S" } +Rollershutter V_DG_W_S "DG West [%d]" { channel="velux:klf200:home:V_DG_W_S" } +Rollershutter V_DG_O_S "DG Ost [%d]" { channel="velux:klf200:home:V_DG_O_S" } + +// +// vim: syntax=Xtend vim: noai:ts=4:sw=4 +// +// end-of-items/velux.items +// diff --git a/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules b/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules new file mode 100644 index 0000000000000..28159b27f7cac --- /dev/null +++ b/bundles/org.openhab.binding.velux/doc/conf/rules/velux.rules @@ -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 + */ + +/** + * OpenHAB rules for velux binding: + * This rule simulates the push button behaviour. + * + * @author Guenther Schreiner - Initial contribution + */ + +rule "PushButton of group gV" + when + Item gV changed + then + // waiting a second. + Thread::sleep(1000) + // Foreach-Switch-is-ON + gV.allMembers.filter( s | s.state == ON).forEach[i| + // switching OFF + i.sendCommand(OFF) + ] + end + +// +// vim: syntax=Xtend vim: noai:ts=4:sw=4 +// +// end-of-rules/velux.rules +// diff --git a/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap b/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap new file mode 100644 index 0000000000000..0ff17a8864c50 --- /dev/null +++ b/bundles/org.openhab.binding.velux/doc/conf/sitemaps/velux.sitemap @@ -0,0 +1,59 @@ +/** + * + * 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 + */ + +/** + * OpenHAB sitemap definition for velux binding: + * displays most of the Velux items + * + * @author Guenther Schreiner - Initial contribution + */ + +sitemap velux label="Velux Environment" +{ + Frame label="Velux Shutter and Window" { + + Switch item=V_DG_M_W_OPEN + Switch item=V_DG_M_W_UNLOCKED + Switch item=V_DG_M_W_CLOSED + Slider item=V_DG_M_W + } + + Frame label="Velux Bridge Status" { + Text item=V_BRIDGE_STATUS + Text item=V_BRIDGE_TIMESTAMP + Switch item=V_BRIDGE_RELOAD + } + + Frame label="Velux Bridge Status" { + Switch item=V_BRIDGE_DETECTION + Text item=V_BRIDGE_CHECK + Text item=V_BRIDGE_SCENES + Text item=V_BRIDGE_PRODUCTS + } + + Frame label="Velux Bridge Configuration" { + Text item=V_BRIDGE_FIRMWARE + Text item=V_BRIDGE_IPADDRESS + Text item=V_BRIDGE_SUBNETMASK + Text item=V_BRIDGE_DEFAULTGW + Switch item=V_BRIDGE_DHCP + Text item=V_BRIDGE_WLANSSID + Text item=V_BRIDGE_WLANPASSWD + } + +} + +// +// vim: syntax=Xtend vim: noai:ts=4:sw=4 +// +// end-of-sitemap/velux.sitemap +// diff --git a/bundles/org.openhab.binding.velux/doc/conf/things/velux-001.things b/bundles/org.openhab.binding.velux/doc/conf/things/velux-001.things new file mode 100644 index 0000000000000..5d99d56ce8034 --- /dev/null +++ b/bundles/org.openhab.binding.velux/doc/conf/things/velux-001.things @@ -0,0 +1,30 @@ +/** + * + * 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 + */ + +/** + * OpenHAB thing definition for velux binding: + * define a Velux device behind the bridge + * + * @author Guenther Schreiner - Initial contribution + */ + +// +// Definition of a Velux window behind bridge velux:klf200:gs28 +// + +Thing velux:window:gs28:window001 (velux:klf200:gs28) [ serial="53:09:40:5A:0C:2A:05:64" ] + +// +// vim: syntax=Xtend vim: noai:ts=4:sw=4 +// +// end-of-things/velux-bridge.things +// diff --git a/bundles/org.openhab.binding.velux/doc/conf/things/velux-bridge.things b/bundles/org.openhab.binding.velux/doc/conf/things/velux-bridge.things new file mode 100644 index 0000000000000..ae38a0b638191 --- /dev/null +++ b/bundles/org.openhab.binding.velux/doc/conf/things/velux-bridge.things @@ -0,0 +1,30 @@ +/** + * + * 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 + */ + +/** + * OpenHAB thing definition for velux binding: + * define basic Velux bridge parameters + * + * @author Guenther Schreiner - Initial contribution + */ + +// +// Definition of Velux bridge velux:klf200:home +// + +Bridge velux:klf200:gs28 [ ipAddress="192.168.45.1", tcpPort=51200, password="secret", isProtocolTraceEnabled=true ] + +// +// vim: syntax=Xtend vim: noai:ts=4:sw=4 +// +// end-of-things/velux-bridge.things +// diff --git a/bundles/org.openhab.binding.velux/doc/conf/things/velux.things.disabled b/bundles/org.openhab.binding.velux/doc/conf/things/velux.things.disabled new file mode 100644 index 0000000000000..c8e3f6af6271a --- /dev/null +++ b/bundles/org.openhab.binding.velux/doc/conf/things/velux.things.disabled @@ -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 + */ + +/** + * OpenHAB thing definition for velux binding: + * define basic Velux bridge parameters + * + * This is a definition which corresponds to the output of th Bridge + * during startup, i.e. + * + * [INFO ] [x.internal.handler.VeluxBridgeHandler] - Found velux actuators: + * Product "#0" / SLIDER_SHUTTER (bridgeIndex=0,serial=56:32:14:5A:12:1C:05:5F,position=0000) + * Product "#1" / SLIDER_SHUTTER (bridgeIndex=1,serial=53:09:40:5A:0C:23:0A:6E,position=0000) + * Product "DG-M-Window" / SLIDER_WINDOW (bridgeIndex=3,serial=56:23:3E:26:0C:1B:00:10,position=C800) + * Product "#2" / SLIDER_SHUTTER (bridgeIndex=2,serial=53:09:40:5A:0C:2A:05:64,position=0000) + * Product "#4" / SWITCH (bridgeIndex=4,serial=Somfy-Switch,position=C800) + * Product "#5" / SWITCH (bridgeIndex=5,serial=Somfy-Switch,position=C800) + * + * @author Guenther Schreiner - Initial contribution + */ + +// +// Definition of Velux bridge velux:klf200:home +// + +Bridge velux:klf200:home [ ipAddress="192.168.45.9", tcpPort=51200, password="verySecret" ] { + +// Velux scenes + + Thing scene windowClosed [ sceneName="V_DG_Window_Mitte_000" ] + Thing scene windowUnlocked [ sceneName="V_DG_Window_Mitte_005" ] + Thing scene windowOpened [ sceneName="V_DG_Window_Mitte_100" ] + Thing scene unknownScene [ sceneName="ThisIsADummySceneName" ] + +// Velux IO-homecontrol devices + + Thing window V_DG_M_W [ serial="56:23:3E:26:0C:1B:00:10" ] + Thing rollershutter V_DG_M_S [ serial="56:23:3E:26:0C:1B:00:10" ] + Thing rollershutter V_DG_W_S [ serial="53:09:40:5A:0C:2A:05:64" ] + Thing rollershuffer V_DG_O_S [ serial="53:09:40:5A:0C:23:0A:6E" ] + Thing actuator V_SWITCH1 [ name="#4" ] + Thing actuator V_SWITCH2 [ name="#5" ] + +// Virtual rollershutter + + Thing vshutter V_WINDOW [ sceneLevels="0,V_DG_Window_Mitte_000#5,V_DG_Window_Mitte_005#100,V_DG_Window_Mitte_100" ] +} + +// +// vim: syntax=Xtend vim: noai:ts=4:sw=4 +// +// end-of-things/velux.things +// diff --git a/bundles/org.openhab.binding.velux/pom.xml b/bundles/org.openhab.binding.velux/pom.xml new file mode 100644 index 0000000000000..7f7534990e766 --- /dev/null +++ b/bundles/org.openhab.binding.velux/pom.xml @@ -0,0 +1,20 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.3-SNAPSHOT + + + org.openhab.binding.velux + + openHAB Add-ons :: Bundles :: Velux Binding + + This binding integrates the Velux devices with help of a gateway, the Velux Bridge KLF200. + diff --git a/bundles/org.openhab.binding.velux/src/main/feature/feature.xml b/bundles/org.openhab.binding.velux/src/main/feature/feature.xml new file mode 100644 index 0000000000000..75a556b51b850 --- /dev/null +++ b/bundles/org.openhab.binding.velux/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.velux/${project.version} + + diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java new file mode 100644 index 0000000000000..fbcdaa68abbc0 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBinding.java @@ -0,0 +1,124 @@ +/** + * 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.velux.internal; + +import static org.apache.commons.lang.StringUtils.isNotBlank; + +import java.lang.reflect.Field; + +import org.apache.commons.lang.StringUtils; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * Class for Velux binding which validates the bridge configuration parameters. + * + *
    + *
  • {@link #VeluxBinding constructor}
  • + *
  • {@link #checked }
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + * @author Joachim Sauer (@Saua) - fix for isBulkRetrievalEnabled, isSequentialEnforced + */ +@NonNullByDefault +public class VeluxBinding extends VeluxBridgeConfiguration { + private final Logger logger = LoggerFactory.getLogger(getClass()); + + /*** + *** Startup methods + ***/ + + /** + * Constructor + * + * initializes the interface towards the Velux bridge. Furthermore, the checked configuration can be retrieved by + * the method {@link #checked checked}. + * + * @param uncheckedConfiguration + * The configuration of type {@link VeluxBridgeConfiguration} + * which shall be checked. + */ + public VeluxBinding(@Nullable VeluxBridgeConfiguration uncheckedConfiguration) { + logger.trace("VeluxBinding(constructor) called."); + if (logger.isTraceEnabled()) { + for (Field field : VeluxBridgeConfiguration.class.getFields()) { + if (!StringUtils.capitalize(field.getName()).contentEquals(field.getName())) { + logger.trace("VeluxBinding(): FYI: a potential configuration string is '{}'.", field.getName()); + } + } + } + if (uncheckedConfiguration == null) { + logger.debug("No configuration found, using default values."); + } else { + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_PROTOCOL); + if (isNotBlank(uncheckedConfiguration.protocol)) { + this.protocol = uncheckedConfiguration.protocol; + } + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_IPADDRESS); + if (isNotBlank(uncheckedConfiguration.ipAddress)) { + this.ipAddress = uncheckedConfiguration.ipAddress; + } + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_TCPPORT); + if ((uncheckedConfiguration.tcpPort > 0) && (uncheckedConfiguration.tcpPort <= 65535)) { + this.tcpPort = uncheckedConfiguration.tcpPort; + } + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_PASSWORD); + if (isNotBlank(uncheckedConfiguration.password)) { + this.password = uncheckedConfiguration.password; + } + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_TIMEOUT_MSECS); + if ((uncheckedConfiguration.timeoutMsecs > 0) && (uncheckedConfiguration.timeoutMsecs <= 10000)) { + this.timeoutMsecs = uncheckedConfiguration.timeoutMsecs; + } + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_RETRIES); + if ((uncheckedConfiguration.retries >= 0) && (uncheckedConfiguration.retries <= 10)) { + this.retries = uncheckedConfiguration.retries; + } + logger.trace("VeluxBinding(): checking {}.", VeluxBridgeConfiguration.BRIDGE_REFRESH_MSECS); + if ((uncheckedConfiguration.refreshMSecs > 0) && (uncheckedConfiguration.refreshMSecs <= 10000)) { + this.refreshMSecs = uncheckedConfiguration.refreshMSecs; + } + this.isBulkRetrievalEnabled = uncheckedConfiguration.isBulkRetrievalEnabled; + this.isSequentialEnforced = uncheckedConfiguration.isSequentialEnforced; + this.isProtocolTraceEnabled = uncheckedConfiguration.isProtocolTraceEnabled; + + } + logger.trace("VeluxBinding(constructor) done."); + } + + /** + * Access method returning a validated configuration. + * + * @return bridgeConfiguration of type {@link VeluxBridgeConfiguration + * VeluxBridgeConfiguration}. + */ + public VeluxBridgeConfiguration checked() { + logger.trace("checked() called."); + logger.debug("{}Config[{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={},{}={}]", + VeluxBindingConstants.BINDING_ID, VeluxBridgeConfiguration.BRIDGE_PROTOCOL, protocol, + VeluxBridgeConfiguration.BRIDGE_IPADDRESS, this.ipAddress, VeluxBridgeConfiguration.BRIDGE_TCPPORT, + tcpPort, VeluxBridgeConfiguration.BRIDGE_PASSWORD, password.replaceAll(".", "*"), + VeluxBridgeConfiguration.BRIDGE_TIMEOUT_MSECS, timeoutMsecs, VeluxBridgeConfiguration.BRIDGE_RETRIES, + retries, VeluxBridgeConfiguration.BRIDGE_REFRESH_MSECS, refreshMSecs, + VeluxBridgeConfiguration.BRIDGE_IS_BULK_RETRIEVAL_ENABLED, isBulkRetrievalEnabled, + VeluxBridgeConfiguration.BRIDGE_IS_SEQUENTIAL_ENFORCED, isSequentialEnforced, + VeluxBridgeConfiguration.BRIDGE_PROTOCOL_TRACE_ENABLED, isProtocolTraceEnabled); + logger.trace("checked() done."); + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.java new file mode 100644 index 0000000000000..f05d437a2df41 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConfig.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.velux.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This represents the configuration of a openHAB item that is binded to a Velux + * KLF200 Gateway. It contains the following information: + * + *
    + *
  • bindingItemType + *

    + * Accessible via + * {@link org.openhab.binding.velux.internal.VeluxBindingConfig#getBindingItemType + * getBindingItemType} as representation of the Velux device is filed in the Velux bridge.

  • + *
  • bindingConfig + *

    + * Accessible via + * {@link org.openhab.binding.velux.internal.VeluxBindingConfig#getBindingConfig getBindingConfig} containing the + * device-specific binding configuration + * as declared in the binding configuration (possibly adapted by preprocessing).

  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +class VeluxBindingConfig { + + private final Logger logger = LoggerFactory.getLogger(VeluxBindingConfig.class); + + /** + * The binding type of the velux item described by type {@link org.openhab.binding.velux.internal.VeluxItemType + * VeluxItemType}. + */ + private VeluxItemType bindingItemType; + + /** + * Device-specific binding configuration as declared in the binding configuration (possibly adapted by + * preprocessing). + */ + private String bindingConfig; + + /** + * Constructor of the VeluxBindingConfig. + * + * @param bindingItemType + * The Velux item type {@link org.openhab.binding.velux.internal.VeluxItemType + * VeluxItemType} + * which the Velux device is filed in the Velux bridge. + * @param bindingConfig + * The optional configuration type of the Velux binding. + */ + public VeluxBindingConfig(VeluxItemType bindingItemType, String bindingConfig) { + logger.trace("VeluxBindingConfig(constructor:{},{}) called.", bindingItemType, bindingConfig); + + this.bindingItemType = bindingItemType; + this.bindingConfig = bindingConfig; + } + + /** + * @return bindingTypeItem of type {@link org.openhab.binding.velux.internal.VeluxItemType + * VeluxItemType}. + */ + public VeluxItemType getBindingItemType() { + return this.bindingItemType; + } + + /** + * @return bindingConfig of type {@link String} that + * has been declared in the binding configuration, + * possibly adapted by preprocessing. + */ + public String getBindingConfig() { + return this.bindingConfig; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java new file mode 100644 index 0000000000000..6992a5bd05577 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingConstants.java @@ -0,0 +1,149 @@ +/** + * 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.velux.internal; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link VeluxBindingConstants} class defines common constants, which are + * used across the whole binding. + *

+ * For an in-depth view of the available Item types with description of parameters, take a look onto + * {@link org.openhab.binding.velux.internal.VeluxItemType VeluxItemType}. + *

+ * This class contains the Thing identifications: + *
    + *
  • {@link #THING_VELUX_BRIDGE} for the bridge itself,
  • + *
  • {@link #THING_VELUX_SCENE} for the scenes summarizing a set of actuator states,
  • + *
  • {@link #THING_VELUX_ACTUATOR} for the general actuators identified on the bridge,
  • + *
  • {@link #THING_VELUX_ROLLERSHUTTER} for the rollershutters identified on the bridge,
  • + *
  • {@link #THING_VELUX_WINDOW} for the windows identified on the bridge.
  • + *
  • {@link #THING_VELUX_VSHUTTER} for the virtual shutters defined in the configuration
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBindingConstants { + + /** Basic binding identification. */ + public static final String BINDING_ID = "velux"; + + // Id of support bridge + /** + * The Thing identification of the binding. + */ + private static final String THING_VELUX_BINDING = "binding"; + /** + * The Thing identification of the Velux bridge. + */ + private static final String THING_VELUX_BRIDGE = "klf200"; + /** + * The Thing identification of a scene defined on the Velux bridge. + */ + private static final String THING_VELUX_SCENE = "scene"; + /** + * The Thing identification of a generic actuator defined on the Velux bridge. + */ + private static final String THING_VELUX_ACTUATOR = "actuator"; + /** + * The Thing identification of a rollershutter defined on the Velux bridge. + */ + private static final String THING_VELUX_ROLLERSHUTTER = "rollershutter"; + /** + * The Thing identification of a window defined on the Velux bridge. + */ + private static final String THING_VELUX_WINDOW = "window"; + /** + * The Thing identification of a virtual shutter defined on the Velux bridge. + */ + private static final String THING_VELUX_VSHUTTER = "vshutter"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_BINDING = new ThingTypeUID(BINDING_ID, THING_VELUX_BINDING); + + // List of all Bridge Type UIDs + public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, THING_VELUX_BRIDGE); + + // List of all Thing Type UIDs beyond the bridge(s) + public static final ThingTypeUID THING_TYPE_VELUX_SCENE = new ThingTypeUID(BINDING_ID, THING_VELUX_SCENE); + public static final ThingTypeUID THING_TYPE_VELUX_ACTUATOR = new ThingTypeUID(BINDING_ID, THING_VELUX_ACTUATOR); + public static final ThingTypeUID THING_TYPE_VELUX_ROLLERSHUTTER = new ThingTypeUID(BINDING_ID, + THING_VELUX_ROLLERSHUTTER); + public static final ThingTypeUID THING_TYPE_VELUX_WINDOW = new ThingTypeUID(BINDING_ID, THING_VELUX_WINDOW); + public static final ThingTypeUID THING_TYPE_VELUX_VSHUTTER = new ThingTypeUID(BINDING_ID, THING_VELUX_VSHUTTER); + + // Definitions of different set of Things + public static final Set SUPPORTED_THINGS_BINDING = new HashSet<>(Arrays.asList(THING_TYPE_BINDING)); + public static final Set SUPPORTED_THINGS_BRIDGE = new HashSet<>(Arrays.asList(THING_TYPE_BRIDGE)); + public static final Set SUPPORTED_THINGS_ITEMS = new HashSet<>( + Arrays.asList(THING_TYPE_VELUX_SCENE, THING_TYPE_VELUX_ACTUATOR, THING_TYPE_VELUX_ROLLERSHUTTER, + THING_TYPE_VELUX_WINDOW, THING_TYPE_VELUX_VSHUTTER)); + + // *** List of all Channel ids *** + + // List of all binding channel ids + + /** Channel identifier describing the current Binding State. */ + public static final String CHANNEL_BINDING_INFORMATION = "information"; + + // List of all bridge channel ids + + /** Channel/Property identifier describing the current Bridge State. */ + public static final String CHANNEL_BRIDGE_STATUS = "status"; + public static final String CHANNEL_BRIDGE_RELOAD = "reload"; + public static final String CHANNEL_BRIDGE_DOWNTIME = "downtime"; + public static final String CHANNEL_BRIDGE_DO_DETECTION = "doDetection"; + + public static final String PROPERTY_BRIDGE_TIMESTAMP_SUCCESS = "connectionSuccess"; + public static final String PROPERTY_BRIDGE_TIMESTAMP_ATTEMPT = "connectionAttempt"; + public static final String PROPERTY_BRIDGE_FIRMWARE = "firmware"; + public static final String PROPERTY_BRIDGE_IPADDRESS = "ipAddress"; + public static final String PROPERTY_BRIDGE_SUBNETMASK = "subnetMask"; + public static final String PROPERTY_BRIDGE_DEFAULTGW = "defaultGW"; + public static final String PROPERTY_BRIDGE_DHCP = "DHCP"; + public static final String PROPERTY_BRIDGE_WLANSSID = "WLANSSID"; + public static final String PROPERTY_BRIDGE_WLANPASSWORD = "WLANPassword"; + + public static final String PROPERTY_BRIDGE_CHECK = "check"; + public static final String PROPERTY_BRIDGE_PRODUCTS = "products"; + public static final String PROPERTY_BRIDGE_SCENES = "scenes"; + + // List of all scene channel ids + public static final String CHANNEL_SCENE_ACTION = "action"; + public static final String CHANNEL_SCENE_SILENTMODE = "silentMode"; + + // List of all actuator channel ids + public static final String CHANNEL_ACTUATOR_POSITION = "position"; + public static final String CHANNEL_ACTUATOR_STATE = "state"; + public static final String CHANNEL_ACTUATOR_SILENTMODE = "silentMode"; + public static final String CHANNEL_ACTUATOR_LIMIT_MINIMUM = "limitMinimum"; + public static final String CHANNEL_ACTUATOR_LIMIT_MAXIMUM = "limitMaximum"; + + // List of all virtual shutter channel ids + public static final String CHANNEL_VSHUTTER_POSITION = "vposition"; + + // Helper definitions + public static final String BINDING_VALUES_SEPARATOR = ","; + public static final String OUTPUT_VALUE_SEPARATOR = ","; + public static final String UNKNOWN = "unknown"; + + // Critical issues to be reported will use the following message + public static final String LOGGING_CONTACT = "Please report to maintainer: "; + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingProperties.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingProperties.java new file mode 100644 index 0000000000000..1e71a932214a6 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxBindingProperties.java @@ -0,0 +1,61 @@ +/** + * 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.velux.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link VeluxBindingProperties} class defines common constants, which are + * used within the property definitions. + * + * This class contains the property identifications: + *
    + *
  • {@link #PROPERTY_BINDING_BUNDLEVERSION} for identification of the binding,
  • + *
  • {@link #PROPERTY_BINDING_NOOFBRIDGES} for number of bridges,
  • + *
  • {@link #PROPERTY_BINDING_NOOFTHINGS} for number of things,
  • + *
+ *
    + *
  • {@link #PROPERTY_SCENE_NAME} for defining the name of a scene,
  • + *
  • {@link #PROPERTY_SCENE_VELOCITY} for defining the velocity of a scene,
  • + *
+ *
    + *
  • {@link #CONFIG_ACTUATOR_SERIALNUMBER} for defining the serial number of an actuator, a rollershutter and a + * window,
  • + *
  • {@link #PROPERTY_ACTUATOR_NAME} for defining the name of an actuator, a rollershutter and a window,
  • + *
  • {@link #PROPERTY_ACTUATOR_INVERTED} for modifying the value of a Channel,
  • + *
+ *
    + *
  • {@link #PROPERTY_VSHUTTER_SCENELEVELS} for defining a virtual shutter.
  • + *
  • {@link #PROPERTY_VSHUTTER_CURRENTLEVEL} for defining a virtual shutter.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBindingProperties { + + public static final String PROPERTY_BINDING_BUNDLEVERSION = "bundleVersion"; + public static final String PROPERTY_BINDING_NOOFBRIDGES = "numberOfBridges"; + public static final String PROPERTY_BINDING_NOOFTHINGS = "numberOfThings"; + + public static final String PROPERTY_SCENE_NAME = "sceneName"; + public static final String PROPERTY_SCENE_VELOCITY = "velocity"; + + public static final String CONFIG_ACTUATOR_SERIALNUMBER = "serial"; + public static final String PROPERTY_ACTUATOR_NAME = "name"; + public static final String PROPERTY_ACTUATOR_INVERTED = "inverted"; + + public static final String PROPERTY_VSHUTTER_SCENELEVELS = "sceneLevels"; + public static final String PROPERTY_VSHUTTER_CURRENTLEVEL = "currentLevel"; + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java new file mode 100644 index 0000000000000..e582e4f5983b0 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxItemType.java @@ -0,0 +1,499 @@ +/** + * 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.velux.internal; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.items.GenericItem; +import org.eclipse.smarthome.core.library.items.NumberItem; +import org.eclipse.smarthome.core.library.items.RollershutterItem; +import org.eclipse.smarthome.core.library.items.StringItem; +import org.eclipse.smarthome.core.library.items.SwitchItem; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Enumeration of Types of a Velux item. + *
+ * Provides information about: + *
    + *
  • associated thing identified by String
  • + *
  • defined channel identified by String
  • + *
  • {@link #getItemClass} item class,
  • + *
  • {@link #isReadable} about a read possibility,
  • + *
  • {@link #isWritable} about a write possibility,
  • + *
  • {@link #isExecutable} about an execute possibility,
  • + *
  • {@link #isToBeRefreshed} about necessarily to be refreshed,
  • + *
  • {@link #isToBeRefreshedNow} about necessarily to be refreshed at this time,
  • + *
  • {@link #isChannel} as indication of being handled as Channel of a thing,
  • + *
  • {@link #isProperty} as indication of being handled as property of a thing.
  • + *
+ * + * In addition there are helper methods providing information about: + * + *
    + *
  • {@link #getIdentifier} returning the common identifier string,
  • + *
  • {@link #getByThingAndChannel} to retrieve an enum instance selected by Thing + * and Channel identifier,
  • + *
  • {@link #getPropertyEntriesByThing} to retrieve any Thing identifiers as array of + * String,
  • + *
+ *

+ * Within this enumeration, the expected behaviour of the OpenHAB item (resp. Channel or Property) is set. For each kind + * of Channel (i.e. bridge or device) parameter a set of information is defined: + *

    + *
  • + * Unique identification by: + *
      + *
    • Thing name as string,
    • + *
    • Channel name as string,
    • + *
    + *
  • + *
  • Channel type as OpenHAB type,
  • + *
  • ability flag whether this item is to be read,
  • + *
  • ability flag whether this item is able to be modified,
  • + *
  • ability flag whether this item is to be used as execution trigger.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + * + */ +@NonNullByDefault +public enum VeluxItemType { + // @formatter:off + UNKNOWN(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.UNKNOWN, TypeFlavor.UNUSABLE), + // + BINDING_INFORMATION(VeluxBindingConstants.THING_TYPE_BINDING, VeluxBindingConstants.CHANNEL_BINDING_INFORMATION, TypeFlavor.READONLY_VOLATILE_STRING), + // + BRIDGE_STATUS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_STATUS, TypeFlavor.READONLY_VOLATILE_STRING), + BRIDGE_DOWNTIME(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DOWNTIME, TypeFlavor.READONLY_VOLATILE_NUMBER), + BRIDGE_RELOAD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_RELOAD, TypeFlavor.INITIATOR), + BRIDGE_DO_DETECTION(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.CHANNEL_BRIDGE_DO_DETECTION, TypeFlavor.INITIATOR), + + BRIDGE_FIRMWARE(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_FIRMWARE, TypeFlavor.PROPERTY), + BRIDGE_IPADDRESS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_IPADDRESS, TypeFlavor.PROPERTY), + BRIDGE_SUBNETMASK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_SUBNETMASK, TypeFlavor.PROPERTY), + BRIDGE_DEFAULTGW(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_DEFAULTGW, TypeFlavor.PROPERTY), + BRIDGE_DHCP(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_DHCP, TypeFlavor.PROPERTY), + BRIDGE_WLANSSID(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_WLANSSID, TypeFlavor.PROPERTY), + BRIDGE_WLANPASSWORD(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_WLANPASSWORD, TypeFlavor.PROPERTY), + BRIDGE_PRODUCTS(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_PRODUCTS, TypeFlavor.PROPERTY), + BRIDGE_SCENES(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_SCENES, TypeFlavor.PROPERTY), + BRIDGE_CHECK(VeluxBindingConstants.THING_TYPE_BRIDGE, VeluxBindingConstants.PROPERTY_BRIDGE_CHECK, TypeFlavor.PROPERTY), + // + ACTUATOR_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), + ACTUATOR_STATE(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_STATE, TypeFlavor.MANIPULATOR_SWITCH), + ACTUATOR_LIMIT_MINIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MINIMUM,TypeFlavor.MANIPULATOR_SHUTTER), + ACTUATOR_LIMIT_MAXIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ACTUATOR, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MAXIMUM,TypeFlavor.MANIPULATOR_SHUTTER), + // + ROLLERSHUTTER_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), + ROLLERSHUTTER_LIMIT_MINIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MINIMUM,TypeFlavor.MANIPULATOR_SHUTTER), + ROLLERSHUTTER_LIMIT_MAXIMUM(VeluxBindingConstants.THING_TYPE_VELUX_ROLLERSHUTTER,VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MAXIMUM,TypeFlavor.MANIPULATOR_SHUTTER), + // + WINDOW_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), + WINDOW_LIMIT_MINIMUM(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MINIMUM,TypeFlavor.MANIPULATOR_SHUTTER), + WINDOW_LIMIT_MAXIMUM(VeluxBindingConstants.THING_TYPE_VELUX_WINDOW, VeluxBindingConstants.CHANNEL_ACTUATOR_LIMIT_MAXIMUM,TypeFlavor.MANIPULATOR_SHUTTER), + // + SCENE_ACTION(VeluxBindingConstants.THING_TYPE_VELUX_SCENE, VeluxBindingConstants.CHANNEL_SCENE_ACTION, TypeFlavor.INITIATOR), + SCENE_SILENTMODE(VeluxBindingConstants.THING_TYPE_VELUX_SCENE, VeluxBindingConstants.CHANNEL_SCENE_SILENTMODE, TypeFlavor.WRITEONLY_VOLATILE_SWITCH), + // + VSHUTTER_POSITION(VeluxBindingConstants.THING_TYPE_VELUX_VSHUTTER, VeluxBindingConstants.CHANNEL_VSHUTTER_POSITION, TypeFlavor.MANIPULATOR_SHUTTER), + ; + // @formatter:on + + private enum TypeFlavor { + /** + * Used to present read-only non-volatile configuration parameters as StringItem. + */ + READONLY_STATIC_STRING, + /** + * Used to present read-only non-volatile configuration parameters as SwitchItem. + */ + READONLY_STATIC_SWITCH, + /** + * Used to present volatile configuration parameters as StringItem. + */ + READONLY_VOLATILE_STRING, + /** + * Used to present volatile configuration parameters as NumberItem. + */ + READONLY_VOLATILE_NUMBER, + /** + * Used to present volatile configuration parameters as NumberItem. + */ + WRITEONLY_VOLATILE_SWITCH, + /** + * Used to present volatile configuration parameters as SwitchItem. + */ + READWRITE_VOLATILE_SWITCH, + /** + * Used to initiate an action. + */ + INITIATOR, + /** + * Used to manipulate an actuator. + */ + MANIPULATOR_SHUTTER, + /** + * Used to manipulate an actuator. + */ + MANIPULATOR_SWITCH, + /** + * Used to present read-only non-volatile configuration parameter (being handled as property of aThing). + */ + PROPERTY, + /** + * Used to define an UNUSABLE entry. + */ + UNUSABLE, + } + + /* + * *************************** + * ***** Private Objects ***** + */ + + private ThingTypeUID thingIdentifier; + private String channelIdentifier; + private Class itemClass; + private boolean itemIsReadable; + private boolean itemIsWritable; + private boolean itemIsExecutable; + private boolean itemIsToBeRefreshed; + private int itemsRefreshDivider; + private boolean itemIsChannel; + private boolean itemIsProperty; + + private static final Logger LOGGER = LoggerFactory.getLogger(VeluxItemType.class); + + private static final int REFRESH_CYCLE_FIRST_TIME = 0; + private static final int REFRESH_ONCE_A_DAY = 8640; + private static final int REFRESH_EACH_HOUR = 360; + private static final int REFRESH_EACH_MINUTE = 6; + private static final int REFRESH_EVERY_CYCLE = 1; + + /* + * ************************ + * ***** Constructors ***** + */ + + VeluxItemType(ThingTypeUID thingIdentifier, String channelIdentifier, TypeFlavor typeFlavor) { + this.thingIdentifier = thingIdentifier; + this.channelIdentifier = channelIdentifier; + this.itemIsChannel = true; + this.itemIsProperty = false; + switch (typeFlavor) { + case READONLY_STATIC_STRING: + this.itemClass = StringItem.class; + this.itemIsReadable = true; + this.itemIsWritable = false; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_ONCE_A_DAY; + break; + case READONLY_STATIC_SWITCH: + this.itemClass = SwitchItem.class; + this.itemIsReadable = true; + this.itemIsWritable = false; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_ONCE_A_DAY; + break; + + case READONLY_VOLATILE_STRING: + this.itemClass = StringItem.class; + this.itemIsReadable = true; + this.itemIsWritable = false; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_EACH_MINUTE; + break; + case READONLY_VOLATILE_NUMBER: + this.itemClass = NumberItem.class; + this.itemIsReadable = true; + this.itemIsWritable = false; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_EVERY_CYCLE; + break; + case WRITEONLY_VOLATILE_SWITCH: + this.itemClass = SwitchItem.class; + this.itemIsReadable = false; + this.itemIsWritable = true; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = false; + this.itemsRefreshDivider = REFRESH_EACH_MINUTE; + break; + case READWRITE_VOLATILE_SWITCH: + this.itemClass = SwitchItem.class; + this.itemIsReadable = true; + this.itemIsWritable = true; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_EVERY_CYCLE; + break; + + case INITIATOR: + this.itemClass = SwitchItem.class; + this.itemIsReadable = false; + this.itemIsWritable = false; + this.itemIsExecutable = true; + this.itemIsToBeRefreshed = false; + this.itemsRefreshDivider = 1; + break; + + case MANIPULATOR_SHUTTER: + this.itemClass = RollershutterItem.class; + this.itemIsReadable = true; + this.itemIsWritable = true; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_EACH_MINUTE; + break; + + case MANIPULATOR_SWITCH: + this.itemClass = SwitchItem.class; + this.itemIsReadable = true; + this.itemIsWritable = true; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_EACH_MINUTE; + break; + + case PROPERTY: + this.itemClass = StringItem.class; + this.itemIsReadable = true; + this.itemIsWritable = false; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = true; + this.itemsRefreshDivider = REFRESH_EACH_HOUR; + this.itemIsChannel = false; + this.itemIsProperty = true; + break; + + case UNUSABLE: + default: + this.itemClass = StringItem.class; + this.itemIsReadable = false; + this.itemIsWritable = false; + this.itemIsExecutable = false; + this.itemIsToBeRefreshed = false; + this.itemsRefreshDivider = REFRESH_ONCE_A_DAY; + this.itemIsChannel = false; + } + } + + /* + * ******************************** + * ***** Class access methods ***** + */ + + @Override + public String toString() { + return this.thingIdentifier + "/" + this.channelIdentifier; + } + + /** + * {@link VeluxItemType} access method to query Identifier on this type of item. + * + * @return thingIdentifier of type String describing the value of the enum {@link VeluxItemType} + * return + */ + public ThingTypeUID getThingTypeUID() { + return this.thingIdentifier; + } + + /** + * {@link VeluxItemType} access method to query common (channel/property) identifier on this type of item. + * + * @return channelIdentifier of type String describing the value of the enum {@link VeluxItemType}. + */ + public String getIdentifier() { + return this.channelIdentifier; + } + + /** + * {@link VeluxItemType} access method to query the appropriate type of item. + * + * @return itemClass of type Item describing the possible type of this item. + */ + public Class getItemClass() { + return this.itemClass; + } + + /** + * {@link VeluxItemType} access method to query Read possibility on this type of item. + * + * @return itemIsReadable of type boolean describing the ability to perform a write operation. + */ + public boolean isReadable() { + return this.itemIsReadable; + } + + /** + * {@link VeluxItemType} access method to query Write possibility on this type of item. + * + * @return itemIsWritable of type boolean describing the ability to perform a write operation. + */ + public boolean isWritable() { + return this.itemIsWritable; + } + + /** + * {@link VeluxItemType} access method to query Execute possibility on this type of item. + * + * @return isExecute of type boolean describing the ability to perform a write operation. + */ + public boolean isExecutable() { + return this.itemIsExecutable; + } + + /** + * {@link VeluxItemType} access method to query the need of refresh on this type of item. + * + * @return isExecute of type boolean describing the ability to perform a write operation. + */ + public boolean isToBeRefreshed() { + return this.itemIsToBeRefreshed; + } + + /** + * {@link VeluxItemType} access method to query the refreshMSecs interval on this type of item. + * + * @return refreshDivider of type int describing the factor. + */ + public int getRefreshDivider() { + return this.itemsRefreshDivider; + } + + /** + * {@link VeluxItemType} access method to query the type of this item. + * + * @return isChannel of type boolean describing the need to be handled as channel. + */ + public boolean isChannel() { + return this.itemIsChannel; + } + + /** + * {@link VeluxItemType} access method to query the type of this item. + * + * @return itemIsProperty of type boolean describing the need to be handled as property. + */ + public boolean isProperty() { + return this.itemIsProperty; + } + + /** + * {@link VeluxItemType} access method to find an enum by itemTypeName. + * + * @param itemTypeName as name of requested Thing of type String. + * + * @return veluxItemType of type VeluxItemType describing the appropriate enum. + */ + public VeluxItemType getByString(String itemTypeName) { + try { + return VeluxItemType.valueOf(itemTypeName); + } catch (IllegalArgumentException e) { + return UNKNOWN; + } + } + + /** + * {@link VeluxItemType} access method to find an enum by name. + * + * @param thingIdentifier as name of requested Thing of type String. + * @param channelIdentifier as name of requested Channel of type String. + * + * @return veluxItemType of type VeluxItemType describing the appropriate enum. + */ + public static VeluxItemType getByThingAndChannel(ThingTypeUID thingIdentifier, String channelIdentifier) { + for (VeluxItemType v : VeluxItemType.values()) { + if (thingIdentifier.equals(v.thingIdentifier) && channelIdentifier.equals(v.channelIdentifier)) { + LOGGER.trace("getByThingAndChannel({},{}) returns enum {}.", thingIdentifier, channelIdentifier, v); + return v; + } + } + LOGGER.trace("getByThingAndChannel({},{}) returns enum UNKNOWN.", thingIdentifier, channelIdentifier); + return UNKNOWN; + } + + /** + * {@link VeluxItemType} access method to find similar enum entries by thingIdentifier. + * + * @param thingIdentifier as name of requested Thing of type String. + * + * @return listOfveluxItemType of type List of VeluxItemType containing all similar enum entries. + */ + public static List getPropertyEntriesByThing(ThingTypeUID thingIdentifier) { + List list = new ArrayList(); + for (VeluxItemType v : VeluxItemType.values()) { + if (thingIdentifier.equals(v.thingIdentifier) && v.itemIsProperty) { + list.add(v); + } + } + LOGGER.trace("getPropertyEntriesByThing({}) returns {}.", thingIdentifier, list); + return list; + } + + /** + * Helper function: Calculate modulo. + * + * @param a as dividend. + * @param b as divisor. + * + * @return true if zero is remainder after division. + */ + private static boolean isModulo(int a, int b) { + return (a % b) == 0; + } + + /** + * {@link VeluxItemType} access method to determine the necessity of being refreshed + * within the current refresh cycle. + * + * @param refreshCycleCounter as identification of the refresh round. + * @param thingIdentifier as name of requested Thing. + * @param channelIdentifier as name of requested Channel. + * + * @return boolean value which expresses the need. + */ + public static boolean isToBeRefreshedNow(int refreshCycleCounter, ThingTypeUID thingIdentifier, + String channelIdentifier) { + VeluxItemType itemType = getByThingAndChannel(thingIdentifier, channelIdentifier); + + if (itemType == VeluxItemType.UNKNOWN) { + LOGGER.warn("isToBeRefreshedNow({},{},{}): returning false, as item is not found.", refreshCycleCounter, + thingIdentifier, channelIdentifier); + return false; + } + + if (((refreshCycleCounter == REFRESH_CYCLE_FIRST_TIME) && (itemType.isReadable())) + || (itemType.isToBeRefreshed())) { + if ((refreshCycleCounter == REFRESH_CYCLE_FIRST_TIME) + || (isModulo(refreshCycleCounter, itemType.getRefreshDivider()))) { + LOGGER.trace("isToBeRefreshedNow(): returning true, as item is to be refreshed, now."); + return true; + } else { + LOGGER.trace("isToBeRefreshedNow(): returning false, as refresh cycle has not yet come for this item."); + } + } else { + LOGGER.trace("isToBeRefreshedNow(): returning false, as item is not refreshable."); + } + return false; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java new file mode 100644 index 0000000000000..c5eddfabfa48e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/VeluxRSBindingConfig.java @@ -0,0 +1,212 @@ +/** + * 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.velux.internal; + +import java.util.Comparator; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +//import org.openhab.model.item.binding.BindingConfigParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This represents the configuration of a openHAB item that is binded to a Velux + * KLF200 Gateway. It contains the following information: + * + *
    + *
  • bindingItemType + *

    + * accessable via + * {@link org.openhab.binding.velux.internal.VeluxRSBindingConfig#getBindingItemType + * getBindingItemType} as representation of the Velux device is filed in the Velux bridge.

  • + *
  • bindingConfig + *

    + * accessable via + * {@link org.openhab.binding.velux.internal.VeluxRSBindingConfig#getBindingConfig getBindingConfig} containing the + * device-specific binding configuration + * as declared in the binding configuration (possibly adapted by preprocessing).

  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxRSBindingConfig extends VeluxBindingConfig { + + private final Logger logger = LoggerFactory.getLogger(VeluxRSBindingConfig.class); + + /** + * The ascending sorted list of generic Objects indexed by an Integer + */ + private SortedMap mapAscending = new TreeMap(new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o1.compareTo(o2); + } + }); + /** + * The descending sorted list of generic Objects indexed by an Integer + */ + private SortedMap mapDescending = new TreeMap(new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o2.compareTo(o1); + } + }); + + /** + * The sorted list of generic Objects indexed by an Integer + */ + private Integer rollershutterLevel = 0; + + private void veluxRollershutterBindingParser(final String channelValue) { + logger.debug("VeluxRollershutterBindingParser({}) called.", channelValue); + + String[] channelValueParts = channelValue.trim().split(VeluxBindingConstants.BINDING_VALUES_SEPARATOR); + if ((channelValueParts.length % 2) != 0) { + throw new IllegalArgumentException( + "Velux Rollershutter binding must contain an even number of configuration parts separated by '" + + VeluxBindingConstants.BINDING_VALUES_SEPARATOR + "' (ignoring binding '" + channelValue + + "')."); + } + + for (int idx = 0; idx < channelValueParts.length; idx++) { + logger.trace("VeluxRollershutterBindingParser() processing {}.", channelValueParts[idx]); + + int degree; + try { + degree = Integer.parseInt(channelValueParts[idx]); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + "Velux Rollershutter binding must contain an even number of configuration parts separated by '" + + VeluxBindingConstants.BINDING_VALUES_SEPARATOR + + "' each consisting of a shutter level followed by a scene name (ignoring binding '" + + channelValue + "')."); + } + idx++; + mapAscending.put(degree, channelValueParts[idx]); + mapDescending.put(degree, channelValueParts[idx]); + } + for (Map.Entry nextEntry : mapAscending.entrySet()) { + logger.trace("VeluxRollershutterBindingParser({},{}) processed.", nextEntry.getKey(), nextEntry.getValue()); + } + } + + /** + * Constructor of the VeluxBindingConfig. + * + * @param bindingItemType + * The Velux item type {@link org.openhab.binding.velux.internal.VeluxItemType + * VeluxItemType} which the Velux device is filed in the Velux bridge. + * @param channelValue + * The optional configuration type of the Velux binding. + * @param rollershutterLevel of type Integer with current position. + */ + public VeluxRSBindingConfig(VeluxItemType bindingItemType, String channelValue, Integer rollershutterLevel) { + super(bindingItemType, channelValue); + logger.trace("VeluxRSBindingConfig(constructor:{},{},{}) called.", bindingItemType, channelValue, + rollershutterLevel); + this.rollershutterLevel = rollershutterLevel; + veluxRollershutterBindingParser(channelValue); + } + + /** + * Constructor of the VeluxBindingConfig. + * + * @param bindingItemType + * The Velux item type {@link org.openhab.binding.velux.internal.VeluxItemType + * VeluxItemType} which the Velux device is filed in the Velux bridge. + * @param channelValue + * The optional configuration type of the Velux binding. + */ + public VeluxRSBindingConfig(VeluxItemType bindingItemType, String channelValue) { + super(bindingItemType, channelValue); + logger.trace("VeluxRSBindingConfig(constructor:{},{}) called.", bindingItemType, channelValue); + veluxRollershutterBindingParser(channelValue); + } + + /** + * Returns the next shutter level for an DOWN command w/ adjusting the actual position. + * + * @return rollershutterLevel of type Integer with next position after DOWN command. + */ + public Integer getNextAscendingLevel() { + logger.trace("getNextAscendingLevel() called."); + + for (Map.Entry nextEntry : mapAscending.entrySet()) { + if (nextEntry.getKey() > this.rollershutterLevel) { + this.rollershutterLevel = nextEntry.getKey(); + break; + } + } + logger.trace("getNextAscendingLevel() returning {}.", this.rollershutterLevel); + return this.rollershutterLevel; + + } + + /** + * Returns the next shutter level for an UP command w/ adjusting the actual position. + * + * @return rollershutterLevel of type Integer with next position after UP command. + */ + public Integer getNextDescendingLevel() { + logger.trace("getNextDescendingLevel() called."); + + for (Map.Entry nextEntry : mapDescending.entrySet()) { + if (nextEntry.getKey() < this.rollershutterLevel) { + this.rollershutterLevel = nextEntry.getKey(); + break; + } + } + logger.trace("getNextDescendingLevel() returning {}.", this.rollershutterLevel); + return this.rollershutterLevel; + + } + + /** + * Returns the current shutter level w/o adjusting the actual positioning. + * + * @return rollershutterLevel of type Integer with current position. + * + */ + public Integer getLevel() { + logger.trace("getLevel() returning {}.", this.rollershutterLevel); + return this.rollershutterLevel; + } + + /** + * Returns the scene name of the current shutter level w/o adjusting the actual positioning. + * + * @return sceneName + * A String describing the next scene. + */ + public String getSceneName() { + return getSceneName(this.rollershutterLevel); + } + + /** + * Returns the scene name w/o adjusting the actual positioning. + * + * @param level + * The shutter level is be queried. + * @return sceneName + * A String describing the next scene. + */ + public String getSceneName(Integer level) { + logger.trace("getSceneName({}) called.", level); + logger.trace("getSceneName() returning {}.", mapDescending.get(level)); + return mapDescending.get(level); + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridge.java new file mode 100644 index 0000000000000..6471e33f06f4a --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridge.java @@ -0,0 +1,284 @@ +/** + * 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.velux.internal.bridge; + +import java.util.Collections; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.bridge.common.Login; +import org.openhab.binding.velux.internal.bridge.common.Logout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 2nd Level I/O interface towards the Velux bridge. + * It provides methods for pre- and post-communication + * as well as a common method for the real communication. + * The following class access methods exist: + *
    + *
  • {@link VeluxBridge#bridgeLogin} for pre-communication,
  • + *
  • {@link VeluxBridge#bridgeLogout} for post-communication,
  • + *
  • {@link VeluxBridge#bridgeCommunicate} as method for the common communication.
  • + *
+ *

+ * Each protocol-specific implementation provides a publicly visible + * set of supported protocols as variable {@link #supportedProtocols}. + * As root of several inheritance levels it predefines an + * interfacing method {@link VeluxBridge#bridgeAPI} which + * has to be implemented by any kind of protocol-specific + * communication returning the appropriate base (1st) level + * communication method as well as any other gateway + * interaction with {@link #bridgeDirectCommunicate}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class VeluxBridge { + private final Logger logger = LoggerFactory.getLogger(VeluxBridge.class); + + /* + * *************************** + * ***** Private Objects ***** + */ + + private final String emptyAuthenticationToken = ""; + + // Type definitions, variables + + /** + * Support protocols for the concrete implementation. + *

+ * For protocol-specific implementations this value has to be adapted along the inheritance i.e. + * with the protocol-specific class values. + */ + public Set supportedProtocols = Collections.emptySet(); + + /** BridgeCommunicationProtocol authentication token for Velux Bridge. */ + protected String authenticationToken = emptyAuthenticationToken; + + /** + * Handler to access global bridge instance methods + * + */ + protected VeluxBridgeInstance bridgeInstance; + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * Constructor. + *

+ * Initializes the binding-wide instance for dealing with common informations and + * the Velux bridge connectivity settings by preparing the configuration settings with help + * by VeluxBridgeConfiguration. + * + * @param bridgeInstance refers to the binding-wide instance for dealing for common informations + * like existing actuators and predefined scenes. + */ + public VeluxBridge(VeluxBridgeInstance bridgeInstance) { + logger.trace("VeluxBridge(constructor,bridgeInstance={}) called.", bridgeInstance); + this.bridgeInstance = bridgeInstance; + logger.trace("VeluxBridge(constructor) done."); + } + + // Destructor methods + + /** + * Destructor. + *

+ * Deinitializes the binding-wide instance. + * + */ + public void shutdown() { + logger.trace("shutdown() called."); + } + + // Class access methods + + /** + * Determines whether the binding is already authenticated against the bridge so that + * any other communication can occur without an additional care about authentication. + *

+ * This method automatically decides on availability of the stored authentication + * information {@link VeluxBridge#authenticationToken} whether a (re-)authentication is possible. + * + * @return true if the bridge is authenticated; false otherwise. + */ + private boolean isAuthenticated() { + boolean success = (authenticationToken.length() > 0); + logger.trace("isAuthenticated() returns {}.", success); + return success; + } + + /** + * Declares the binding as unauthenticated against the bridge so that the next + * communication will take care about (re-)authentication. + */ + protected void resetAuthentication() { + logger.trace("resetAuthentication() called."); + authenticationToken = emptyAuthenticationToken; + return; + } + + /** + * Prepare an authorization request and communicate it with the Velux veluxBridge. + * If login is successful, the returned authorization token will be stored within this class + * for any further communication via {@link#bridgeCommunicate} up + * to an authorization with method {@link VeluxBridge#bridgeLogout}. + * + * @return true if the login was successful, and false otherwise. + */ + public synchronized boolean bridgeLogin() { + logger.trace("bridgeLogin() called."); + + Login bcp = bridgeAPI().login(); + bcp.setPassword(bridgeInstance.veluxBridgeConfiguration().password); + if (bridgeCommunicate(bcp, false)) { + logger.trace("bridgeLogin(): communication succeeded."); + if (bcp.isCommunicationSuccessful()) { + logger.trace("bridgeLogin(): storing authentication token for further access."); + authenticationToken = bcp.getAuthToken(); + return true; + } + } + return false; + } + + /** + * Prepare an authenticated deauthorization request and communicate it with the Velux veluxBridge. + * The authorization token stored in this class will be destroyed, so that the + * next communication has to start with {@link VeluxBridge#bridgeLogin}. + * + * @return true if the logout was successful, and false otherwise. + */ + public synchronized boolean bridgeLogout() { + logger.trace("bridgeLogout() called: emptying authentication token."); + authenticationToken = ""; + + Logout bcp = bridgeAPI().logout(); + if (bridgeCommunicate(bcp, false)) { + logger.trace("bridgeLogout(): communication succeeded."); + if (bcp.isCommunicationSuccessful()) { + logger.trace("bridgeLogout(): logout successful."); + return true; + } + } + return false; + } + + /** + * Initializes a client/server communication towards Velux veluxBridge + * based on the Basic I/O interface {@link VeluxBridge} and parameters + * passed as arguments (see below) and provided by VeluxBridgeConfiguration. + * + * @param communication the intended communication, + * that is request and response interactions as well as appropriate URL definition. + * @param useAuthentication whether to use authenticated communication. + * @return true if communication was successful, and false otherwise. + */ + private synchronized boolean bridgeCommunicate(BridgeCommunicationProtocol communication, + boolean useAuthentication) { + logger.trace("bridgeCommunicate({},{}authenticated) called.", communication.name(), + useAuthentication ? "" : "un"); + + if (!isAuthenticated()) { + if (useAuthentication) { + logger.trace("bridgeCommunicate(): no auth token available, aborting."); + return false; + } else { + logger.trace("bridgeCommunicate(): no auth token available, continuing."); + } + } + return bridgeDirectCommunicate(communication, useAuthentication); + } + + /** + * Initializes a client/server communication towards Velux Bridge + * based on the Basic I/O interface {@link VeluxBridge} and parameters + * passed as arguments (see below) and provided by VeluxBridgeConfiguration. + * This method automatically decides to invoke a login communication before the + * intended request if there has not been an authentication before. + * + * @param communication the intended communication, that is request and response interactions as well as appropriate + * URL definition. + * @return true if communication was successful, and false otherwise. + */ + public synchronized boolean bridgeCommunicate(BridgeCommunicationProtocol communication) { + logger.trace("bridgeCommunicate({}) called.", communication.name()); + if (!isAuthenticated()) { + bridgeLogin(); + } + return bridgeCommunicate(communication, true); + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last communication. + *

+ * If possible, it should be overwritten by protocol specific implementation. + *

+ * + * @return timestamp (default zero). + */ + public long lastCommunication() { + logger.trace("lastCommunication() returns zero."); + return 0L; + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last successful communication. + *

+ * If possible, it should be overwritten by protocol specific implementation. + *

+ * + * @return timestamp (default zero). + */ + public long lastSuccessfulCommunication() { + logger.trace("lastSuccessfulCommunication() returns zero."); + return 0L; + } + + /** + * Provides information about the base-level communication method and + * any kind of available gateway interaction. + *

+ * For protocol-specific implementations this method has to be overwritten along the inheritance i.e. + * with the protocol-specific class implementations. + * + * @return bridgeAPI of type {@link org.openhab.binding.velux.internal.bridge.common.BridgeAPI BridgeAPI}. + */ + public abstract BridgeAPI bridgeAPI(); + + /** + * Initializes a client/server communication towards Velux veluxBridge + * based on the protocol-specific implementations with common parameters + * passed as arguments (see below) and provided by VeluxBridgeConfiguration. + *

+ * For protocol-specific implementations this method has to be overwritten along the inheritance i.e. + * with the protocol-specific class implementations. + * + * @param communication Structure of interface type {@link BridgeCommunicationProtocol} describing the + * intended communication. + * @param useAuthentication boolean flag to decide whether to use authenticated communication. + * @return success of type boolean which signals the success of the communication. + */ + protected abstract boolean bridgeDirectCommunicate(BridgeCommunicationProtocol communication, + boolean useAuthentication); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeActuators.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeActuators.java new file mode 100644 index 0000000000000..8193fd2af431f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeActuators.java @@ -0,0 +1,168 @@ +/** + * 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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetProduct; +import org.openhab.binding.velux.internal.bridge.common.GetProducts; +import org.openhab.binding.velux.internal.things.VeluxExistingProducts; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeActuators} represents a complete set of transactions + * for retrieving of any available products into a structure {@link #channel} + * defined on the Velux bridge. + *

+ * It provides the methods: + *

    + *
  • {@link #getProducts} for retrieval of information from the bridge. + *
  • {@link #getChannel} for accessing the retrieved information. + *
  • {@link #autoRefresh} for retrieval of information for supporting the update the corresponding openHAB items. + *
+ * + * @see VeluxProduct + * @see VeluxExistingProducts + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeActuators { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeActuators.class); + + // Configuration constants + + /** + * Limitation of Discovery on parts of the System table + * + * Whereas the parameter {@link org.openhab.binding.velux.internal.things.VeluxKLFAPI#KLF_SYSTEMTABLE_MAX} + * represents the + * maximum size of the system table in general, for speed up of the discovery process + * a binding-internal limitation of number of possible devices is introduced. + */ + private static final int VELUXBINDING_SYSTEMTABLE_MAX = 16; + + // Type definitions, class-internal variables + + /** + * Actuator information consisting of: + *
    + *
  • isRetrieved (boolean), + *
  • existingProducts ({@link VeluxExistingProducts}). + *
+ */ + @NonNullByDefault + public class Channel { + public VeluxExistingProducts existingProducts = new VeluxExistingProducts(); + } + + private Channel channel; + + // Constructor methods + + /** + * Constructor. + *

+ * Initializes the internal data structure {@link #channel} of Velux actuators/products, + * which is publicly accessible via the method {@link #getChannel()}. + */ + public VeluxBridgeActuators() { + logger.trace("VeluxBridgeActuators(constructor) called."); + channel = new Channel(); + logger.trace("VeluxBridgeActuators(constructor) done."); + } + + // Class access methods + + /** + * Provide access to the internal structure of actuators/products. + * + * @return a channel describing the overall actuator situation. + */ + public Channel getChannel() { + return channel; + } + + /** + * Login into bridge, retrieve all products and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. The results + * are stored within {@link org.openhab.binding.velux.internal.things.VeluxExistingProducts + * VeluxExistingProducts}. + * + * @param bridge Initialized Velux bridge (communication) handler. + * @return true if successful, and false otherwise. + */ + public boolean getProducts(VeluxBridge bridge) { + logger.trace("getProducts() called."); + + GetProducts bcp = bridge.bridgeAPI().getProducts(); + GetProduct bcpSbS = bridge.bridgeAPI().getProduct(); + if ((bcpSbS != null) && !bridge.bridgeInstance.veluxBridgeConfiguration().isBulkRetrievalEnabled) { + logger.trace("getProducts() working on step-by-step retrieval."); + for (int nodeId = 0; nodeId < VeluxKLFAPI.KLF_SYSTEMTABLE_MAX + && nodeId < VELUXBINDING_SYSTEMTABLE_MAX; nodeId++) { + logger.trace("getProducts() working on product number {}.", nodeId); + bcpSbS.setProductId(nodeId); + if (bridge.bridgeCommunicate(bcpSbS) && bcpSbS.isCommunicationSuccessful()) { + VeluxProduct veluxProduct = bcpSbS.getProduct(); + if (bcpSbS.isCommunicationSuccessful()) { + logger.debug("getProducts() found product {}.", veluxProduct); + if (!channel.existingProducts.isRegistered(veluxProduct)) { + channel.existingProducts.register(veluxProduct); + } + } + } + } + } else { + logger.trace("getProducts() working on bulk retrieval."); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + for (VeluxProduct product : bcp.getProducts()) { + logger.trace("getProducts() found product {} (type {}).", product.getProductName(), + product.getProductType()); + if (!channel.existingProducts.isRegistered(product)) { + logger.debug("getProducts() storing new product {}.", product); + channel.existingProducts.register(product); + } else { + logger.debug("getProducts() storing updates for product {}.", product); + channel.existingProducts.update(product); + } + } + } else { + logger.trace("getProducts() finished with failure."); + return false; + } + } + logger.debug("getProducts() finally has found products {}.", channel.existingProducts); + return true; + } + + /** + * In case of an empty list of recognized products, the method will + * initiate a product retrieval using {@link #getProducts(VeluxBridge)}. + * + * @param bridge Initialized Velux bridge (communication) handler. + * @return true if at least one product was found, and false otherwise. + */ + public boolean autoRefresh(VeluxBridge bridge) { + int numberOfActuators = channel.existingProducts.getNoMembers(); + if (numberOfActuators == 0) { + logger.trace("autoRefresh(): is about to fetch existing products."); + getProducts(bridge); + } + return (numberOfActuators > 0); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDetectProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDetectProducts.java new file mode 100644 index 0000000000000..4dc15c8b755aa --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDetectProducts.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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.internal.bridge.common.RunProductDiscovery; +import org.openhab.binding.velux.internal.things.VeluxGwState; +import org.openhab.binding.velux.internal.things.VeluxGwState.VeluxGatewaySubState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeDetectProducts} represents a complete set of transactions + * for temporary activation of device detection mode on the Velux bridge. + *

+ * It therefore provides a method: + *

    + *
  • {@link VeluxBridgeDetectProducts#detectProducts} for starting the detection. + *
+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeDetectProducts { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeDetectProducts.class); + + // Class access methods + + /** + * Login into bridge, start process to detect (new) products, loop until bridge is idle again and logout from bridge + * based on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @return success + * of type boolean describing the overall result of this interaction. + */ + public boolean detectProducts(VeluxBridge bridge) { + logger.trace("detectProducts() called."); + boolean success = false; + + logger.trace("detectProducts() About to activate detection."); + RunProductDiscovery bcp1 = bridge.bridgeAPI().runProductDiscovery(); + if (!(bridge.bridgeCommunicate(bcp1)) || (bcp1.isCommunicationSuccessful())) { + while (true) { + logger.trace("detectProducts() About to query detection status."); + GetDeviceStatus bcp = bridge.bridgeAPI().getDeviceStatus(); + if (!(bridge.bridgeCommunicate(bcp)) || (bcp.isCommunicationSuccessful())) { + logger.trace("detectProducts() finished with failure."); + break; + } + VeluxGwState deviceStatus = bcp.getState(); + if (deviceStatus.getSubState() == (byte) VeluxGatewaySubState.GW_SS_P1.getStateValue()) { + logger.trace("detectProducts() bridge is still busy."); + } else if (deviceStatus.getSubState() == (byte) VeluxGatewaySubState.GW_SS_IDLE.getStateValue()) { + logger.trace("detectProducts() bridge is idle again, now."); + success = true; + break; + } else { + logger.info("detectProducts() unknown devicestatus ({}) received.", deviceStatus); + } + } + } else { + logger.trace("detectProducts() activate detection finished with failure."); + } + + logger.debug("detectProducts() finished {}.", success ? "successfully" : "with failure"); + return success; + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceCheckLostNodes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceCheckLostNodes.java new file mode 100644 index 0000000000000..7aa93ab84ec69 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceCheckLostNodes.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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.RunProductSearch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeDeviceCheckLostNodes} represents a complete set of transactions + * for querying device status on the Velux bridge. + *

+ * It therefore provides a method + *

    + *
  • {@link #initiate} for starting the detection. + *
+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeDeviceCheckLostNodes { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeDeviceCheckLostNodes.class); + + // Class access methods + + /** + * Login into bridge, query the bridge for device status and logout from bridge + * based on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + */ + public void initiate(VeluxBridge bridge) { + logger.trace("initiate() called."); + RunProductSearch bcp = bridge.bridgeAPI().runProductSearch(); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.trace("initiate() finished successfully."); + } else { + logger.trace("initiate() finished with failure."); + } + return; + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceStatus.java new file mode 100644 index 0000000000000..303bd36b1e2ef --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeDeviceStatus.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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.StringType; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.internal.things.VeluxGwState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeDeviceStatus} represents a complete set of transactions + * for querying device status on the Velux bridge. + *

+ * It therefore provides a method + *

    + *
  • {@link #retrieve} for starting the detection. + *
  • {@link #getChannel} for accessing the retrieved information. + *
+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeDeviceStatus { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeDeviceStatus.class); + + // Type definitions, class-internal variables + + /** + * Bridge information consisting of: + *
    + *
  • {@link #isRetrieved} describing the retrieval state, + *
  • {@link #gwState} containing the brief gateway state, + *
  • {@link #gwStateDescription} containing the verbose gateway state. + *
+ */ + @NonNullByDefault + public class Channel { + public boolean isRetrieved = false; + public StringType gwState = new StringType(VeluxBindingConstants.UNKNOWN); + public StringType gwStateDescription = new StringType(VeluxBindingConstants.UNKNOWN); + } + + private Channel channel; + + // Constructor methods + + /** + * Constructor. + *

+ * Initializes the internal data structure {@link #channel} of Velux actuators/products, + * which is publicly accessible via the method {@link #getChannel()}. + */ + public VeluxBridgeDeviceStatus() { + logger.trace("VeluxBridgeDeviceStatus(constructor) called."); + channel = new Channel(); + } + + // Class access methods + + /** + * Provide access to the internal structure of the device status. + * + * @return a channel describing the overall actual device status. + */ + public Channel getChannel() { + return channel; + } + + /** + * Complete workflow for retrieving the firmware version, consisting of Login into bridge, querying the firmware + * version and logout from bridge based on a well-prepared environment of a {@link VeluxBridgeProvider}, where the + * results are stored in {@link VeluxBridgeDeviceStatus#channel}. + * + * @param bridge Initialized Velux bridge handler. + * @return channel of type {@link VeluxBridgeDeviceStatus.Channel} describing the overall result of this + * interaction. + */ + public Channel retrieve(VeluxBridge bridge) { + logger.trace("retrieve() called. About to query device status."); + GetDeviceStatus bcp = bridge.bridgeAPI().getDeviceStatus(); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + VeluxGwState state = bcp.getState(); + channel.gwState = new StringType(state.toString()); + channel.gwStateDescription = new StringType(state.toDescription()); + channel.isRetrieved = true; + logger.trace("retrieve() finished successfully with result {}.", state.toDescription()); + } else { + channel.isRetrieved = false; + logger.trace("retrieve() finished with failure."); + } + return channel; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetFirmware.java new file mode 100644 index 0000000000000..ae3d62f36d1b9 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetFirmware.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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.StringType; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetFirmware; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeGetFirmware} represents a complete set of transactions + * for retrieving of firmware version string on the Velux bridge. + *

+ * It provides the following methods: + *

    + *
  • {@link #retrieve} for retrieval of information. + *
  • {@link #getChannel} for accessing the retrieved information. + *
+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeGetFirmware { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeGetFirmware.class); + + // Type definitions, class-internal variables + + /** + * Bridge information consisting of: + *
    + *
  • isRetrieved (boolean flag), + *
  • firmwareVersion (human readable String). + *
+ */ + @NonNullByDefault + public class Channel { + public boolean isRetrieved = false; + public StringType firmwareVersion = new StringType(VeluxBindingConstants.UNKNOWN); + } + + private Channel channel; + + // Constructor methods + + /** + * Constructor. + *

+ * Initializes the internal data structure {@link #channel} of Velux firmware information, + * which is publicly accessible via the method {@link #getChannel()}. + */ + public VeluxBridgeGetFirmware() { + logger.trace("VeluxBridgeGetFirmware(constructor) called."); + channel = new Channel(); + } + + // Class access methods + + /** + * Provide access to the internal structure of actuators/products. + * + * @return {@link Channel} describing the overall actuator situation. + */ + public Channel getChannel() { + return channel; + } + + /** + * Complete workflow for retrieving the firmware version, consisting of Login into bridge, querying the firmware + * version and logout from bridge based on a well-prepared environment of a {@link VeluxBridgeProvider}, where the + * results are stored in {@link VeluxBridgeGetFirmware#channel}. + * + * @param bridge Initialized Velux bridge handler. + * @return channel of type {@link VeluxBridgeGetFirmware.Channel} describing the overall result of this + * interaction. + */ + public Channel retrieve(VeluxBridge bridge) { + logger.trace("retrieve() called."); + + GetFirmware bcp = bridge.bridgeAPI().getFirmware(); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + this.channel.firmwareVersion = new StringType(bcp.getFirmware().getfirmwareVersion()); + this.channel.isRetrieved = true; + logger.trace("retrieve() found successfully firmware {}.", this.channel.firmwareVersion); + } else { + this.channel.isRetrieved = false; + logger.trace("retrieve() finished with failure."); + } + return channel; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetHouseStatus.java new file mode 100644 index 0000000000000..745ae6694b781 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetHouseStatus.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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetHouseStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeGetHouseStatus} represents a complete set of transactions + * for receiving the current state by the House Status Monitor on the Velux bridge. + *

+ * The HSM is responsible for continuous updates towards the communication initiator + * about any changes of actuator states. + *

+ * It therefore provides a method {@link VeluxBridgeGetHouseStatus#evaluateState} for check of availability of House + * Monitoring Messages. + * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeGetHouseStatus { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeGetHouseStatus.class); + + // Class access methods + + /** + * Login into bridge, fetch the HSM state and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @return true if successful or false otherwise. + */ + public boolean evaluateState(VeluxBridge bridge) { + logger.trace("evaluateState() called."); + + boolean success = false; + GetHouseStatus bcp = bridge.bridgeAPI().getHouseStatus(); + if (bcp != null) { + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + success = true; + } + } + logger.debug("evaluateState() finished {}.", (success ? "successfully" : "with failure")); + return success; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetLimitation.java new file mode 100644 index 0000000000000..2e7a05fbafac1 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeGetLimitation.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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetProductLimitation; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeGetLimitation} represents a complete set of transactions + * for retrieval of the limitation of an actuator defined on the Velux bridge. + *

+ * It therefore provides the methods + *

    + *
  • {@link VeluxBridgeGetLimitation#getMinimumLimitation} for querying the lower limitation of an actuator,
  • + *
  • {@link VeluxBridgeGetLimitation#getMaximumLimitation} for querying the high limitation of an actuator.
  • + *
+ * + * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeGetLimitation { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeGetLimitation.class); + + // Private Objects + + private VeluxProductPosition limitationResult = VeluxProductPosition.UNKNOWN; + + // Class access methods + + /** + * Login into bridge, instruct the bridge to pass a command towards an actuator based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param nodeId Number of Actuator to be modified. + * @return true if successful, and false otherwise. + */ + public boolean getMinimumLimitation(VeluxBridge bridge, int nodeId) { + logger.trace("getMinimumLimitation(nodeId={}) called.", nodeId); + + boolean success = false; + GetProductLimitation bcp = bridge.bridgeAPI().getProductLimitation(); + if (bcp != null) { + bcp.setActuatorIdAndLimitationType(nodeId, true); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + success = true; + limitationResult = new VeluxProductPosition(bcp.getLimitation()); + } + } + logger.debug("getMinimumLimitation() finished {}.", (success ? "successfully" : "with failure")); + return success; + } + + /** + * Login into bridge, instruct the bridge to pass a command towards an actuator based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param nodeId Number of Actuator to be modified. + * @return true if successful, and false otherwise. + */ + public boolean getMaximumLimitation(VeluxBridge bridge, int nodeId) { + logger.trace("getMaximumLimitation(nodeId={}) called.", nodeId); + + boolean success = false; + GetProductLimitation bcp = bridge.bridgeAPI().getProductLimitation(); + if (bcp != null) { + bcp.setActuatorIdAndLimitationType(nodeId, false); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + success = true; + limitationResult = new VeluxProductPosition(bcp.getLimitation()); + } + } + logger.debug("getMaximumLimitation() finished {}.", (success ? "successfully" : "with failure")); + return success; + } + + /** + * Return the limitation value. + * + * @return limitationResult of type VeluxProductPosition. + */ + public VeluxProductPosition getLimitation() { + return limitationResult; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeInstance.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeInstance.java new file mode 100644 index 0000000000000..edac94e74c4ae --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeInstance.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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration; +import org.openhab.binding.velux.internal.things.VeluxExistingProducts; +import org.openhab.binding.velux.internal.things.VeluxExistingScenes; + +/** + * This interface is implemented by classes that deal with a specific Velux bridge and its configuration. + *

+ * Configuration + *

+ *
    + *
  • {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration VeluxBridgeConfiguration} + * for specification by a specific Velux bridge,
  • + *
+ * + *

+ * Status + *

+ * Two methods for bridge-internal configuration retrieval: + *
    + *
  • {@link #existingProducts} + * for retrieving scene information,
  • + *
  • {@link #existingScenes} + * for retrieving product information.
  • + *
+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public interface VeluxBridgeInstance { + + /** + * Bridge configuration + * + * @return VeluxBridgeConfiguration containing all bridge configuration settings. + */ + public VeluxBridgeConfiguration veluxBridgeConfiguration(); + + /** + * Information retrieved by {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeActuators#getProducts} + * + * @return VeluxExistingProducts containing all registered products, or null in case of any error. + */ + public VeluxExistingProducts existingProducts(); + + /** + * Information retrieved by {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeScenes#getScenes} + * + * @return VeluxExistingScenes containing all registered scenes, or null in case of any error. + */ + public VeluxExistingScenes existingScenes(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeLANConfig.java new file mode 100644 index 0000000000000..0e98d7ed23931 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeLANConfig.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.velux.internal.bridge; + +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.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetLANConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeLANConfig} represents a complete set of transactions + * for retrieving the network configuration of the Velux bridge. + *

+ * It provides the following methods: + *

    + *
  • {@link #retrieve} for retrieval of information. + *
  • {@link #getChannel} for accessing the retrieved information. + *
+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeLANConfig { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeLANConfig.class); + + // Type definitions, class-internal variables + + /** + * IP Network configuration, consisting of: + *
    + *
  • isRetrieved (boolean flag), + *
  • ipAddress, + *
  • subnetMask, + *
  • defaultGW and + *
  • enabledDHCP. + *
+ */ + @NonNullByDefault + public class Channel { + public boolean isRetrieved = false; + public StringType openHABipAddress = new StringType(VeluxBindingConstants.UNKNOWN); + public StringType openHABsubnetMask = new StringType(VeluxBindingConstants.UNKNOWN); + public StringType openHABdefaultGW = new StringType(VeluxBindingConstants.UNKNOWN); + public OnOffType openHABenabledDHCP = OnOffType.OFF; + } + + private Channel channel; + + // Constructor methods + + /** + * Constructor. + *

+ * Initializes the internal data structure {@link #channel} of Velux LAN information, + * which is publicly accessible via the method {@link #getChannel()}. + */ + public VeluxBridgeLANConfig() { + logger.trace("VeluxBridgeLANConfig(constructor) called."); + channel = new Channel(); + } + + // Class access methods + + /** + * Provide access to the internal structure of LAN information. + * + * @return a channel describing the overall actual LAN information. + */ + public Channel getChannel() { + return channel; + } + + /** + * Complete workflow for retrieving the network configuration, consisting of Login into bridge, querying + * the network configuration and logout from bridge based on a well-prepared environment of a + * {@link VeluxBridgeProvider}, where the results are stored within as well in + * {@link VeluxBridgeLANConfig#channel}. + * + * @param bridge Initialized Velux bridge handler. + * @return channel of type {@link VeluxBridgeLANConfig.Channel} describing the overall result of this + * interaction. + */ + public Channel retrieve(VeluxBridge bridge) { + logger.trace("retrieve() called."); + + GetLANConfig bcp = bridge.bridgeAPI().getLANConfig(); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.trace("retrieve() found successfully configuration {}.", bcp.getLANConfig()); + channel.openHABipAddress = new StringType(bcp.getLANConfig().getIpAddress()); + channel.openHABsubnetMask = new StringType(bcp.getLANConfig().getSubnetMask()); + channel.openHABdefaultGW = new StringType(bcp.getLANConfig().getDefaultGW()); + channel.openHABenabledDHCP = bcp.getLANConfig().getDHCP() ? OnOffType.ON : OnOffType.OFF; + channel.isRetrieved = true; + } else { + channel.isRetrieved = false; + logger.trace("retrieve() finished with failure."); + } + return channel; + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeProvider.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeProvider.java new file mode 100644 index 0000000000000..5339554b6bfa3 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeProvider.java @@ -0,0 +1,59 @@ +/** + * 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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; + +/** + * This interface is implemented by classes that provide general communication with the Velux bridge. + *

+ * Communication + *

+ *
    + *
  • {@link VeluxBridgeProvider#bridgeCommunicate} for generic communication,
  • + *
  • {@link VeluxBridgeProvider#bridgeAPI} for access to all interaction/communication methods.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public interface VeluxBridgeProvider { + + /** + * Initializes a client/server communication towards Velux Bridge + * based on the Basic I/O interface {@link VeluxBridge} and parameters + * passed as arguments (see below) and provided by + * {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * This method automatically decides to invoke a login communication before the + * intended request if there has not been an authentication before. + * + * @param communication {@link BridgeCommunicationProtocol} describing the intended + * communication, that is request and response interactions as well as appropriate URL + * definition. + * @return true if communication was successful, and false otherwise. + */ + + public boolean bridgeCommunicate(BridgeCommunicationProtocol communication); + + /** + * Returns the class {@link BridgeAPI} which summarizes all interfacing methods. + * + * @return BridgeAPI + * containing all API methods. + */ + public @Nullable BridgeAPI bridgeAPI(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunProductCommand.java new file mode 100644 index 0000000000000..91c98bdc2c091 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunProductCommand.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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.RunProductCommand; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeRunProductCommand} represents a complete set of transactions + * for executing a scene defined on the Velux bridge. + *

+ * It provides a method {@link VeluxBridgeRunProductCommand#sendCommand} for sending a parameter change command. + * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeRunProductCommand { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeRunProductCommand.class); + + // Class access methods + + /** + * Login into bridge, instruct the bridge to pass a command towards an actuator based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param nodeId Number of Actuator to be modified. + * @param value Target value for Actuator main parameter. + * @return true if successful, and false otherwise. + */ + public boolean sendCommand(VeluxBridge bridge, int nodeId, VeluxProductPosition value) { + logger.trace("sendCommand(nodeId={},value={}) called.", nodeId, value); + + boolean success = false; + RunProductCommand bcp = bridge.bridgeAPI().runProductCommand(); + if (bcp != null) { + int veluxValue = value.getPositionAsVeluxType(); + + bcp.setNodeAndMainParameter(nodeId, veluxValue); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + success = true; + } + } + logger.debug("sendCommand() finished {}.", (success ? "successfully" : "with failure")); + return success; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunScene.java new file mode 100644 index 0000000000000..c4509699b3f8c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeRunScene.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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.RunScene; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeRunScene} represents a complete set of transactions + * for executing a scene defined on the Velux bridge. + *

+ * It provides a method {@link VeluxBridgeRunScene#execute} for execution of a scene. + * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeRunScene { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeRunScene.class); + + /** + * Login into bridge, execute a scene and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param sceneNo Number of scene to be executed. + * @return true if successful, and false otherwise. + */ + public boolean execute(VeluxBridge bridge, int sceneNo) { + logger.trace("execute({}) called.", sceneNo); + + RunScene bcp = bridge.bridgeAPI().runScene().setSceneId(sceneNo); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.debug("execute() finished successfully."); + return true; + } else { + logger.trace("execute() finished with failure."); + return false; + } + } + + /** + * Login into bridge, execute a scene and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param sceneNo Number of scene to be executed. + * @param velocity integer representing the velocity. + * @return true if successful, and false otherwise. + */ + public boolean execute(VeluxBridge bridge, int sceneNo, int velocity) { + logger.trace("execute({},{}) called.", sceneNo, velocity); + + RunScene bcp = bridge.bridgeAPI().runScene().setVelocity(velocity).setSceneId(sceneNo); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.debug("execute() finished successfully."); + return true; + } else { + logger.trace("execute() finished with failure."); + return false; + } + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeScenes.java new file mode 100644 index 0000000000000..4cee44d2b7864 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeScenes.java @@ -0,0 +1,130 @@ +/** + * 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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetScenes; +import org.openhab.binding.velux.internal.things.VeluxExistingScenes; +import org.openhab.binding.velux.internal.things.VeluxScene; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeScenes} represents a complete set of transactions + * for retrieving of any available scenes into a structure {@link VeluxExistingScenes} + * defined on the Velux bridge. + *

+ * It provides the following methods: + *

    + *
  • {@link #getScenes} for retrieval of information.
  • + *
  • {@link #getChannel} for accessing the retrieved information.
  • + *
  • {@link #autoRefresh} for retrieval of information in case of an empty list of actuators.
  • + *
+ * + * @see VeluxScene + * @see VeluxExistingScenes + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeScenes { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeScenes.class); + + // Type definitions, class-internal variables + + /** + * Actuator information consisting of: + *
    + *
  • existingScenes ({@link VeluxExistingScenes}). + *
+ */ + @NonNullByDefault + public class Channel { + public VeluxExistingScenes existingScenes = new VeluxExistingScenes(); + } + + private Channel channel; + + // Constructor methods + + /** + * Constructor. + *

+ * Initializes the internal data structure {@link #channel} of Velux scenes, + * which is publicly accessible via the method {@link #getChannel()}. + */ + public VeluxBridgeScenes() { + logger.trace("VeluxBridgeScenes(constructor) called."); + channel = new Channel(); + } + + // Class access methods + + /** + * Provide access to the internal structure of scenes. + * + * @return a channel describing the overall scenes situation. + */ + public Channel getChannel() { + return channel; + } + + /** + * Login into bridge, retrieve all scenes and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. The results + * are stored within a public structure {@link org.openhab.binding.velux.internal.things.VeluxExistingScenes + * VeluxExistingScenes}. + * + * @param bridge Initialized Velux bridge (communication) handler. + * @return true if successful, or false otherwise. + */ + public boolean getScenes(VeluxBridge bridge) { + logger.trace("getScenes() called."); + + GetScenes bcp = bridge.bridgeAPI().getScenes(); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + for (VeluxScene scene : bcp.getScenes()) { + logger.trace("getScenes() found scene {}.", scene.toString()); + + VeluxScene veluxScene = new VeluxScene(scene); + logger.trace("getScenes() storing scene {}.", veluxScene); + if (!channel.existingScenes.isRegistered(veluxScene)) { + channel.existingScenes.register(veluxScene); + } + logger.trace("getScenes() stored scene {}.", veluxScene); + } + logger.debug("getScenes() finally has found scenes {}.", channel.existingScenes); + return true; + } else { + logger.trace("getScenes() finished with failure."); + return false; + } + } + + /** + * In case of an empty list of recognized scenes, the method will + * initiate a product retrieval using {@link #getScenes(VeluxBridge)}. + * + * @param bridge Initialized Velux bridge (communication) handler. + * @return true if at lease one scene was found, and false otherwise. + */ + public boolean autoRefresh(VeluxBridge bridge) { + if (channel.existingScenes.getNoMembers() == 0) { + logger.trace("autoRefresh(): is about to fetch existing scenes."); + getScenes(bridge); + } + return (channel.existingScenes.getNoMembers() > 0); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetHouseStatusMonitor.java new file mode 100644 index 0000000000000..6e508b981e27f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetHouseStatusMonitor.java @@ -0,0 +1,63 @@ +/** + * 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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.SetHouseStatusMonitor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeSetHouseStatusMonitor} represents a complete set of transactions + * for modifying the service state of the House Status Monitor on the Velux bridge. + *

+ * The HSM is responsible for continuous updates towards the communication initiator + * about any changes of actuator states. + *

+ * It therefore provides a method {@link VeluxBridgeSetHouseStatusMonitor#modifyHSM} for modifying the HSM settings. + * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeSetHouseStatusMonitor { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeSetHouseStatusMonitor.class); + + // Class access methods + + /** + * Login into bridge, modify HSM and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param enableService Flag whether the HSM should be activated. + * @return true if successful or false otherwise. + */ + public boolean modifyHSM(VeluxBridge bridge, boolean enableService) { + logger.trace("modifyHSM({}) called.", enableService); + + boolean success = false; + SetHouseStatusMonitor bcp = bridge.bridgeAPI().setHouseStatusMonitor(); + if (bcp != null) { + bcp.serviceActivation(enableService); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + success = true; + } + } + logger.debug("modifyHSM() finished {}.", (success ? "successfully" : "with failure")); + return success; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetLimitation.java new file mode 100644 index 0000000000000..ffae910c35f42 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetLimitation.java @@ -0,0 +1,94 @@ +/** + * 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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.SetProductLimitation; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeSetLimitation} represents a complete set of transactions + * for modifying the limitation of an actuator defined on the Velux bridge. + *

+ * It therefore provides the methods + *

    + *
  • {@link VeluxBridgeSetLimitation#setMinimumLimitation} for modifying the lower limitation of an actuator,
  • + *
  • {@link VeluxBridgeSetLimitation#setMaximumLimitation} for modifying the high limitation of an actuator.
  • + *
+ * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeSetLimitation { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeSetLimitation.class); + + // Class access methods + + /** + * Login into bridge, modify the scene parameters and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param nodeId Number of Actuator to be modified. + * @param limitationMinimum new value for minimum limit. + * @return true if successful, and false otherwise. + */ + public boolean setMinimumLimitation(VeluxBridge bridge, int nodeId, VeluxProductPosition limitationMinimum) { + logger.trace("setMinimumLimitation(nodeId={}, limitation={}) called.", nodeId, limitationMinimum); + + SetProductLimitation bcp = bridge.bridgeAPI().setProductLimitation(); + if (bcp == null) { + logger.info("setMinimumLimitation(): aborting processing as there is handler available."); + return false; + } + bcp.setActuatorIdAndMinimumLimitation(nodeId, limitationMinimum.getPositionAsVeluxType()); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.trace("setMinimumLimitation() finished successfully."); + return true; + } + logger.trace("setMinimumLimitation() finished with failure."); + return false; + } + + /** + * Login into bridge, modify the scene parameters and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param nodeId Number of Actuator to be modified. + * @param limitationMaximum new value for maximum limit. + * @return true if successful, and false otherwise. + */ + public boolean setMaximumLimitation(VeluxBridge bridge, int nodeId, VeluxProductPosition limitationMaximum) { + logger.trace("setMaximumLimitation(nodeId={}, limitation={}) called.", nodeId, limitationMaximum); + + SetProductLimitation bcp = bridge.bridgeAPI().setProductLimitation(); + if (bcp == null) { + logger.info("setMaximumLimitation(): aborting processing as there is handler available."); + return false; + } + bcp.setActuatorIdAndMaximumLimitation(nodeId, limitationMaximum.getPositionAsVeluxType()); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.trace("setMaximumLimitation() finished successfully."); + return true; + } + logger.trace("setMaximumLimitation() finished with failure."); + return false; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetSceneVelocity.java new file mode 100644 index 0000000000000..d9c021c60299f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeSetSceneVelocity.java @@ -0,0 +1,61 @@ +/** + * 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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.SetSceneVelocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeSetSceneVelocity} represents a complete set of transactions + * for modifying the silent-mode of a scene defined on the Velux bridge. + *

+ * It therefore provides a method + *

    + *
  • {@link VeluxBridgeSetSceneVelocity#setSilentMode} for modifying the behaviour of a scene. + *
+ * Any parameters are controlled by {@link org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration}. + * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeSetSceneVelocity { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeSetSceneVelocity.class); + + // Class access methods + + /** + * Login into bridge, modify the scene parameters and logout from bridge based + * on a well-prepared environment of a {@link VeluxBridgeProvider}. + * + * @param bridge Initialized Velux bridge handler. + * @param sceneNo Number of scene to be modified. + * @param silentMode Mode of this mentioned scene. + * @return true if successful, and false otherwise. + */ + public boolean setSilentMode(VeluxBridge bridge, int sceneNo, boolean silentMode) { + logger.trace("setSilentMode({},{}) called.", sceneNo, silentMode); + + SetSceneVelocity bcp = bridge.bridgeAPI().setSceneVelocity(); + bcp.setMode(sceneNo, silentMode); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.trace("setSilentMode() finished successfully."); + return true; + } + logger.trace("setSilentMode() finished with failure."); + return false; + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeWLANConfig.java new file mode 100644 index 0000000000000..7949844d47b11 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/VeluxBridgeWLANConfig.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.velux.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.StringType; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetWLANConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxBridgeWLANConfig} represents a complete set of transactions + * for retrieving the wireless network configuration of the Velux bridge. + *

+ * It provides the following methods: + *

    + *
  • {@link #retrieve} for retrieval of information. + *
  • {@link #getChannel} for accessing the retrieved information. + *
+ *

+ * + * @see VeluxBridgeProvider + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeWLANConfig { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeWLANConfig.class); + + // Type definitions, class-internal variables + + /** + * Wireless network configuration, consisting of: + *

    + *
  • isRetrieved, + *
  • wlanSSID, + *
  • wlanPassword. + *
+ */ + @NonNullByDefault + public class Channel { + public boolean isRetrieved = false; + public StringType openHABwlanSSID = new StringType(VeluxBindingConstants.UNKNOWN); + public StringType openHABwlanPassword = new StringType(VeluxBindingConstants.UNKNOWN); + } + + private Channel channel; + + // Constructor methods + + /** + * Constructor. + *

+ * Initializes the internal data structure {@link #channel} of Velux WLAN information, + * which is publicly accessible via the method {@link #getChannel()}. + */ + public VeluxBridgeWLANConfig() { + logger.trace("VeluxBridgeWLANConfig(constructor) called."); + channel = new Channel(); + } + + // Class access methods + + /** + * Provide access to the internal structure of WLAN information. + * + * @return a channel describing the overall WLAN situation. + */ + public Channel getChannel() { + return channel; + } + + /** + * Complete workflow for retrieving the wireless network configuration, consisting of Login into bridge, querying + * the network configuration and logout from bridge based on a well-prepared environment of a + * {@link VeluxBridgeProvider}, where the results are stored within {@link VeluxBridgeWLANConfig#channel}. + * + * @param bridge Initialized Velux bridge handler. + * @return channel - or null - + * of type {@link VeluxBridgeWLANConfig.Channel} describing the overall result of this interaction. + */ + public Channel retrieve(VeluxBridge bridge) { + logger.trace("retrieve() called."); + + GetWLANConfig bcp = bridge.bridgeAPI().getWLANConfig(); + if (bridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + logger.trace("retrieve() found successfully configuration {}.", bcp.getWLANConfig()); + channel.openHABwlanSSID = new StringType(bcp.getWLANConfig().getSSID()); + channel.openHABwlanPassword = new StringType(bcp.getWLANConfig().getPassword()); + channel.isRetrieved = true; + } else { + channel.isRetrieved = false; + logger.trace("retrieve() finished with failure."); + } + return channel; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeAPI.java new file mode 100644 index 0000000000000..fe89f92816c61 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeAPI.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Definition of the 3rd Level I/O interface towards the Velux bridge. + *

+ * It provides the one-and-only protocol specific 1st-level communication class. + * Additionally it provides all methods for different gateway interactions. + *

+ * The following class access methods exist: + *

    + *
  • {@link #getDeviceStatus} for retrieving the bridge state (i.e. IDLE, BUSY, a.s.o),
  • + *
  • {@link #getFirmware} for retrieving the firmware version of the bridge,
  • + *
  • {@link #getHouseStatus} for retrieving the information about device state changes recognized by the + * bridge,
  • + *
  • {@link #getLANConfig} for retrieving the complete LAN information of the bridge,
  • + *
  • {@link #getProduct} for retrieving the any information about a device behind the bridge,
  • + *
  • {@link #getProductLimitation} for retrieving the limitation information about a device behind the + * bridge,
  • + *
  • {@link #getProducts} for retrieving the any information for all devices behind the bridge,
  • + *
  • {@link #getScenes} for retrieving the any information for all scenes defined on the bridge,
  • + *
  • {@link #getWLANConfig} for retrieving the complete WLAN information of the bridge,
  • + *
  • {@link #login} for establishing a trusted connectivity by authentication,
  • + *
  • {@link #logout} for tearing down the trusted connectivity by deauthentication,
  • + *
  • {@link #runProductCommand} for manipulation of a device behind the bridge (i.e. instructing to + * modify a position),
  • + *
  • {@link #runProductDiscovery} for activation of learning mode of the bridge to discovery new + * products,
  • + *
  • {@link #runProductIdentification} for human-oriented identification a device behind the bridge (i.e. + * by winking or switching on-and-off),
  • + *
  • {@link #runProductSearch} for searching for lost products on the bridge,
  • + *
  • {@link #runScene} for manipulation of a set of devices behind the bridge which are tied together as scene,
  • + *
  • {@link #setHouseStatusMonitor} for activation or deactivation of the house monitoring mode to be informed about + * device state changes recognized by the bridge,
  • + *
  • {@link #setSceneVelocity} for changes the velocity of a scene defined on the bridge (i.e. silent or + * fast mode).
  • + *
+ *

+ * Message semantic: Retrieval of Bridge configuration and information of devices beyond the bridge. + *

+ * + * It defines information how to send query and receive answer through the + * VeluxBridgeProvider as described by the BridgeCommunicationProtocol. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public interface BridgeAPI { + + Login login(); + + Logout logout(); + + @Nullable + SetHouseStatusMonitor setHouseStatusMonitor(); + + @Nullable + GetHouseStatus getHouseStatus(); + + RunProductDiscovery runProductDiscovery(); + + RunProductSearch runProductSearch(); + + RunProductIdentification runProductIdentification(); + + GetDeviceStatus getDeviceStatus(); + + GetFirmware getFirmware(); + + GetLANConfig getLANConfig(); + + GetWLANConfig getWLANConfig(); + + GetProducts getProducts(); + + @Nullable + GetProduct getProduct(); + + @Nullable + GetProductLimitation getProductLimitation(); + + @Nullable + SetProductLimitation setProductLimitation(); + + @Nullable + RunProductCommand runProductCommand(); + + GetScenes getScenes(); + + SetSceneVelocity setSceneVelocity(); + + RunScene runScene(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeCommunicationProtocol.java new file mode 100644 index 0000000000000..c71e26b602379 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/BridgeCommunicationProtocol.java @@ -0,0 +1,52 @@ +/** + * 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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Protocol independent bridge communication supported by the Velux bridge. + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 2nd level interface it defines the methods to help in sending a query and + * processing the received answer. + *

+ * Methods in this interface for the appropriate interaction: + *

    + *
  • {@link name} to return the name of the interaction for human interface.
  • + *
  • {@link isCommunicationSuccessful} to signal the success of the interaction (only available + * after storing the response).
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public interface BridgeCommunicationProtocol { + + /** + * Returns the name of this communication pair. + * + * @return name of the communication pair for human beings. + */ + public String name(); + + /** + * Returns the communication status included within the response message. + * + * @return true if the communication was successful, and false otherwise. + */ + public boolean isCommunicationSuccessful(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetDeviceStatus.java new file mode 100644 index 0000000000000..96451fc78beb4 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetDeviceStatus.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.things.VeluxGwState; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #getState} for retrieval of information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetDeviceStatus implements BridgeCommunicationProtocol { + /** + * Parameter of the device state. + * + * @return thisState as VeluxGwState describing the current status of the bridge. + */ + public abstract VeluxGwState getState(); +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetFirmware.java new file mode 100644 index 0000000000000..b1e1fc6398b49 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetFirmware.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.things.VeluxGwFirmware; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #getFirmware} for retrieving the Velux firmware information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetFirmware implements BridgeCommunicationProtocol { + + /** + * Retrieval of firmware version string + * + * @return firmware as VeluxGwFirmware describing the current software of the bridge. + */ + public abstract VeluxGwFirmware getFirmware(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetHouseStatus.java new file mode 100644 index 0000000000000..1bc2355232580 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetHouseStatus.java @@ -0,0 +1,32 @@ +/** + * 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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetHouseStatus implements BridgeCommunicationProtocol { + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetLANConfig.java new file mode 100644 index 0000000000000..f65d55d49822f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetLANConfig.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.things.VeluxGwLAN; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #getLANConfig} for retrieving the Velux LAN configuration information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetLANConfig implements BridgeCommunicationProtocol { + + /** + * Retrieval of the parameters of the LAN configuration. + * + * @return lanConfig as VeluxGwLAN describing the current status of the bridge. + */ + public abstract VeluxGwLAN getLANConfig(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java new file mode 100644 index 0000000000000..43fc41f0bc208 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProduct.java @@ -0,0 +1,51 @@ +/** + * 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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.things.VeluxProduct; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setProductId} for defining the intended product. + *
  • {@link #getProduct} for accessing the retrieved information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetProduct implements BridgeCommunicationProtocol { + + /** + * Set the intended node identifier to be queried + * + * @param nodeId Gateway internal node identifier (zero to 199) + */ + public abstract void setProductId(int nodeId); + + /** + * Retrieval of information about the selected product + * + * @return veluxProduct as VeluxProduct. + */ + public abstract VeluxProduct getProduct(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProductLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProductLimitation.java new file mode 100644 index 0000000000000..7c4d464633aa8 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProductLimitation.java @@ -0,0 +1,51 @@ +/** + * 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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setActuatorIdAndLimitationType} for defining the intended actuator and the query type. + *
  • {@link #getLimitation} for accessing the retrieved information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetProductLimitation implements BridgeCommunicationProtocol { + + /** + * Set the intended node identifier to be queried + * + * @param nodeId Gateway internal node identifier (zero to 199). + * @param getLimitationMinimum true, if we query for Minimum. + */ + public abstract void setActuatorIdAndLimitationType(int nodeId, boolean getLimitationMinimum); + + /** + * Retrieval of information about the selected product + * + * @return limitation as int. + */ + public abstract int getLimitation(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProducts.java new file mode 100644 index 0000000000000..73f5d31661f42 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetProducts.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.things.VeluxProduct; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #getProducts} for retrieving the Velux products/actuators information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + * + */ +@NonNullByDefault +public abstract class GetProducts implements BridgeCommunicationProtocol { + + /** + * Retrieval of information about all products + * + * @return arrayOfVeluxProducts as Array of VeluxProduct. + */ + public abstract VeluxProduct[] getProducts(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetScenes.java new file mode 100644 index 0000000000000..eb94d8d0eec16 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetScenes.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.things.VeluxScene; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #getScenes} for retrieving the Velux scenes information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetScenes implements BridgeCommunicationProtocol { + + /** + * Retrieval of information about all defined scenes + * + * @return arrayOfScenes as Array of VeluxScene describing the current scene configuration of the bridge. + */ + public abstract VeluxScene[] getScenes(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetWLANConfig.java new file mode 100644 index 0000000000000..d8d51b17e359c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/GetWLANConfig.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.things.VeluxGwWLAN; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #getWLANConfig} for retrieving the Velux WLAN configuration information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class GetWLANConfig implements BridgeCommunicationProtocol { + + /** + * Retrieval of the parameters of the wireless LAN configuration. + * + * @return wlanConfig as VeluxGwWLAN describing the current status of the bridge. + */ + public abstract VeluxGwWLAN getWLANConfig(); +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Login.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Login.java new file mode 100644 index 0000000000000..d2eefeb074d0c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Login.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Communication to authenticate itself, resulting in a return of current bridge state. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setPassword} for defining the intended authentication value. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class Login implements BridgeCommunicationProtocol { + + /** + * Sets the intended password string to be used for authentication + * + * @param thisPassword Password passed as String. + */ + public void setPassword(String thisPassword) { + } + + /** + * Returns the authentication information optionally to be used for later following + * messages. + * + * @return authentication token as String which can be used for next operations. + */ + public String getAuthToken() { + return ""; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Logout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Logout.java new file mode 100644 index 0000000000000..7bcd9c68f1e95 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/Logout.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Communication to authenticate itself, resulting in a return of current bridge state. + *

+ * Note: even before the deauthentication, an authentication is intended. + *

+ * Each protocol-specific implementation has to provide the common + * methods defined by {@link BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class Logout implements BridgeCommunicationProtocol { +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductCommand.java new file mode 100644 index 0000000000000..e70247b0a9913 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductCommand.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setNodeAndMainParameter} for defining the intended node and the main parameter value. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class RunProductCommand implements BridgeCommunicationProtocol { + + /** + * Modifies the state of an actuator + * + * @param actuatorId Gateway internal actuator identifier (zero to 199). + * @param parameterValue target device state. + * @return reference to the class instance. + */ + public abstract RunProductCommand setNodeAndMainParameter(int actuatorId, int parameterValue); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductDiscovery.java new file mode 100644 index 0000000000000..8bd6bd571d3b7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductDiscovery.java @@ -0,0 +1,32 @@ +/** + * 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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * Each protocol-specific implementation has to provide the common + * methods defined by {@link BridgeCommunicationProtocol}. + * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class RunProductDiscovery implements BridgeCommunicationProtocol { + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductIdentification.java new file mode 100644 index 0000000000000..5b7c813c7791f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductIdentification.java @@ -0,0 +1,45 @@ +/** + * 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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setProductId} for defining the intended node. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class RunProductIdentification implements BridgeCommunicationProtocol { + + /** + * Set the intended node identifier to be identified + * + * @param id Gateway internal node identifier (zero to 199) + * @return reference to the class instance. + */ + public RunProductIdentification setProductId(int id) { + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductSearch.java new file mode 100644 index 0000000000000..1b5e4777cf5f3 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunProductSearch.java @@ -0,0 +1,32 @@ +/** + * 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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * Each protocol-specific implementation has to provide the common + * methods defined by {@link BridgeCommunicationProtocol}. + * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class RunProductSearch implements BridgeCommunicationProtocol { + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunScene.java new file mode 100644 index 0000000000000..4c31645a087b1 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/RunScene.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setSceneId} for defining the intended scene. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class RunScene implements BridgeCommunicationProtocol { + + /** + * Sets the intended scene identifier to be executed + * + * @param id Gateway internal scene identifier + * @return reference to the class instance. + */ + public RunScene setSceneId(int id) { + return this; + } + + /** + * Sets the intended scene velocity for later execution + * + * @param velocity setting as String. + * @return reference to the class instance. + */ + public RunScene setVelocity(int velocity) { + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetHouseStatusMonitor.java new file mode 100644 index 0000000000000..26f7c6f8f64ad --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetHouseStatusMonitor.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #serviceActivation} for defining the intended parameter value. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class SetHouseStatusMonitor implements BridgeCommunicationProtocol { + + /** + * Modifies the intended state of the gateway-internal house monitoring settings. + * + * @param enableService as boolean. + * @return reference to the class instance. + */ + public abstract SetHouseStatusMonitor serviceActivation(boolean enableService); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetProductLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetProductLimitation.java new file mode 100644 index 0000000000000..ccf6c66a67d56 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetProductLimitation.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setActuatorIdAndMinimumLimitation} for defining the intended actuator and the minimum limitation + * value,
  • + *
  • {@link #setActuatorIdAndMaximumLimitation} for defining the intended actuator and the maximum limitation + * value.
  • + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class SetProductLimitation implements BridgeCommunicationProtocol { + + /** + * Set the intended node identifier to be queried + * + * @param nodeId Gateway internal node identifier (zero to 199). + * @param limitationMinimum Minimum Restriction value. + */ + public abstract void setActuatorIdAndMinimumLimitation(int nodeId, int limitationMinimum); + + /** + * Set the intended node identifier to be queried + * + * @param nodeId Gateway internal node identifier (zero to 199). + * @param limitationMaximum Maximum Restriction value. + */ + public abstract void setActuatorIdAndMaximumLimitation(int nodeId, int limitationMaximum); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetSceneVelocity.java new file mode 100644 index 0000000000000..df54d191c1d82 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/SetSceneVelocity.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.velux.internal.bridge.common; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Common bridge communication message scheme supported by the Velux bridge. + *

+ * Message semantic will be defined by the implementations according to the different comm paths. + *

+ * In addition to the common methods defined by {@link BridgeCommunicationProtocol} + * each protocol-specific implementation has to provide the following methods: + *

    + *
  • {@link #setMode} for retrieval of information. + *
+ * + * @see BridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class SetSceneVelocity implements BridgeCommunicationProtocol { + + public abstract SetSceneVelocity setMode(int id, boolean silent); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/package-info.java new file mode 100644 index 0000000000000..b17c564ad2d8c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/common/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * Interface definitions being used to implement the protocol-dependent interactions to IO-homecontrolled devices. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.bridge.common; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetDeviceStatus.java new file mode 100644 index 0000000000000..024322de4807f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetDeviceStatus.java @@ -0,0 +1,170 @@ +/** + * 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.velux.internal.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.internal.things.VeluxGwState; +import org.openhab.binding.velux.internal.things.VeluxGwState.VeluxGatewayState; +import org.openhab.binding.velux.internal.things.VeluxGwState.VeluxGatewaySubState; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Retrieval of current bridge state. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCgetDeviceStatus extends GetDeviceStatus implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/device"; + private static final String DESCRIPTION = "get device status"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + + /** + * Bridge I/O Request message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * for serializing. + * + * Resulting JSON: + * + *

+     * {"action":"getDeviceStatus","params":{}}
+     * 
+ * + * NOTE: the gateway software is extremely sensitive to this exact JSON structure. + * Any modifications (like omitting empty params) will lead to an gateway error. + */ + @NonNullByDefault + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "getDeviceStatus"; + this.params = new HashMap(); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"discovering",       or "IDLE"
+     *  "data":{},
+     *  "errors":[]
+     * }
+     * 
+ */ + @NonNullByDefault + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data = null; + private String[] errors = {}; + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.result; + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /* + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public VeluxGwState getState() { + String deviceStatus = this.getDeviceStatus(); + byte stateValue = (byte) VeluxGatewayState.GW_S_GWM.getStateValue(); + byte subStateValue; + if ("discovering".equals(deviceStatus)) { + subStateValue = (byte) VeluxGatewaySubState.GW_SS_P1.getStateValue(); + } else if ("IDLE".equals(deviceStatus)) { + subStateValue = (byte) VeluxGatewaySubState.GW_SS_IDLE.getStateValue(); + } else { + subStateValue = (byte) VeluxGatewaySubState.GW_SS_P2.getStateValue(); + } + return new VeluxGwState(stateValue, subStateValue); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetFirmware.java new file mode 100644 index 0000000000000..dc6dc7f4d5bff --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetFirmware.java @@ -0,0 +1,166 @@ +/** + * 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.velux.internal.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetFirmware; +import org.openhab.binding.velux.internal.things.VeluxGwFirmware; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Retrieval of Bridge configuration. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCgetFirmware extends GetFirmware implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/settings"; + private static final String DESCRIPTION = "get firmware version"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + + /** + * Bridge I/O Request message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"getFirmware","params":{}}
+     * 
+ */ + @NonNullByDefault + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "getFirmware"; + this.params = new HashMap(); + } + } + + /** + * Bridge Communication Structure containing the version of the firmware. + *

+ * Used within structure {@link JCgetFirmware} to describe the software of the Bridge. + */ + @NonNullByDefault + private static class BCfirmwareVersion { + /* + * "version": "0.1.1.0.41.0" + */ + private String version = VeluxBindingConstants.UNKNOWN; + } + + /** + * Bridge I/O Response message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge} for + * deserializing with including component access methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"IDLE",
+     *  "data":{"version":"0.1.1.0.41.0"},
+     *  "errors":[]
+     * }
+     * 
+ */ + @NonNullByDefault + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + private BCfirmwareVersion data = new BCfirmwareVersion(); + private String[] errors = {}; + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.result; + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /** + * Methods in addition to interface {@link JsonBridgeCommunicationProtocol}. + */ + @Override + public VeluxGwFirmware getFirmware() { + VeluxGwFirmware gwFirmware = new VeluxGwFirmware(response.data.version); + return gwFirmware; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetLANConfig.java new file mode 100644 index 0000000000000..27b40b8e24afb --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetLANConfig.java @@ -0,0 +1,190 @@ +/** + * 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.velux.internal.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.bridge.common.GetLANConfig; +import org.openhab.binding.velux.internal.things.VeluxGwLAN; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Retrieval of LAN configuration. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCgetLANConfig extends GetLANConfig implements BridgeCommunicationProtocol, JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/lan"; + private static final String DESCRIPTION = "get LAN configuration"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"get","params":{}}
+     * 
+ */ + @NonNullByDefault + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "get"; + this.params = new HashMap(); + } + } + + /** + * Bridge Communication Structure containing the network parameters. + *

+ * Used within structure {@link JCgetLANConfig} to describe the network connectivity of the Bridge. + * + *

+     * {"ipAddress":"192.168.45.9","subnetMask":"255.255.255.0","defaultGateway":"192.168.45.129","dhcp":false}
+     * 
+ */ + @NonNullByDefault + private static class BCLANConfig { + private String ipAddress = VeluxBindingConstants.UNKNOWN; + private String subnetMask = VeluxBindingConstants.UNKNOWN; + private String defaultGateway = VeluxBindingConstants.UNKNOWN; + private boolean dhcp; + + @Override + public String toString() { + return String.format("ipAddress=%s,subnetMask=%s,defaultGateway=%s,dhcp=%s", this.ipAddress, + this.subnetMask, this.defaultGateway, this.dhcp ? "on" : "off"); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for unmarshalling with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"IDLE",
+     *  "data":"ipAddress":"192.168.45.9","subnetMask":"255.255.255.0","defaultGateway":"192.168.45.129","dhcp":false},
+     *  "errors":[]
+     * }
+     * 
+ */ + @NonNullByDefault + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + private BCLANConfig data = new BCLANConfig(); + private String[] errors = {}; + + public boolean getResult() { + return result; + } + + public String getDeviceStatus() { + return deviceStatus; + } + + public String[] getErrors() { + return errors; + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object response) { + this.response = (Response) response; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.getDeviceStatus(); + } + + @Override + public String[] getErrors() { + return response.getErrors(); + } + + /** + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + @Override + public VeluxGwLAN getLANConfig() { + VeluxGwLAN gwLAN = new VeluxGwLAN(response.data.ipAddress, response.data.subnetMask, + response.data.defaultGateway, response.data.dhcp); + return gwLAN; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetProducts.java new file mode 100644 index 0000000000000..c0aca4b0e632c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetProducts.java @@ -0,0 +1,197 @@ +/** + * 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.velux.internal.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetProducts; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProductName; +import org.openhab.binding.velux.internal.things.VeluxProductType; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Retrieval of products. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCgetProducts extends GetProducts implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/products"; + private static final String DESCRIPTION = "get Products"; + + private Request request = new Request(); + private Response response = new Response(); + + /** + * Bridge Communication class describing a product + * + *

+     * "name": "Rolladen Bad",
+     * "category": "Roller shutter",
+     * "id": 2,
+     * "typeId": 2,
+     * "subtype": 0,
+     * "scenes": [
+     * "V_DG_Shutter_Mitte_000",
+     * "V_DG_Shutter_Mitte_085",
+     * "V_DG_Shutter_Mitte_100"
+     * ]
+     * 
+ */ + @NonNullByDefault + private class BCproduct { + private String name = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private String category = VeluxBindingConstants.UNKNOWN; + private int id; + private int typeId; + @SuppressWarnings("unused") + private int subtype; + @SuppressWarnings("unused") + private String[] scenes = {}; + } + + /** + * Bridge I/O Request message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"get","params":{}}
+     * 
+ */ + @NonNullByDefault + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "get"; + this.params = new HashMap(); + } + } + + /** + * Bridge I/O Response message used by {@link org.openhab.binding.velux.internal.bridge.VeluxBridge VeluxBridge} for + * deserialization with including component access methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     * "token": "pESIc/9zDWa1CJR6hCDzLw==",
+     * "result": true,
+     * "deviceStatus": "IDLE",
+     * "data": [
+     *  { "name": "Bad",
+     *    "category": "Window opener",
+     *    "id": 0,
+     *    "typeId": 4,
+     *    "subtype": 1,
+     *    "scenes": [
+     *       "V_DG_Window_Mitte_000",
+     *       "V_DG_Window_Mitte_100"
+     *    ]
+     *  },
+     * ],
+     * "errors": []
+     * }
+     * 
+ */ + @NonNullByDefault + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + private JCgetProducts.BCproduct[] data = {}; + private String[] errors = {}; + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.result; + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /** + * Methods in addition to interface {@link JsonBridgeCommunicationProtocol}. + */ + @Override + public VeluxProduct[] getProducts() { + VeluxProduct[] products = new VeluxProduct[response.data.length]; + for (int productIdx = 0; productIdx < response.data.length; productIdx++) { + products[productIdx] = new VeluxProduct(new VeluxProductName(response.data[productIdx].name), + VeluxProductType.get(response.data[productIdx].typeId), + new ProductBridgeIndex(response.data[productIdx].id)); + } + return products; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetScenes.java new file mode 100644 index 0000000000000..127ef8c471a68 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetScenes.java @@ -0,0 +1,229 @@ +/** + * 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.velux.internal.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetScenes; +import org.openhab.binding.velux.internal.things.VeluxProductName; +import org.openhab.binding.velux.internal.things.VeluxProductReference; +import org.openhab.binding.velux.internal.things.VeluxProductState; +import org.openhab.binding.velux.internal.things.VeluxScene; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Retrieval of scene configurations. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCgetScenes extends GetScenes implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/scenes"; + private static final String DESCRIPTION = "get Scenes"; + + private Request request = new Request(); + private Response response = new Response(); + + /** + * Bridge Communication Structure containing the state of a product. + *

+ * Therefore it includes the typeId and name identifying the product, as well as actuator and status. + *

+ * Used within structure {@link BCscene} to describe the final states of the products belonging to this scene. + * + *

+     * "typeId": 2,
+     * "name": "Rolladen Schlafzimmer",
+     * "actuator": 0,
+     * "status": 0
+     * 
+ */ + @NonNullByDefault + private static class BCproductState { + private int typeId; + private String name = VeluxBindingConstants.UNKNOWN; + private int actuator; + private int status; + } + + /** + * Bridge Communication Structure containing a scene with different states of products. + *

+ * Therefore it includes the name and id identifying the scene, a flag about silence-mode, as well as the different + * states. + *

+ * These states are defined by an array of {@link BCproductState} as part of this structure. + * + *

+     * {
+     * "name": "V_DG_Shutter_West_100",
+     * "id": 0,
+     * "silent": true,
+     * "bCproductStates": [
+     * {
+     * "typeId": 2,
+     * "name": "Rolladen Schlafzimmer",
+     * "actuator": 0,
+     * "status": 100
+     * }
+     * ]
+     * },
+     * 
+ */ + @NonNullByDefault + private static class BCscene { + private String name = VeluxBindingConstants.UNKNOWN; + private int id; + private boolean silent; + private BCproductState[] products = {}; + } + + /** + * Bridge I/O Request message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"get","params":{}}
+     * 
+ */ + @NonNullByDefault + private static class Request { + + @SuppressWarnings("unused") + private String action; + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "get"; + this.params = new HashMap(); + } + } + + /** + * Bridge Communication Structure describing a response to be received from the Velux Bridge. + * + *
+     * {
+     * "token": "kWwXRQ5mlwgYfvk23g2zXw==",
+     * "result": true,
+     * "deviceStatus": "IDLE",
+     * "data": [
+     * {
+     * "name": "V_DG_Shutter_West_100",
+     * "id": 0,
+     * "silent": true,
+     * "bCproductStates": [
+     * {
+     * "typeId": 2,
+     * "name": "Rolladen Schlafzimmer",
+     * "actuator": 0,
+     * "status": 100
+     * }
+     * ]
+     * },
+     * "errors": []
+     * }
+     * 
+ */ + @NonNullByDefault + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + private BCscene[] data = {}; + private String[] errors = {}; + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.result; + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /** + * Methods in addition to interface {@link JsonBridgeCommunicationProtocol}. + */ + @Override + public VeluxScene[] getScenes() { + VeluxScene[] scenes = new VeluxScene[response.data.length]; + for (int sceneIdx = 0; sceneIdx < response.data.length; sceneIdx++) { + + VeluxProductState[] productStates = new VeluxProductState[response.data[sceneIdx].products.length]; + for (int productIdx = 0; productIdx < response.data[sceneIdx].products.length; productIdx++) { + productStates[productIdx] = new VeluxProductState( + new VeluxProductReference( + new VeluxProductName(response.data[sceneIdx].products[productIdx].name), + response.data[sceneIdx].products[productIdx].typeId), + response.data[sceneIdx].products[productIdx].actuator, + response.data[sceneIdx].products[productIdx].status); + } + scenes[sceneIdx] = new VeluxScene(response.data[sceneIdx].name, response.data[sceneIdx].id, + response.data[sceneIdx].silent, productStates); + } + return scenes; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetWLANConfig.java new file mode 100644 index 0000000000000..46e40500fae7f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCgetWLANConfig.java @@ -0,0 +1,184 @@ +/** + * 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.velux.internal.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.GetWLANConfig; +import org.openhab.binding.velux.internal.things.VeluxGwWLAN; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Retrieval of WLAN configuration. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCgetWLANConfig extends GetWLANConfig implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/settings"; + private static final String DESCRIPTION = "get WLAN configuration"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + + /** + * Bridge I/O Request message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"wifi","params":{}}
+     * 
+ */ + @NonNullByDefault + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "wifi"; + this.params = new HashMap(); + } + } + + /** + * Bridge Communication Structure containing the version of the firmware. + *

+ * Used within structure {@link JCgetWLANConfig} to describe the network connectivity of the Bridge. + * + *

+     * {"password":"Esf56mxqFY","name":"VELUX_KLF_847C"}
+     * 
+ */ + @NonNullByDefault + private static class BCWLANConfig { + + private String password = VeluxBindingConstants.UNKNOWN; + private String name = VeluxBindingConstants.UNKNOWN; + + @Override + public String toString() { + return String.format("SSID=%s,password=********", this.name); + } + } + + /** + * Bridge I/O Response message used by {@link JsonBridgeCommunicationProtocol} for deserialization with including + * component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"IDLE",
+     *  "data":{"password":"Esf56mxqFY","name":"VELUX_KLF_847C"},
+     *  "errors":[]
+     * }
+     * 
+ */ + @NonNullByDefault + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + private BCWLANConfig data = new BCWLANConfig(); + private String[] errors = {}; + + public boolean getResult() { + return result; + } + + @Override + public String toString() { + return data.toString(); + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object response) { + this.response = (Response) response; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /** + * Methods in addition to interface {@link JsonBridgeCommunicationProtocol}. + */ + @Override + public VeluxGwWLAN getWLANConfig() { + VeluxGwWLAN gwWLAN = new VeluxGwWLAN(response.data.name, response.data.password); + return gwWLAN; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogin.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogin.java new file mode 100644 index 0000000000000..535b28db70b8b --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogin.java @@ -0,0 +1,171 @@ +/** + * 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.velux.internal.bridge.json; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.Login; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Communication to authenticate itself, resulting in a return of current bridge state. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JClogin extends Login implements JsonBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(JClogin.class); + + private static final String URL = "/api/v1/auth"; + private static final String DESCRIPTION = "authenticate / login"; + + private static Request request = new Request(); + private static Response response = new Response(); + + /* + * Message Objects + */ + @NonNullByDefault + private static class ParamsLogin { + @SuppressWarnings("unused") + private String password = VeluxBindingConstants.UNKNOWN; + } + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"login","params":{"password":"PASSWORD"}}
+     * 
+ */ + @NonNullByDefault + private static class Request { + + @SuppressWarnings("unused") + private final String action = "login"; + private ParamsLogin params; + + public Request() { + this.params = new ParamsLogin(); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * '{"token": "PHPnfLda71xfGlxoYEOTGQ==", "result": true, "deviceStatus": "IDLE", "data": {}, "errors": [] }'
+     * 
+ */ + @NonNullByDefault + private static class Response { + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data; + private String[] errors = {}; + + public String getToken() { + return token; + } + + public boolean getResult() { + return result; + } + } + + /* + * Constructor Method + */ + + public JClogin() { + logger.trace("JClogin(constructor) called."); + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /* + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public void setPassword(String thisPassword) { + logger.trace("setPassword() called."); + request.params.password = thisPassword; + } + + @Override + public String getAuthToken() { + return response.getToken(); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogout.java new file mode 100644 index 0000000000000..a0eef33608841 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JClogout.java @@ -0,0 +1,137 @@ +/** + * 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.velux.internal.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.Logout; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Communication to deauthenticate itself, resulting in a return of current bridge state. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JClogout extends Logout implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/auth"; + private static final String DESCRIPTION = "deauthenticate / logout"; + + private Request request = new Request(); + private Response response = new Response(); + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} for serializing. + *

+ * Resulting JSON: + * + *

+     * {"action":"logout","params":{}}
+     * 
+ */ + @NonNullByDefault + private static class Request { + + @SuppressWarnings("unused") + private String action; + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "logout"; + this.params = new HashMap(); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * '{"token": "PHPnfLda71xfGlxoYEOTGQ==", "result": true, "deviceStatus": "IDLE", "data": {}, "errors": [] }'
+     * 
+ */ + @NonNullByDefault + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data = null; + private String[] errors = {}; + + public boolean getResult() { + return result; + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductDiscovery.java new file mode 100644 index 0000000000000..e12c380d985c0 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductDiscovery.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.velux.internal.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.RunProductDiscovery; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic:Action to start discovery of products, i.e. Velux devices. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link JsonBridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCrunProductDiscovery extends RunProductDiscovery implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/products"; + private static final String DESCRIPTION = "discover products"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} + * for serializing. + * + * Resulting JSON: + * + *

+     * {"action":"discover","params":{}}
+     * 
+ * + * NOTE: the gateway software is extremely sensitive to this exact JSON structure. + * Any modifications (like omitting empty params) will lead to an gateway error. + */ + @NonNullByDefault + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "discover"; + this.params = new HashMap(); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods. + * + * Expected JSON (sample): + * + *
+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"discovering",
+     *  "data":{},
+     *  "errors":[]
+     * }
+     * 
+ */ + @NonNullByDefault + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data = null; + private String[] errors = {}; + + public boolean getResult() { + return result; + } + + public String getDeviceStatus() { + return deviceStatus; + } + + public String[] getErrors() { + return errors; + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.getDeviceStatus(); + } + + @Override + public String[] getErrors() { + return response.getErrors(); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductIdentification.java new file mode 100644 index 0000000000000..a70b9227d699b --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductIdentification.java @@ -0,0 +1,176 @@ +/** + * 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.velux.internal.bridge.json; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.RunProductIdentification; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Trigger action to identify a product, resulting in a return of current bridge state. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link JsonBridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault + +class JCrunProductIdentification extends RunProductIdentification implements JsonBridgeCommunicationProtocol { + private static final int DEFAULT_IDENTIFY_TIME = 50; + + private static final String URL = "/api/v1/products"; + private static final String DESCRIPTION = "identify one product"; + + private Request request = new Request(); + private Response response = new Response(); + + private static int productId; + private static int identifyTime = DEFAULT_IDENTIFY_TIME; + + /* + * Message Objects + */ + @NonNullByDefault + private static class ParamsIdentifyProduct { + @SuppressWarnings("unused") + private int id; + @SuppressWarnings("unused") + private int time; + + private ParamsIdentifyProduct(int id, int time) { + this.id = id; + this.time = time; + } + } + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} for serializing. + *

+ * Resulting JSON (sample): + * + *

+     * {"action":"identify","params":{"id":23,"time":254}}
+     * 
+ */ + @NonNullByDefault + private static class Request { + @SuppressWarnings("unused") + private String action; + @SuppressWarnings("unused") + private ParamsIdentifyProduct params; + + public Request() { + this.action = "identify"; + this.params = new ParamsIdentifyProduct(JCrunProductIdentification.productId, + JCrunProductIdentification.identifyTime); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     * "token": "NkR/AA5xXj7iL6NiIW8keA==",
+     * "result": false,
+     * "deviceStatus": "IDLE",
+     * "data": {},
+     * "errors": [ 104 ]
+     * }
+     * 
+ */ + @NonNullByDefault + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data; + private String[] errors = {}; + + public boolean getResult() { + return result; + } + + public String getDeviceStatus() { + return deviceStatus; + } + + public String[] getErrors() { + return errors; + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object response) { + this.response = (Response) response; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.getDeviceStatus(); + } + + @Override + public String[] getErrors() { + return response.getErrors(); + } + + /* + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + @Override + public JCrunProductIdentification setProductId(int id) { + JCrunProductIdentification.productId = id; + return this; + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductSearch.java new file mode 100644 index 0000000000000..a42e29d62772a --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunProductSearch.java @@ -0,0 +1,149 @@ +/** + * 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.velux.internal.bridge.json; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.RunProductSearch; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: query for lost nodes, resulting in a return of current bridge state. + *

+ * Implementing the abstract class {@link RunProductSearch}. + *

+ * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCrunProductSearch extends RunProductSearch implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/device"; + private static final String DESCRIPTION = "check lost nodes"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + + /** + * Bridge I/O Request message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge JsonVeluxBridge} + * for serializing. + * + * Resulting JSON: + * + *

+     * {"action":"checkLostNodes","params":{}}
+     * 
+ * + * NOTE: the gateway software is extremely sensitive to this exact JSON structure. + * Any modifications (like omitting empty params) will lead to an gateway error. + */ + @NonNullByDefault + private static class Request { + + @SuppressWarnings("unused") + private String action; + + @SuppressWarnings("unused") + private Map params; + + public Request() { + this.action = "checkLostNodes"; + this.params = new HashMap(); + } + } + + /** + * Bridge I/O Response message used by {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge} for + * deserializing with including component access methods + * + * Expected JSON (sample): + * + *
+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"IDLE",
+     *  "data":[],
+     *  "errors":[]
+     * }
+     * 
+ */ + @NonNullByDefault + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private String[] data = {}; + private String[] errors = {}; + + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object thisResponse) { + response = (Response) thisResponse; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.result; + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunScene.java new file mode 100644 index 0000000000000..a7cab20b2547b --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCrunScene.java @@ -0,0 +1,157 @@ +/** + * 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.velux.internal.bridge.json; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.RunScene; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: Trigger activation of a specific scene, resulting in a return of current bridge state. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCrunScene extends RunScene implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/scenes"; + private static final String DESCRIPTION = "run Scene"; + + private Request request = new Request(); + private Response response = new Response(); + + /* + * Message Objects + */ + @NonNullByDefault + private static class ParamsRunScene { + @SuppressWarnings("unused") + private int id; + } + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON (sample): + * + *

+     * {"action":"run","params":{"id":9}}
+     * 
+ */ + @NonNullByDefault + private static class Request { + @SuppressWarnings("unused") + private String action; + private ParamsRunScene params; + + public Request() { + this.action = "run"; + this.params = new ParamsRunScene(); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"IDLE",
+     *  "data":{},
+     *  "errors":[]
+     * }
+     * 
+ */ + @NonNullByDefault + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data; + private String[] errors = {}; + + public boolean getResult() { + return result; + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object response) { + this.response = (Response) response; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /* + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + @Override + public JCrunScene setSceneId(int id) { + request.params.id = id; + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCsetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCsetSceneVelocity.java new file mode 100644 index 0000000000000..e463aca5894ee --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JCsetSceneVelocity.java @@ -0,0 +1,169 @@ +/** + * 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.velux.internal.bridge.json; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.SetSceneVelocity; + +/** + * Specific bridge communication message supported by the Velux bridge. + *

+ * Message semantic: setting of scene silent mode, resulting in a return of current bridge state. + *

+ * + * It defines information how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JCsetSceneVelocity extends SetSceneVelocity implements JsonBridgeCommunicationProtocol { + + private static final String URL = "/api/v1/scenes"; + private static final String DESCRIPTION = "modify silent mode"; + + private Request request = new Request(); + private Response response = new Response(); + + private static int productId; + private static boolean silentMode; + + /* + * Message Objects + */ + @NonNullByDefault + private static class ParamsRunScene { + @SuppressWarnings("unused") + private int id; + @SuppressWarnings("unused") + private boolean silent; + + private ParamsRunScene(int id, boolean silent) { + this.id = id; + this.silent = silent; + } + } + + /** + * Bridge I/O Request message used by {@link JsonVeluxBridge} + * for serializing. + *

+ * Resulting JSON (sample): + * + *

+     * {"action":"setSilentMode","params":{"id":9,"silent":false}}}
+     * 
+ */ + @NonNullByDefault + private static class Request { + @SuppressWarnings("unused") + private String action; + @SuppressWarnings("unused") + private ParamsRunScene params; + + public Request() { + this.action = "setSilentMode"; + this.params = new ParamsRunScene(JCsetSceneVelocity.productId, JCsetSceneVelocity.silentMode); + } + } + + /** + * Bridge I/O Response message used by {@link JsonVeluxBridge} for deserializing with including component access + * methods + *

+ * Expected JSON (sample): + * + *

+     * {
+     *  "token":"RHIKGlJyZhidI/JSK0a2RQ==",
+     *  "result":true,
+     *  "deviceStatus":"IDLE",
+     *  "data":{},
+     *  "errors":[]
+     * }
+     * 
+ */ + @NonNullByDefault + private static class Response { + @SuppressWarnings("unused") + private String token = VeluxBindingConstants.UNKNOWN; + private boolean result; + private String deviceStatus = VeluxBindingConstants.UNKNOWN; + @SuppressWarnings("unused") + private @Nullable Object data; + private String[] errors = {}; + + public boolean getResult() { + return result; + } + } + + /* + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public String getURL() { + return URL; + } + + @Override + public Object getObjectOfRequest() { + return request; + } + + @Override + public Class getClassOfResponse() { + return Response.class; + } + + @Override + public void setResponse(Object response) { + this.response = (Response) response; + } + + @Override + public boolean isCommunicationSuccessful() { + return response.getResult(); + } + + @Override + public String getDeviceStatus() { + return response.deviceStatus; + } + + @Override + public String[] getErrors() { + return response.errors; + } + + /* + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + @Override + public JCsetSceneVelocity setMode(int id, boolean silent) { + JCsetSceneVelocity.productId = id; + JCsetSceneVelocity.silentMode = silent; + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeAPI.java new file mode 100644 index 0000000000000..fdf452c511566 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeAPI.java @@ -0,0 +1,209 @@ +/** + * 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.velux.internal.bridge.json; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.internal.bridge.common.GetFirmware; +import org.openhab.binding.velux.internal.bridge.common.GetHouseStatus; +import org.openhab.binding.velux.internal.bridge.common.GetLANConfig; +import org.openhab.binding.velux.internal.bridge.common.GetProduct; +import org.openhab.binding.velux.internal.bridge.common.GetProductLimitation; +import org.openhab.binding.velux.internal.bridge.common.GetProducts; +import org.openhab.binding.velux.internal.bridge.common.GetScenes; +import org.openhab.binding.velux.internal.bridge.common.GetWLANConfig; +import org.openhab.binding.velux.internal.bridge.common.Login; +import org.openhab.binding.velux.internal.bridge.common.Logout; +import org.openhab.binding.velux.internal.bridge.common.RunProductCommand; +import org.openhab.binding.velux.internal.bridge.common.RunProductDiscovery; +import org.openhab.binding.velux.internal.bridge.common.RunProductIdentification; +import org.openhab.binding.velux.internal.bridge.common.RunProductSearch; +import org.openhab.binding.velux.internal.bridge.common.RunScene; +import org.openhab.binding.velux.internal.bridge.common.SetHouseStatusMonitor; +import org.openhab.binding.velux.internal.bridge.common.SetProductLimitation; +import org.openhab.binding.velux.internal.bridge.common.SetSceneVelocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * JSON-based 3rd Level I/O interface towards the Velux bridge. + *

+ * It provides the one-and-only protocol specific 1st-level communication class. + * Additionally it provides all methods for different gateway interactions. + *

+ * The following class access methods exist: + *

    + *
  • {@link JsonBridgeAPI#getDeviceStatus} for retrieving the bridge state (i.e. IDLE, BUSY, a.s.o),
  • + *
  • {@link JsonBridgeAPI#getFirmware} for retrieving the firmware version of the bridge,
  • + *
  • {@link JsonBridgeAPI#getHouseStatus} for retrieving the information about device state changes recognized by the + * bridge,
  • + *
  • {@link JsonBridgeAPI#getLANConfig} for retrieving the complete LAN information of the bridge,
  • + *
  • {@link JsonBridgeAPI#getProduct} for retrieving the any information about a device behind the bridge,
  • + *
  • {@link JsonBridgeAPI#getProductLimitation} for retrieving the limitation information about a device behind the + * bridge,
  • + *
  • {@link JsonBridgeAPI#getProducts} for retrieving the any information for all devices behind the bridge,
  • + *
  • {@link JsonBridgeAPI#getScenes} for retrieving the any information for all scenes defined on the bridge,
  • + *
  • {@link JsonBridgeAPI#getWLANConfig} for retrieving the complete WLAN information of the bridge,
  • + *
  • {@link JsonBridgeAPI#login} for establishing a trusted connectivity by authentication,
  • + *
  • {@link JsonBridgeAPI#logout} for tearing down the trusted connectivity by deauthentication,
  • + *
  • {@link JsonBridgeAPI#runProductCommand} for manipulation of a device behind the bridge (i.e. instructing to + * modify a position),
  • + *
  • {@link JsonBridgeAPI#runProductDiscovery} for activation of learning mode of the bridge to discovery new + * products,
  • + *
  • {@link JsonBridgeAPI#runProductIdentification} for human-oriented identification a device behind the bridge (i.e. + * by winking or switching on-and-off),
  • + *
  • {@link JsonBridgeAPI#runProductSearch} for searching for lost products on the bridge,
  • + *
  • {@link JsonBridgeAPI#runScene} for manipulation of a set of devices behind the bridge which are tied together as + * scene,
  • + *
  • {@link JsonBridgeAPI#setHouseStatusMonitor} for activation or deactivation of the house monitoring mode to be + * informed about device state changes recognized by the bridge,
  • + *
  • {@link JsonBridgeAPI#setSceneVelocity} for changes the velocity of a scene defined on the bridge (i.e. silent or + * fast mode).
  • + *
+ *

+ * As most derived class of the several inheritance levels it defines an + * interfacing method which returns the JSON-protocol-specific communication for gateway interaction. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class JsonBridgeAPI implements BridgeAPI { + private final Logger logger = LoggerFactory.getLogger(JsonBridgeAPI.class); + + private static final GetDeviceStatus GETDEVICESTATUS = new JCgetDeviceStatus(); + private static final GetFirmware GETFIRMWARE = new JCgetFirmware(); + private static final GetLANConfig GETLANCONFIG = new JCgetLANConfig(); + private static final GetProducts GETPRODUCTS = new JCgetProducts(); + private static final GetScenes GETSCENES = new JCgetScenes(); + private static final GetWLANConfig GETWLANCONFIG = new JCgetWLANConfig(); + private static final Login LOGIN = new JClogin(); + private static final Logout LOGOUT = new JClogout(); + private static final RunProductDiscovery RUNPRODUCTDISCOVERY = new JCrunProductDiscovery(); + private static final RunProductIdentification RUNPRODUCTIDENTIFICATION = new JCrunProductIdentification(); + private static final RunProductSearch RUNPRODUCTSEARCH = new JCrunProductSearch(); + private static final RunScene RUNSCENE = new JCrunScene(); + private static final SetSceneVelocity SETSCENEVELOCITY = new JCsetSceneVelocity(); + + /** + * Constructor. + *

+ * Inherits the initialization of the binding-wide instance for dealing for common information and + * initializes the handler {@link org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge#bridgeAPI + * JsonVeluxBridge.bridgeAPI} + * to pass the interface methods. + * + * @param bridgeInstance refers to the binding-wide instance for dealing for common informations. + */ + JsonBridgeAPI(VeluxBridgeInstance bridgeInstance) { + logger.trace("JsonBridgeAPI(constructor) called."); + } + + @Override + public GetDeviceStatus getDeviceStatus() { + return GETDEVICESTATUS; + } + + @Override + public GetFirmware getFirmware() { + return GETFIRMWARE; + } + + @Override + public @Nullable GetHouseStatus getHouseStatus() { + return null; + } + + @Override + public GetLANConfig getLANConfig() { + return GETLANCONFIG; + } + + @Override + public @Nullable GetProduct getProduct() { + return null; + } + + @Override + public @Nullable GetProductLimitation getProductLimitation() { + return null; + } + + @Override + public @Nullable SetProductLimitation setProductLimitation() { + return null; + } + + @Override + public GetProducts getProducts() { + return GETPRODUCTS; + } + + @Override + public GetScenes getScenes() { + return GETSCENES; + } + + @Override + public GetWLANConfig getWLANConfig() { + return GETWLANCONFIG; + } + + @Override + public Login login() { + return LOGIN; + } + + @Override + public Logout logout() { + return LOGOUT; + } + + @Override + public @Nullable RunProductCommand runProductCommand() { + return null; + } + + @Override + public RunProductDiscovery runProductDiscovery() { + return RUNPRODUCTDISCOVERY; + } + + @Override + public RunProductIdentification runProductIdentification() { + return RUNPRODUCTIDENTIFICATION; + } + + @Override + public RunProductSearch runProductSearch() { + return RUNPRODUCTSEARCH; + } + + @Override + public RunScene runScene() { + return RUNSCENE; + } + + @Override + public @Nullable SetHouseStatusMonitor setHouseStatusMonitor() { + return null; + } + + @Override + public SetSceneVelocity setSceneVelocity() { + return SETSCENEVELOCITY; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeCommunicationProtocol.java new file mode 100644 index 0000000000000..59f77ab20202b --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonBridgeCommunicationProtocol.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.velux.internal.bridge.json; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; + +/** + * Common JSON-based bridge communication message scheme supported by the Velux bridge. + *

+ * This bridge communication is an extension of the common + * {@link org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol BridgeCommunicationProtocol}. + *

+ * Message semantic will be defined by the implementation of the separate message classes, + * which are defined within {@link org.openhab.binding.velux.internal.bridge.json.JsonBridgeAPI JsonBridgeAPI}. + *

+ * The implementations will define the information which to send query and receive answer + * through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider}. + *

+ * (Methods in this interface for the appropriate interaction: + *

    + *
  • {@link #getURL} to return the URL suffix for accessing the specific service access point.
  • + *
  • {@link #getObjectOfRequest} to return the request object for further JSON serialization.
  • + *
  • {@link #getClassOfResponse} to retrieve the class of the object of response message for further JSON + * deserialization.
  • + *
  • {@link #setResponse} for storing the response according to the desired class after JSON deserialization.
  • + *
  • {@link #getDeviceStatus} to retrieve the current device status.
  • + *
  • {@link #getErrors} to retrieve the current error status.
  • + *
+ * + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +interface JsonBridgeCommunicationProtocol extends BridgeCommunicationProtocol { + + /** + * Returning the URL suffix for accessing the specific service access point. + * + * @return sapURL + * as String which is to be combined with the bridge address. + */ + String getURL(); + + /** + * Returning the request object for further JSON serialization. + * + * @return ObjectOfRequest + * is an Object. + */ + Object getObjectOfRequest(); + + /** + * Returning the class of the object of response message for further JSON deserialization. + * + * @return ClassOfResponseObject + * is the appropriate class Object. + */ + Class getClassOfResponse(); + + /** + * Storing the response according to the desired class after JSON deserialization. + * + * @param response is the appropriate object of previously given class Object. + */ + void setResponse(Object response); + + /** + * Returning the communication status included within the response message. + * + * @return deviceStatus as String describing the current status of the bridge. + */ + String getDeviceStatus(); + + /** + * Returning the communication status included within the response message. + * + * @return errors as String[] describing the status of the operation according to the request in depth. + */ + String[] getErrors(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonVeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonVeluxBridge.java new file mode 100644 index 0000000000000..146f2d8f8413d --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/JsonVeluxBridge.java @@ -0,0 +1,322 @@ +/** + * 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.velux.internal.bridge.json; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Properties; +import java.util.TreeSet; + +import org.apache.commons.io.IOUtils; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.io.net.http.HttpUtil; +import org.openhab.binding.velux.internal.bridge.VeluxBridge; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +/** + * JSON-based 2nd Level I/O interface towards the Velux bridge. + *

+ * It provides methods for pre- and postcommunication + * as well as a common method for the real communication. + *

+ * The following class access methods exist: + *

    + *
  • {@link VeluxBridge#bridgeLogin} for pre-communication,
  • + *
  • {@link VeluxBridge#bridgeLogout} for post-communication,
  • + *
  • {@link VeluxBridge#bridgeCommunicate} as method for the common communication.
  • + *
+ *

+ * As root of several inheritance levels it predefines an + * interfacing method {@link VeluxBridge#bridgeAPI} which + * has to be implemented by any kind of protocol-specific + * communication returning the appropriate base (1st) level + * communication method as well as any other gateway + * interaction. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class JsonVeluxBridge extends VeluxBridge { + private final Logger logger = LoggerFactory.getLogger(JsonVeluxBridge.class); + + /** + * Timestamp of last communication in milliseconds. + * + */ + private long lastCommunicationInMSecs = 0; + + /** + * Timestamp of last successful communication in milliseconds. + * + */ + private long lastSuccessfulCommunicationInMSecs = 0; + + /** + * Handler passing the interface methods to other classes. + * Can be accessed via method {@link org.openhab.binding.velux.internal.bridge.common.BridgeAPI BridgeAPI}. + * + */ + private BridgeAPI bridgeAPI; + + /** + * Constructor. + *

+ * Inherits the initialization of the binding-wide instance for dealing for common informations and + * initializes the Velux bridge connectivity settings. + * + * @param bridgeInstance refers to the binding-wide instance for dealing for common informations. + */ + public JsonVeluxBridge(VeluxBridgeInstance bridgeInstance) { + super(bridgeInstance); + logger.trace("JsonVeluxBridge(constructor) called."); + bridgeAPI = new JsonBridgeAPI(bridgeInstance); + supportedProtocols = new TreeSet(); + supportedProtocols.add("http"); + supportedProtocols.add("https"); + logger.trace("JsonVeluxBridge(constructor) done."); + } + + /** + * Provides information about the base-level communication method and + * any kind of available gateway interactions. + *

+ * Note: the implementation within this class {@link JsonVeluxBridge} as inherited from {@link VeluxBridge} + * will return the protocol-specific class implementations. + *

+ * The information will be initialized by the corresponding API class {@link JsonBridgeAPI}. + * + * @return bridgeAPI of type {@link BridgeAPI} contains all possible methods. + */ + @Override + public BridgeAPI bridgeAPI() { + logger.trace("bridgeAPI() called."); + return bridgeAPI; + } + + /** + * Method as implementation of abstract superclass method. + *

+ * Initializes a client/server communication towards Velux veluxBridge + * based on the Basic I/O interface {@link #io} and parameters + * passed as arguments (see below). + * + * @param communication Structure of interface type {@link JsonBridgeCommunicationProtocol} describing the intended + * communication, that is request and response interactions as well as appropriate URL definition. + * @param useAuthentication boolean flag to decide whether to use authenticated communication. + * @return success of type boolean which signals the success of the communication. + * + */ + @Override + protected boolean bridgeDirectCommunicate(BridgeCommunicationProtocol communication, boolean useAuthentication) { + logger.trace("bridgeDirectCommunicate(BCP: {},{}authenticated) called.", communication.name(), + useAuthentication ? "" : "un"); + return bridgeDirectCommunicate((JsonBridgeCommunicationProtocol) communication, useAuthentication); + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last (potentially faulty) communication. + * + * @return timestamp in milliseconds. + */ + @Override + public long lastCommunication() { + return lastCommunicationInMSecs; + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last successful communication. + * + * @return timestamp in milliseconds. + */ + @Override + public long lastSuccessfulCommunication() { + return lastSuccessfulCommunicationInMSecs; + } + + /** + * Initializes a client/server communication towards Velux veluxBridge + * based on the Basic I/O interface {@link VeluxBridge} and parameters + * passed as arguments (see below). + * + * @param communication Structure of interface type {@link JsonBridgeCommunicationProtocol} describing the + * intended + * communication, + * that is request and response interactions as well as appropriate URL definition. + * @param useAuthentication boolean flag to decide whether to use authenticated communication. + * @return response of type boolean will indicate the success of the communication. + */ + private synchronized boolean bridgeDirectCommunicate(JsonBridgeCommunicationProtocol communication, + boolean useAuthentication) { + logger.trace("bridgeDirectCommunicate({},{}authenticated) called.", communication.name(), + useAuthentication ? "" : "un"); + + String sapURL = this.bridgeInstance.veluxBridgeConfiguration().protocol.concat("://") + .concat(this.bridgeInstance.veluxBridgeConfiguration().ipAddress).concat(":") + .concat(Integer.toString(this.bridgeInstance.veluxBridgeConfiguration().tcpPort)) + .concat(communication.getURL()); + logger.trace("bridgeCommunicate(): using SAP {}.", sapURL); + Object getRequest = communication.getObjectOfRequest(); + Class classOfResponse = communication.getClassOfResponse(); + Object response; + + try { + if (useAuthentication) { + response = ioAuthenticated(sapURL, authenticationToken, getRequest, classOfResponse); + } else { + response = ioUnauthenticated(sapURL, getRequest, classOfResponse); + } + communication.setResponse(response); + logger.trace("bridgeCommunicate(): communication result is {}, returning details.", + communication.isCommunicationSuccessful()); + return true; + } catch (IOException ioe) { + logger.warn("bridgeCommunicate(): Exception occurred on accessing {}: {}.", sapURL, ioe.getMessage()); + return false; + } catch (JsonSyntaxException jse) { + logger.warn("bridgeCommunicate(): Exception occurred on (de-)serialization during accessing {}: {}.", + sapURL, jse.getMessage()); + return false; + } + } + + /** + * Base level communication with the Velux bridge. + * + * @param This describes the request type parameter. + * @param This describes the response type parameter. + * @param url as String describing the Service Access Point location i.e. http://localhost/api . + * @param authentication as String providing the Authentication token to be passed with the request header. + * @param request as Object representing the structure of the message request body to be converted into + * JSON. + * @param classOfResponse as Class representing the expected structure of the message response body to be converted + * from JSON. + * @return response of type Object containing all resulting informations, i.e. device status, errors a.s.o. + * Will + * return + * null in case of communication or decoding error. + * @throws java.io.IOException in case of continuous communication I/O failures. + * @throws JsonSyntaxException in case of unusual communication failures. + */ + private T io(String url, String authentication, U request, Class classOfResponse) + throws JsonSyntaxException, IOException { + /** Local handles */ + int retryCount = 0; + + lastCommunicationInMSecs = System.currentTimeMillis(); + do { + try { + Gson gson = new Gson(); + String jsonRequest = gson.toJson(request); + logger.trace("io() to {} using request {}.", url, jsonRequest); + + Properties headerItems = new Properties(); + if (authentication.length() > 0) { + headerItems.setProperty("Authorization", String.format("Bearer %s", authentication)); + } + InputStream content = IOUtils.toInputStream(jsonRequest, StandardCharsets.UTF_8.name()); + + String jsonResponse = HttpUtil.executeUrl("PUT", url, headerItems, content, "application/json", + this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + if (jsonResponse == null) { + throw new IOException("transport error"); + } + logger.trace("io(): wait time {} msecs.", this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + // Give the bridge some time to breathe + try { + Thread.sleep(this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + } catch (InterruptedException ie) { + logger.trace("io() wait interrupted."); + } + logger.trace("io() got response {}.", jsonResponse.replaceAll("\\p{C}", ".")); + jsonResponse = jsonResponse.replaceAll("^.+,\n", ""); + logger.trace("io() cleaned response {}.", jsonResponse); + T response = gson.fromJson(jsonResponse, classOfResponse); + lastCommunicationInMSecs = lastSuccessfulCommunicationInMSecs = System.currentTimeMillis(); + return response; + + } catch (IOException ioe) { + logger.trace("io(): Exception occurred during I/O: {}.", ioe.getMessage()); + // Error Retries with Exponential Backoff + long waitTime = ((long) Math.pow(2, retryCount) + * this.bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + logger.trace("io(): wait time {} msecs.", waitTime); + try { + Thread.sleep(waitTime); + } catch (InterruptedException ie) { + logger.trace("io() wait interrupted."); + } + } catch (JsonSyntaxException jse) { + logger.info("io(): Exception occurred on deserialization: {}, aborting.", jse.getMessage()); + throw jse; + } + + } while (retryCount++ < this.bridgeInstance.veluxBridgeConfiguration().retries); + throw new IOException(String.format("io(): socket I/O failed (%d times).", + this.bridgeInstance.veluxBridgeConfiguration().retries)); + } + + /** + * Initializes an authenticated communication with the {@link JsonVeluxBridge Velux bridge}. + * + * @param This describes the request type parameter. + * @param This describes the response type parameter. + * @param url as String describing the Service Access Point location i.e. http://localhost/api . + * @param authentication as String providing the Authentication token to be passed with the request header. + * @param request as Object representing the structure of the message request body to be converted into + * JSON. + * @param classOfResponse as Class representing the expected structure of the message response body to be converted + * from JSON. + * @return response of type T containing all resulting informations, i.e. device status, errors a.s.o. Will + * return + * null in case of communication or decoding error. + * @throws java.io.IOException in case of continuous communication I/O failures. + * @throws JsonSyntaxException in case of unusual communication failures. + */ + private T ioAuthenticated(String url, String authentication, U request, Class classOfResponse) + throws JsonSyntaxException, IOException { + return io(url, authentication, request, classOfResponse); + } + + /** + * Initializes an unauthenticated communication with the {@link JsonVeluxBridge Velux bridge}. + * + * @param This describes the request type parameter. + * @param This describes the response type parameter. + * @param url as String describing the Service Access Point location i.e. http://localhost/api . + * @param request as Object representing the structure of the message request body to be converted into + * JSON. + * @param classOfResponse as Class representing the expected structure of the message response body to be converted + * from JSON. + * @return response of type Object containing all resulting informations, i.e. device status, errors a.s.o. + * Will + * return + * null in case of communication or decoding error. + * @throws java.io.IOException in case of continuous communication I/O failures. + * @throws JsonSyntaxException in case of unusual communication failures. + */ + private T ioUnauthenticated(String url, U request, Class classOfResponse) + throws JsonSyntaxException, IOException { + return io(url, "", request, classOfResponse); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/package-info.java new file mode 100644 index 0000000000000..1945de82632fb --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/json/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * JSON-protocol specific implementations of the interactions to IO-homecontrolled devices. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.bridge.json; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/package-info.java new file mode 100644 index 0000000000000..59eebdd716f15 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/package-info.java @@ -0,0 +1,19 @@ +/** + * 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 + */ +/** + * Classes for Generic protocol-independent interactions to IO-homecontrolled devices via the Velux gateway. + * This layer is responsible for the transformation of openHAB "objects" into Velux "object" and vice versa. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.bridge; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetDeviceStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetDeviceStatus.java new file mode 100644 index 0000000000000..7ced9a1202c8c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetDeviceStatus.java @@ -0,0 +1,152 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxGwState; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Get Bridge Device Status + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getState} to retrieve the Velux gateway status.
  • + *
+ * + * @see GetDeviceStatus + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetDeviceStatus extends GetDeviceStatus implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetDeviceStatus.class); + + private static final String DESCRIPTION = "Get Bridge Device Status"; + private static final Command COMMAND = Command.GW_GET_STATE_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int cfmGatewayState; + private int cfmSubState; + @SuppressWarnings("unused") + private int cfmStateData; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + logger.trace("getRequestDataAsArrayOfBytes() returns data."); + requestData = new byte[0]; + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_STATE_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 6)) { + finished = true; + break; + } + cfmGatewayState = responseData.getOneByteValue(0); + cfmSubState = responseData.getOneByteValue(1); + cfmStateData = responseData.getFourByteValue(2); + finished = true; + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public VeluxGwState getState() { + VeluxGwState thisGwState = new VeluxGwState((byte) cfmGatewayState, (byte) cfmSubState); + logger.trace("getState() returns {} ({}).", thisGwState, thisGwState.toDescription()); + return thisGwState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetFirmware.java new file mode 100644 index 0000000000000..83ae94889c091 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetFirmware.java @@ -0,0 +1,165 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetFirmware; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxGwFirmware; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Get Firmware Version + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getFirmware} to retrieve the Velux firmware version.
  • + *
+ * + * @see GetFirmware + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetFirmware extends GetFirmware implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetFirmware.class); + + private static final String DESCRIPTION = "Retrieve firmware version"; + private static final Command COMMAND = Command.GW_GET_VERSION_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int cfmSoftwareVersionCommand = 0; + private int cfmSoftwareVersionWhole = 0; + private int cfmSoftwareVersionSub = 0; + private int cfmSoftwareVersionBranch = 0; + private int cfmSoftwareVersionBuild = 0; + private int cfmSoftwareVersionMicroBuild = 0; + private int cfmHardwareVersion = 0; + private int cfmProductGroup = 0; + private int cfmProductType = 0; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + requestData = new byte[1]; + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_VERSION_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 9)) { + finished = true; + break; + } + cfmSoftwareVersionCommand = responseData.getOneByteValue(0); + cfmSoftwareVersionWhole = responseData.getOneByteValue(1); + cfmSoftwareVersionSub = responseData.getOneByteValue(2); + cfmSoftwareVersionBranch = responseData.getOneByteValue(3); + cfmSoftwareVersionBuild = responseData.getOneByteValue(4); + cfmSoftwareVersionMicroBuild = responseData.getOneByteValue(5); + cfmHardwareVersion = responseData.getOneByteValue(6); + cfmProductGroup = responseData.getOneByteValue(7); + cfmProductType = responseData.getOneByteValue(8); + success = true; + finished = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public VeluxGwFirmware getFirmware() { + String result = String.format("Software version %d.%d.%d.%d.%d.%d, Hardware version %d.%d.%d", + cfmSoftwareVersionCommand, cfmSoftwareVersionWhole, cfmSoftwareVersionSub, cfmSoftwareVersionBranch, + cfmSoftwareVersionBuild, cfmSoftwareVersionMicroBuild, cfmHardwareVersion, cfmProductGroup, + cfmProductType); + logger.trace("getFirmware() returns {}.", result); + return new VeluxGwFirmware(result); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetHouseStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetHouseStatus.java new file mode 100644 index 0000000000000..e67458b1438b7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetHouseStatus.java @@ -0,0 +1,184 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.bridge.common.GetHouseStatus; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve House Status + *

+ * Common Message semantic: Communication from the bridge and storing returned information within the class itself. + *

+ * As 3rd level class it defines informations how to receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getNtfNodeID} to retrieve the node identifier which has been changed.
  • + *
  • {@link #getNtfState} to retrieve the state of the node which has been changed.
  • + *
  • {@link #getNtfCurrentPosition} to retrieve the actual position of this node.
  • + *
  • {@link #getNtfTarget} to retrieve the target position of this node.
  • + *
+ *

+ * NOTE: the class does NOT define a request as it only works as receiver. + * + * @see BridgeCommunicationProtocol + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetHouseStatus extends GetHouseStatus implements BridgeCommunicationProtocol, SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetHouseStatus.class); + + private static final String DESCRIPTION = "Retrieve House Status"; + private static final Command COMMAND = Command.GW_OPENHAB_RECEIVEONLY; + + /* + * =========================================================== + * Message Objects + */ + + @SuppressWarnings("unused") + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + private int ntfNodeID; + private int ntfState; + private int ntfCurrentPosition; + private int ntfTarget; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + return new byte[0]; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = true; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_NODE_STATE_POSITION_CHANGED_NTF: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 20)) { + break; + } + ntfNodeID = responseData.getOneByteValue(0); + ntfState = responseData.getOneByteValue(1); + ntfCurrentPosition = responseData.getTwoByteValue(2); + ntfTarget = responseData.getTwoByteValue(4); + @SuppressWarnings("unused") + int ntfFP1CurrentPosition = responseData.getTwoByteValue(6); + @SuppressWarnings("unused") + int ntfFP2CurrentPosition = responseData.getTwoByteValue(8); + @SuppressWarnings("unused") + int ntfFP3CurrentPosition = responseData.getTwoByteValue(10); + @SuppressWarnings("unused") + int ntfFP4CurrentPosition = responseData.getTwoByteValue(12); + int ntfRemainingTime = responseData.getTwoByteValue(14); + int ntfTimeStamp = responseData.getFourByteValue(16); + // Extracting information items + logger.trace("setResponse(): ntfNodeID={}.", ntfNodeID); + logger.trace("setResponse(): ntfState={}.", ntfState); + logger.trace("setResponse(): ntfCurrentPosition={}.", ntfCurrentPosition); + logger.trace("setResponse(): ntfTarget={}.", ntfTarget); + logger.trace("setResponse(): ntfRemainingTime={}.", ntfRemainingTime); + logger.trace("setResponse(): ntfTimeStamp={}.", ntfTimeStamp); + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return true; + } + + @Override + public boolean isCommunicationSuccessful() { + return true; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + */ + + /** + * @return ntfNodeID returns the Actuator Id as int. + */ + public int getNtfNodeID() { + return ntfNodeID; + } + + /** + * @return ntfState returns the state of the Actuator as int. + */ + public int getNtfState() { + return ntfState; + } + + /** + * @return ntfCurrentPosition returns the current position of the Actuator as int. + */ + public int getNtfCurrentPosition() { + return ntfCurrentPosition; + } + + /** + * @return ntfTarget returns the target position of the Actuator as int. + */ + public int getNtfTarget() { + return ntfTarget; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLANConfig.java new file mode 100644 index 0000000000000..38099fae545ea --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLANConfig.java @@ -0,0 +1,153 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetLANConfig; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxGwLAN; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve LAN configuration + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getLANConfig} to retrieve the current LAN configuration.
  • + *
+ * + * @see GetLANConfig + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetLANConfig extends GetLANConfig implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetLANConfig.class); + + private static final String DESCRIPTION = "Retrieve LAN configuration"; + private static final Command COMMAND = Command.GW_GET_NETWORK_SETUP_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int cfmIpAddress; + private int cfmMask; + private int cfmDefGW; + private boolean cfmDHCP; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + requestData = new byte[1]; + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_NETWORK_SETUP_CFM: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 13)) { + break; + } + cfmIpAddress = responseData.getFourByteValue(0); + cfmMask = responseData.getFourByteValue(4); + cfmDefGW = responseData.getFourByteValue(8); + cfmDHCP = responseData.getOneByteValue(12) == 0 ? false : true; + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public VeluxGwLAN getLANConfig() { + logger.trace("getLANConfig() called."); + VeluxGwLAN result = new VeluxGwLAN(Packet.intToIPAddressString(cfmIpAddress), + Packet.intToIPAddressString(cfmMask), Packet.intToIPAddressString(cfmDefGW), cfmDHCP); + logger.debug("getLANConfig() returns {}.", result); + return result; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java new file mode 100644 index 0000000000000..84b7966d9bbaf --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetLimitation.java @@ -0,0 +1,300 @@ +/** + * 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.velux.internal.bridge.slip; + +import java.util.Random; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetProductLimitation; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve Product Limitations + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setActuatorIdAndLimitationType(int,boolean)} to define the one specific product.
  • + *
+ * + * @see GetProductLimitation + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetLimitation extends GetProductLimitation implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetLimitation.class); + + private static final String DESCRIPTION = "Retrieve Actuator Limitation"; + private static final Command COMMAND = Command.GW_GET_LIMITATION_STATUS_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqSessionID = 0; + private int reqIndexArrayCount = 1; // One node will be addressed + private int reqIndexArray01 = 1; // This is the node + private int reqParameterID = 0; // MP = Main parameter + private int reqLimitationType = 0; // Resulting minimum limitation. 1= maximum. + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + private int limitationValue = 0; + + /* + * =========================================================== + * Constructor Method + */ + + public SCgetLimitation() { + logger.debug("SCgetLimitation(Constructor) called."); + Random rand = new Random(); + reqSessionID = rand.nextInt(0x0fff); + logger.debug("SCgetLimitation(): starting sessions with the random number {}.", reqSessionID); + } + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + Packet request = new Packet(new byte[25]); + reqSessionID = (reqSessionID + 1) & 0xffff; + request.setTwoByteValue(0, reqSessionID); + request.setOneByteValue(2, reqIndexArrayCount); + request.setOneByteValue(3, reqIndexArray01); + request.setOneByteValue(23, reqParameterID); + request.setOneByteValue(24, reqLimitationType); + logger.trace("getRequestDataAsArrayOfBytes(): ntfSessionID={}.", reqSessionID); + logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArrayCount={}.", reqIndexArrayCount); + logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArray01={}.", reqIndexArray01); + logger.trace("getRequestDataAsArrayOfBytes(): reqParameterID={}.", reqParameterID); + logger.trace("getRequestDataAsArrayOfBytes(): reqLimitationType={}.", reqLimitationType); + requestData = request.toByteArray(); + logger.trace("getRequestDataAsArrayOfBytes() data is {}.", new Packet(requestData).toString()); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_LIMITATION_STATUS_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 3)) { + finished = true; + break; + } + int cfmSessionID = responseData.getTwoByteValue(0); + int cfmStatus = responseData.getOneByteValue(2); + switch (cfmStatus) { + case 0: + logger.info("setResponse(): returned status: Error – Command rejected."); + finished = true; + break; + case 1: + logger.debug("setResponse(): returned status: OK - Command is accepted."); + if (!KLF200Response.check4matchingSessionID(logger, cfmSessionID, reqSessionID)) { + finished = true; + } + break; + default: + logger.warn("setResponse(): returned status={} (not defined).", cfmStatus); + finished = true; + break; + } + break; + + case GW_LIMITATION_STATUS_NTF: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 10)) { + break; + } + // Extracting information items + int ntfSessionID = responseData.getTwoByteValue(0); + int ntfNodeID = responseData.getOneByteValue(2); + int ntfParameterID = responseData.getOneByteValue(3); + int ntfMinValue = responseData.getTwoByteValue(4); + int ntfMaxValue = responseData.getTwoByteValue(6); + int ntfLimitationOriginator = responseData.getOneByteValue(8); + int ntfLimitationTime = responseData.getOneByteValue(9); + + if (!KLF200Response.check4matchingSessionID(logger, ntfSessionID, reqSessionID)) { + finished = true; + break; + } + + logger.trace("setResponse(): nodeId={}.", ntfNodeID); + logger.trace("setResponse(): ntfParameterID={}.", ntfParameterID); + logger.trace("setResponse(): ntfMinValue={}.", ntfMinValue); + logger.trace("setResponse(): ntfMaxValue={}.", ntfMaxValue); + logger.trace("setResponse(): ntfLimitationOriginator={}.", ntfLimitationOriginator); + logger.trace("setResponse(): ntfLimitationTime={}.", ntfLimitationTime); + + // Determine the returned value + limitationValue = (reqLimitationType == 0) ? ntfMinValue : ntfMaxValue; + logger.debug("setResponse(): {} limitation for node {} is {}.", + (reqLimitationType == 0) ? "minimum" : "maximum", reqIndexArray01, limitationValue); + + success = true; + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + finished = true; + } + break; + + case GW_COMMAND_RUN_STATUS_NTF: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 13)) { + finished = true; + break; + } + ntfSessionID = responseData.getTwoByteValue(0); + int ntfStatusiD = responseData.getOneByteValue(2); + int ntfIndex = responseData.getOneByteValue(3); + int ntfNodeParameter = responseData.getOneByteValue(4); + int ntfParameterValue = responseData.getTwoByteValue(5); + int ntfRunStatus = responseData.getOneByteValue(7); + int ntfStatusReply = responseData.getOneByteValue(8); + int ntfInformationCode = responseData.getFourByteValue(9); + // Extracting information items + logger.trace("setResponse(): ntfSessionID={} (requested {}).", ntfSessionID, reqSessionID); + logger.trace("setResponse(): ntfStatusiD={}.", ntfStatusiD); + logger.trace("setResponse(): ntfIndex={}.", ntfIndex); + logger.trace("setResponse(): ntfNodeParameter={}.", ntfNodeParameter); + logger.trace("setResponse(): ntfParameterValue={}.", ntfParameterValue); + logger.trace("setResponse(): ntfRunStatus={}.", ntfRunStatus); + logger.trace("setResponse(): ntfStatusReply={}.", ntfStatusReply); + logger.trace("setResponse(): ntfInformationCode={}.", ntfInformationCode); + + if (!KLF200Response.check4matchingSessionID(logger, ntfSessionID, reqSessionID)) { + finished = true; + } + switch (ntfRunStatus) { + case 0: + logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_COMPLETED."); + success = true; + break; + case 1: + logger.info("setResponse(): returned ntfRunStatus: EXECUTION_FAILED."); + finished = true; + break; + case 2: + logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_ACTIVE."); + break; + default: + logger.warn("setResponse(): returned ntfRunStatus={} (not defined).", ntfRunStatus); + finished = true; + break; + } + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + + case GW_SESSION_FINISHED_NTF: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 2)) { + break; + } + int finishedNtfSessionID = responseData.getTwoByteValue(0); + if (!KLF200Response.check4matchingSessionID(logger, finishedNtfSessionID, reqSessionID)) { + break; + } + logger.debug("setResponse(): finishedNtfSessionID={}.", finishedNtfSessionID); + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link GetProductLimitation} + */ + + @Override + public void setActuatorIdAndLimitationType(int nodeId, boolean limitationMinimum) { + logger.trace("setProductId({},{}) called.", nodeId, limitationMinimum); + this.reqIndexArray01 = nodeId; + this.reqLimitationType = limitationMinimum ? 0 : 1; + return; + } + + @Override + public int getLimitation() { + return limitationValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProduct.java new file mode 100644 index 0000000000000..b810f96346ece --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProduct.java @@ -0,0 +1,268 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetProduct; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; +import org.openhab.binding.velux.internal.things.VeluxProductName; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; +import org.openhab.binding.velux.internal.things.VeluxProductType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve Product + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setProductId(int)} to define the one specific product.
  • + *
  • {@link #getProduct} to retrieve one specific product.
  • + *
+ * + * @see GetProduct + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetProduct extends GetProduct implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetProduct.class); + + private static final String DESCRIPTION = "Retrieve Product"; + private static final Command COMMAND = Command.GW_GET_NODE_INFORMATION_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqNodeID; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + private VeluxProduct product = VeluxProduct.UNKNOWN; + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + logger.trace("getRequestDataAsArrayOfBytes() returns data for retrieving node with id {}.", reqNodeID); + Packet request = new Packet(new byte[1]); + request.setOneByteValue(0, reqNodeID); + requestData = request.toByteArray(); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_NODE_INFORMATION_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 2)) { + finished = true; + break; + } + int cfmStatus = responseData.getOneByteValue(0); + int cfmNodeID = responseData.getOneByteValue(1); + switch (cfmStatus) { + case 0: + logger.trace("setResponse(): returned status: OK - Request accepted."); + if (!KLF200Response.check4matchingNodeID(logger, reqNodeID, cfmNodeID)) { + finished = true; + } + break; + case 1: + finished = true; + logger.trace("setResponse(): returned status: Error – Request rejected."); + break; + case 2: + finished = true; + logger.trace("setResponse(): returned status: Error – Invalid node index."); + break; + default: + finished = true; + logger.warn("setResponse({}): returned status={} (Reserved/unknown).", + Command.get(responseCommand).toString(), cfmStatus); + break; + } + break; + + case GW_GET_NODE_INFORMATION_NTF: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 124)) { + break; + } + // Extracting information items + int ntfNodeID = responseData.getOneByteValue(0); + logger.trace("setResponse(): ntfNodeID={}.", ntfNodeID); + int ntfOrder = responseData.getTwoByteValue(1); + logger.trace("setResponse(): ntfOrder={}.", ntfOrder); + int ntfPlacement = responseData.getOneByteValue(3); + logger.trace("setResponse(): ntfPlacement={}.", ntfPlacement); + String ntfName = responseData.getString(4, 64); + logger.trace("setResponse(): ntfName={}.", ntfName); + int ntfVelocity = responseData.getOneByteValue(68); + logger.trace("setResponse(): ntfVelocity={}.", ntfVelocity); + int ntfNodeTypeSubType = responseData.getTwoByteValue(69); + logger.trace("setResponse(): ntfNodeTypeSubType={} ({}).", ntfNodeTypeSubType, + VeluxProductType.get(ntfNodeTypeSubType)); + logger.trace("setResponse(): derived product description={}.", + VeluxProductType.toString(ntfNodeTypeSubType)); + int ntfProductGroup = responseData.getTwoByteValue(71); + logger.trace("setResponse(): ntfProductGroup={}.", ntfProductGroup); + int ntfProductType = responseData.getOneByteValue(72); + logger.trace("setResponse(): ntfProductType={}.", ntfProductType); + int ntfNodeVariation = responseData.getOneByteValue(73); + logger.trace("setResponse(): ntfNodeVariation={}.", ntfNodeVariation); + int ntfPowerMode = responseData.getOneByteValue(74); + logger.trace("setResponse(): ntfPowerMode={}.", ntfPowerMode); + int ntfBuildNumber = responseData.getOneByteValue(75); + logger.trace("setResponse(): ntfBuildNumber={}.", ntfBuildNumber); + byte[] ntfSerialNumber = responseData.getByteArray(76, 8); + logger.trace("setResponse(): ntfSerialNumber={}.", ntfSerialNumber); + int ntfState = responseData.getOneByteValue(84); + logger.trace("setResponse(): ntfState={}.", ntfState); + int ntfCurrentPosition = responseData.getTwoByteValue(85); + logger.trace("setResponse(): ntfCurrentPosition={}.", ntfCurrentPosition); + int ntfTarget = responseData.getTwoByteValue(87); + logger.trace("setResponse(): ntfTarget={}.", ntfTarget); + int ntfFP1CurrentPosition = responseData.getTwoByteValue(89); + logger.trace("setResponse(): ntfFP1CurrentPosition={}.", ntfFP1CurrentPosition); + int ntfFP2CurrentPosition = responseData.getTwoByteValue(91); + logger.trace("setResponse(): ntfFP2CurrentPosition={}.", ntfFP2CurrentPosition); + int ntfFP3CurrentPosition = responseData.getTwoByteValue(93); + logger.trace("setResponse(): ntfFP3CurrentPosition={}.", ntfFP3CurrentPosition); + int ntfFP4CurrentPosition = responseData.getTwoByteValue(95); + logger.trace("setResponse(): ntfFP4CurrentPosition={}.", ntfFP4CurrentPosition); + int ntfRemainingTime = responseData.getFourByteValue(97); + logger.trace("setResponse(): ntfRemainingTime={}.", ntfRemainingTime); + int ntfTimeStamp = responseData.getFourByteValue(99); + logger.trace("setResponse(): ntfTimeStamp={}.", ntfTimeStamp); + int ntfNbrOfAlias = responseData.getOneByteValue(103); + logger.trace("setResponse(): ntfNbrOfAlias={}.", ntfNbrOfAlias); + int ntfAliasOne = responseData.getFourByteValue(104); + logger.trace("setResponse(): ntfAliasOne={}.", ntfAliasOne); + int ntfAliasTwo = responseData.getFourByteValue(108); + logger.trace("setResponse(): ntfAliasTwo={}.", ntfAliasTwo); + int ntfAliasThree = responseData.getFourByteValue(112); + logger.trace("setResponse(): ntfAliasThree={}.", ntfAliasThree); + int ntfAliasFour = responseData.getFourByteValue(116); + logger.trace("setResponse(): ntfAliasFour={}.", ntfAliasFour); + int ntfAliasFive = responseData.getFourByteValue(120); + logger.trace("setResponse(): ntfAliasFive={}.", ntfAliasFive); + + if (!KLF200Response.check4matchingNodeID(logger, reqNodeID, ntfNodeID)) { + break; + } + + if ((ntfName.length() == 0) || ntfName.startsWith("_")) { + ntfName = "#".concat(String.valueOf(ntfNodeID)); + logger.debug("setResponse(): device provided invalid name, using '{}' instead.", ntfName); + } + String commonSerialNumber = VeluxProductSerialNo.toString(ntfSerialNumber); + if (VeluxProductSerialNo.isInvalid(ntfSerialNumber)) { + commonSerialNumber = new String(ntfName); + logger.debug("setResponse(): device provided invalid serial number, using name '{}' instead.", + commonSerialNumber); + } + + product = new VeluxProduct(new VeluxProductName(ntfName), VeluxProductType.get(ntfNodeTypeSubType), + new ProductBridgeIndex(ntfNodeID), ntfOrder, ntfPlacement, ntfVelocity, ntfNodeVariation, + ntfPowerMode, commonSerialNumber, ntfState, ntfCurrentPosition, ntfTarget, ntfRemainingTime, + ntfTimeStamp); + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link GetProduct} + */ + + @Override + public void setProductId(int nodeId) { + logger.trace("setProductId({}) called.", nodeId); + this.reqNodeID = nodeId; + return; + } + + @Override + public VeluxProduct getProduct() { + logger.trace("getProduct(): returning product {}.", product); + return product; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProducts.java new file mode 100644 index 0000000000000..31cc6862c4101 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetProducts.java @@ -0,0 +1,276 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetProducts; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; +import org.openhab.binding.velux.internal.things.VeluxProductName; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; +import org.openhab.binding.velux.internal.things.VeluxProductType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve Products + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getProducts()} to retrieve the currently registered products.
  • + *
+ * + * @see GetProducts + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetProducts extends GetProducts implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetProducts.class); + + private static final String DESCRIPTION = "Retrieve Products"; + private static final Command COMMAND = Command.GW_GET_ALL_NODES_INFORMATION_REQ; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + private VeluxProduct[] productArray = new VeluxProduct[0]; + private int totalNumberOfProducts = 0; + private int nextProductArrayItem = 0; + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + requestData = new byte[0]; + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_ALL_NODES_INFORMATION_CFM: + logger.trace("setResponse(): got GW_GET_ALL_NODES_INFORMATION_CFM."); + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 2)) { + finished = true; + break; + } + int cfmStatus = responseData.getOneByteValue(0); + int cfmTotalNumberOfNodes = responseData.getOneByteValue(1); + logger.trace("setResponse(): status={}.", cfmStatus); + logger.trace("setResponse(): TotalNumberOfNodes={}.", cfmTotalNumberOfNodes); + + // Initialize storage area + productArray = new VeluxProduct[0]; + nextProductArrayItem = 0; + switch (cfmStatus) { + case 0: + logger.trace("setResponse(): returned status: OK - Request accepted."); + totalNumberOfProducts = cfmTotalNumberOfNodes; + productArray = new VeluxProduct[totalNumberOfProducts]; + break; + case 1: + logger.trace("setResponse(): returned status: Error – System table empty."); + finished = true; + break; + default: + finished = true; + logger.warn("setResponse({}): returned status={} (Reserved/unknown).", + Command.get(responseCommand).toString(), cfmStatus); + break; + } + break; + case GW_GET_ALL_NODES_INFORMATION_NTF: + logger.trace("setResponse(): got GW_GET_ALL_NODES_INFORMATION_NTF."); + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 124)) { + finished = true; + break; + } + if (productArray.length == 0) { + logger.warn("setResponse({}): sequence of answers unexpected.", + Command.get(responseCommand).toString()); + finished = true; + break; + } + // Extracting information items + int ntfNodeID = responseData.getOneByteValue(0); + logger.trace("setResponse(): ntfNodeID={}.", ntfNodeID); + int ntfOrder = responseData.getTwoByteValue(1); + logger.trace("setResponse(): ntfOrder={}.", ntfOrder); + int ntfPlacement = responseData.getOneByteValue(3); + logger.trace("setResponse(): ntfPlacement={}.", ntfPlacement); + String ntfName = responseData.getString(4, 64); + logger.trace("setResponse(): ntfName={}.", ntfName); + int ntfVelocity = responseData.getOneByteValue(68); + logger.trace("setResponse(): ntfVelocity={}.", ntfVelocity); + int ntfNodeTypeSubType = responseData.getTwoByteValue(69); + logger.trace("setResponse(): ntfNodeTypeSubType={} ({}).", ntfNodeTypeSubType, + VeluxProductType.get(ntfNodeTypeSubType)); + logger.trace("setResponse(): derived product description={}.", + VeluxProductType.toString(ntfNodeTypeSubType)); + int ntfProductGroup = responseData.getOneByteValue(71); + logger.trace("setResponse(): ntfProductGroup={}.", ntfProductGroup); + int ntfProductType = responseData.getOneByteValue(72); + logger.trace("setResponse(): ntfProductType={}.", ntfProductType); + int ntfNodeVariation = responseData.getOneByteValue(73); + logger.trace("setResponse(): ntfNodeVariation={}.", ntfNodeVariation); + int ntfPowerMode = responseData.getOneByteValue(74); + logger.trace("setResponse(): ntfPowerMode={}.", ntfPowerMode); + int ntfBuildNumber = responseData.getOneByteValue(75); + logger.trace("setResponse(): ntfBuildNumber={}.", ntfBuildNumber); + byte[] ntfSerialNumber = responseData.getByteArray(76, 8); + logger.trace("setResponse(): ntfSerialNumber={}.", ntfSerialNumber); + int ntfState = responseData.getOneByteValue(84); + logger.trace("setResponse(): ntfState={}.", ntfState); + int ntfCurrentPosition = responseData.getTwoByteValue(85); + logger.trace("setResponse(): ntfCurrentPosition={}.", ntfCurrentPosition); + int ntfTarget = responseData.getTwoByteValue(87); + logger.trace("setResponse(): ntfTarget={}.", ntfTarget); + int ntfFP1CurrentPosition = responseData.getTwoByteValue(89); + logger.trace("setResponse(): ntfFP1CurrentPosition={}.", ntfFP1CurrentPosition); + int ntfFP2CurrentPosition = responseData.getTwoByteValue(91); + logger.trace("setResponse(): ntfFP2CurrentPosition={}.", ntfFP2CurrentPosition); + int ntfFP3CurrentPosition = responseData.getTwoByteValue(93); + logger.trace("setResponse(): ntfFP3CurrentPosition={}.", ntfFP3CurrentPosition); + int ntfFP4CurrentPosition = responseData.getTwoByteValue(95); + logger.trace("setResponse(): ntfFP4CurrentPosition={}.", ntfFP4CurrentPosition); + int ntfRemainingTime = responseData.getTwoByteValue(97); + logger.trace("setResponse(): ntfRemainingTime={}.", ntfRemainingTime); + int ntfTimeStamp = responseData.getFourByteValue(99); + logger.trace("setResponse(): ntfTimeStamp={}.", ntfTimeStamp); + int ntfNbrOfAlias = responseData.getOneByteValue(103); + logger.trace("setResponse(): ntfNbrOfAlias={}.", ntfNbrOfAlias); + int ntfAliasOne = responseData.getFourByteValue(104); + logger.trace("setResponse(): ntfAliasOne={}.", ntfAliasOne); + int ntfAliasTwo = responseData.getFourByteValue(108); + logger.trace("setResponse(): ntfAliasTwo={}.", ntfAliasTwo); + int ntfAliasThree = responseData.getFourByteValue(112); + logger.trace("setResponse(): ntfAliasThree={}.", ntfAliasThree); + int ntfAliasFour = responseData.getFourByteValue(116); + logger.trace("setResponse(): ntfAliasFour={}.", ntfAliasFour); + int ntfAliasFive = responseData.getFourByteValue(120); + logger.trace("setResponse(): ntfAliasFive={}.", ntfAliasFive); + + if ((ntfName.length() == 0) || ntfName.startsWith("_")) { + ntfName = "#".concat(String.valueOf(ntfNodeID)); + logger.debug("setResponse(): device provided invalid name, using '{}' instead.", ntfName); + } + + String commonSerialNumber = VeluxProductSerialNo.toString(ntfSerialNumber); + if (VeluxProductSerialNo.isInvalid(ntfSerialNumber)) { + commonSerialNumber = new String(ntfName); + logger.debug("setResponse(): device provided invalid serial number, using name '{}' instead.", + commonSerialNumber); + } + + VeluxProduct product = new VeluxProduct(new VeluxProductName(ntfName), + VeluxProductType.get(ntfNodeTypeSubType), new ProductBridgeIndex(ntfNodeID), ntfOrder, + ntfPlacement, ntfVelocity, ntfNodeVariation, ntfPowerMode, commonSerialNumber, ntfState, + ntfCurrentPosition, ntfTarget, ntfRemainingTime, ntfTimeStamp); + if (nextProductArrayItem < totalNumberOfProducts) { + productArray[nextProductArrayItem++] = product; + } else { + logger.warn("setResponse(): expected {} products, received one more, ignoring it.", + totalNumberOfProducts); + } + success = true; + break; + + case GW_GET_ALL_NODES_INFORMATION_FINISHED_NTF: + logger.trace("setResponse(): got GW_GET_ALL_NODES_INFORMATION_FINISHED_NTF."); + logger.debug("setResponse(): finished-packet received."); + success = true; + finished = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link GetProducts} + */ + + @Override + public VeluxProduct[] getProducts() { + if (success && finished) { + logger.trace("getProducts(): returning array of {} products.", productArray.length); + return productArray; + } else { + logger.trace("getProducts(): returning null."); + return new VeluxProduct[0]; + } + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetScenes.java new file mode 100644 index 0000000000000..6499c5f4c50e7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetScenes.java @@ -0,0 +1,175 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.GetScenes; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxProductState; +import org.openhab.binding.velux.internal.things.VeluxScene; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve Scenes + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getScenes()} to retrieve the set of current scenes.
  • + *
+ * + * @see GetScenes + * @see SlipBridgeCommunicationProtocol + * + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetScenes extends GetScenes implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetScenes.class); + + private static final String DESCRIPTION = "Retrieve Scenes"; + private static final Command COMMAND = Command.GW_GET_SCENE_LIST_REQ; + + /* + * Message Objects + */ + + private boolean success; + private boolean finished; + + private int sceneIdx; + private VeluxScene[] scenes = new VeluxScene[0]; + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + return EMPTYDATA; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_GET_SCENE_LIST_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 1)) { + finished = true; + break; + } + int ntfTotalNumberOfObjects = responseData.getOneByteValue(0); + scenes = new VeluxScene[ntfTotalNumberOfObjects]; + if (ntfTotalNumberOfObjects == 0) { + logger.trace("setResponse(): no scenes defined."); + success = true; + finished = true; + } else { + logger.trace("setResponse(): {} scenes defined.", ntfTotalNumberOfObjects); + } + sceneIdx = 0; + break; + case GW_GET_SCENE_LIST_NTF: + if (thisResponseData.length < 1) { + logger.trace("setResponse(): malformed response packet (length is {} less than one).", + thisResponseData.length); + finished = true; + break; + } + int ntfNumberOfObject = responseData.getOneByteValue(0); + logger.trace("setResponse(): NTF number of objects={}.", ntfNumberOfObject); + if (ntfNumberOfObject == 0) { + logger.trace("setResponse(): finished."); + finished = true; + success = true; + } + if (thisResponseData.length != (2 + 65 * ntfNumberOfObject)) { + logger.trace("setResponse(): malformed response packet (real length {}, expected length {}).", + thisResponseData.length, (2 + 65 * ntfNumberOfObject)); + finished = true; + break; + } + for (int objectIndex = 0; objectIndex < ntfNumberOfObject; objectIndex++) { + int ntfSceneID = responseData.getOneByteValue(1 + 65 * objectIndex); + int beginOfString = 2 + 65 * objectIndex; + String ntfSceneName = responseData.getString(beginOfString, 64); + logger.trace("setResponse(): scene {}, name {}.", ntfSceneID, ntfSceneName); + scenes[sceneIdx++] = new VeluxScene(ntfSceneName, ntfSceneID, false, new VeluxProductState[0]); + } + int ntfRemainingNumberOfObject = responseData.getOneByteValue(1 + 65 * ntfNumberOfObject); + logger.trace("setResponse(): {} scenes remaining.", ntfRemainingNumberOfObject); + if (ntfRemainingNumberOfObject == 0) { + logger.trace("setResponse(): finished."); + finished = true; + success = true; + } + break; + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /** + * =========================================================== + *

+ * Public Methods required for abstract class {@link GetScenes}. + */ + @Override + public VeluxScene[] getScenes() { + logger.trace("getScenes(): returning {} scenes.", scenes.length); + return scenes; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetWLANConfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetWLANConfig.java new file mode 100644 index 0000000000000..532544e8dae85 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCgetWLANConfig.java @@ -0,0 +1,124 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.internal.bridge.common.GetWLANConfig; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxGwWLAN; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve WLAN configuration + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #getWLANConfig} to retrieve the current WLAN configuration.
  • + *
+ * + * @see GetWLANConfig + * @see SlipBridgeCommunicationProtocol + * + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCgetWLANConfig extends GetWLANConfig implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCgetWLANConfig.class); + + private static final String DESCRIPTION = "Retrieve WLAN configuration"; + private static final Command COMMAND = Command.GW_GET_NETWORK_SETUP_REQ; + + private static final String UNSUPPORTED = "*** unsupported-by-current-gateway-firmware ***"; + + /* + * Message Objects + */ + + private byte[] requestData = new byte[0]; + private short responseCommand; + @SuppressWarnings("unused") + private byte @Nullable [] responseData; + + /* + * =========================================================== + * Constructor Method + */ + + public SCgetWLANConfig() { + logger.trace("SCgetWLANConfig(constructor) called."); + requestData = new byte[1]; + } + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + return requestData; + } + + @Override + public void setResponse(short thisResponseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + logger.trace("setResponseCommand({}, {}) called.", thisResponseCommand, new Packet(thisResponseData)); + responseCommand = thisResponseCommand; + responseData = thisResponseData; + } + + @Override + public boolean isCommunicationFinished() { + return true; + } + + @Override + public boolean isCommunicationSuccessful() { + return (responseCommand == Command.GW_GET_NETWORK_SETUP_CFM.getShort()); + } + + /* + * =========================================================== + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public VeluxGwWLAN getWLANConfig() { + logger.trace("getWLANConfig() called."); + // Enhancement idea: Velux should provide an enhanced API. + return new VeluxGwWLAN(UNSUPPORTED, UNSUPPORTED); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogin.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogin.java new file mode 100644 index 0000000000000..f0c87007bcb31 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogin.java @@ -0,0 +1,166 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.common.Login; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Authenticate / login + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setPassword(String)} to define the authentication reqPassword to be used.
  • + *
  • {@link #getAuthToken()} to retrieve the authentication reqPassword.
  • + *
+ * + * @see Login + * @see SlipBridgeCommunicationProtocol + * + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SClogin extends Login implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SClogin.class); + + private static final String DESCRIPTION = "Authenticate / login"; + private static final Command COMMAND = Command.GW_PASSWORD_ENTER_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private String reqPassword = ""; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + requestData = new byte[32]; + byte[] password = reqPassword.getBytes(); + System.arraycopy(password, 0, requestData, 0, password.length); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = true; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_PASSWORD_ENTER_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 1)) { + break; + } + int cfmStatus = responseData.getOneByteValue(0); + switch (cfmStatus) { + case 0: + logger.info("{} bridge connection successfully established (login succeeded).", + VeluxBindingConstants.BINDING_ID); + logger.debug("setResponse(): returned status: The request was successful."); + success = true; + break; + case 1: + logger.warn("{} bridge connection successfully established but login failed.", + VeluxBindingConstants.BINDING_ID); + logger.debug("setResponse(): returned status: The request failed."); + break; + default: + logger.warn("setResponse(): returned status={} (not defined).", cfmStatus); + break; + } + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public void setPassword(String thisPassword) { + logger.trace("setPassword({}) called.", thisPassword.replaceAll(".", "*")); + reqPassword = thisPassword; + return; + } + + @Override + public String getAuthToken() { + logger.trace("getAuthToken() called, returning {}.", reqPassword.replaceAll(".", "*")); + return reqPassword; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogout.java new file mode 100644 index 0000000000000..2090ddcad7693 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SClogout.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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.Logout; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Retrieve LAN configuration + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * There are no methods in addition to the mentioned interface. + * + * @see Logout + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SClogout extends Logout implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SClogout.class); + + private static final String DESCRIPTION = "Deauthenticate / logout"; + private static final Command COMMAND = Command.GW_OPENHAB_CLOSE; + + /* + * =========================================================== + * Message Objects + */ + + private final byte[] emptyPacket = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + return emptyPacket; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = true; + finished = true; + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductCommand.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductCommand.java new file mode 100644 index 0000000000000..da54c23d71bcf --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductCommand.java @@ -0,0 +1,313 @@ +/** + * 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.velux.internal.bridge.slip; + +import java.util.Random; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.RunProductCommand; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Send Command to Actuator + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setNodeAndMainParameter} to define the node and intended parameter value.
  • + *
+ * + * @see RunProductCommand + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCrunProductCommand extends RunProductCommand implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCrunProductCommand.class); + + private static final String DESCRIPTION = "Send Command to Actuator"; + private static final Command COMMAND = Command.GW_COMMAND_SEND_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqSessionID = 0; + private int reqCommandOriginator = 8; // SAAC + private int reqPriorityLevel = 5; // Comfort Level 2 + private int reqParameterActive = 0; // Main Parameter + private int reqFPI1 = 0; // Functional Parameter Indicator 1 set of bits + private int reqFPI2 = 0; // Functional Parameter Indicator 2 set of bits + private int reqMainParameter = 0; // for FunctionalParameterValueArray + private int reqIndexArrayCount = 1; // One node will be addressed + private int reqIndexArray01 = 1; // This is the node + private int reqPriorityLevelLock = 0; // Do not set a new lock on priority level + private int reqPL03 = 0; // unused + private int reqPL47 = 0; // unused + private int reqLockTime = 0; // 30 seconds + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Constructor Method + */ + + public SCrunProductCommand() { + logger.debug("SCgetProduct(Constructor) called."); + Random rand = new Random(); + reqSessionID = rand.nextInt(0x0fff); + logger.debug("SCgetProduct(): starting sessions with the random number {}.", reqSessionID); + } + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {}.", COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + Packet request = new Packet(new byte[66]); + reqSessionID = (reqSessionID + 1) & 0xffff; + request.setTwoByteValue(0, reqSessionID); + request.setOneByteValue(2, reqCommandOriginator); + request.setOneByteValue(3, reqPriorityLevel); + request.setOneByteValue(4, reqParameterActive); + request.setOneByteValue(5, reqFPI1); + request.setOneByteValue(6, reqFPI2); + request.setTwoByteValue(7, reqMainParameter); + request.setOneByteValue(41, reqIndexArrayCount); + request.setOneByteValue(42, reqIndexArray01); + request.setOneByteValue(62, reqPriorityLevelLock); + request.setOneByteValue(63, reqPL03); + request.setOneByteValue(64, reqPL47); + request.setOneByteValue(65, reqLockTime); + logger.trace("getRequestDataAsArrayOfBytes(): ntfSessionID={}.", reqSessionID); + logger.trace("getRequestDataAsArrayOfBytes(): reqCommandOriginator={}.", reqCommandOriginator); + logger.trace("getRequestDataAsArrayOfBytes(): reqPriorityLevel={}.", reqPriorityLevel); + logger.trace("getRequestDataAsArrayOfBytes(): reqParameterActive={}.", reqParameterActive); + logger.trace("getRequestDataAsArrayOfBytes(): reqFPI1={}.", reqFPI1); + logger.trace("getRequestDataAsArrayOfBytes(): reqFPI2={}.", reqFPI2); + logger.trace("getRequestDataAsArrayOfBytes(): reqMainParameter={}.", reqMainParameter); + logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArrayCount={}.", reqIndexArrayCount); + logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArray01={}.", reqIndexArray01); + logger.trace("getRequestDataAsArrayOfBytes(): reqPriorityLevelLock={}.", reqPriorityLevelLock); + logger.trace("getRequestDataAsArrayOfBytes(): reqPL03={}.", reqPL03); + logger.trace("getRequestDataAsArrayOfBytes(): reqPL47={}.", reqPL47); + logger.trace("getRequestDataAsArrayOfBytes(): reqLockTime={}.", reqLockTime); + requestData = request.toByteArray(); + logger.trace("getRequestDataAsArrayOfBytes() data is {}.", new Packet(requestData).toString()); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_COMMAND_SEND_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 3)) { + finished = true; + break; + } + int cfmSessionID = responseData.getTwoByteValue(0); + int cfmStatus = responseData.getOneByteValue(2); + switch (cfmStatus) { + case 0: + logger.info("setResponse(): returned status: Error – Command rejected."); + finished = true; + break; + case 1: + logger.debug("setResponse(): returned status: OK - Command is accepted."); + if (!KLF200Response.check4matchingSessionID(logger, cfmSessionID, reqSessionID)) { + finished = true; + } else if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + finished = true; + success = true; + } + break; + default: + logger.warn("setResponse(): returned status={} (not defined).", cfmStatus); + finished = true; + break; + } + break; + + case GW_COMMAND_RUN_STATUS_NTF: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 13)) { + finished = true; + break; + } + int ntfSessionID = responseData.getTwoByteValue(0); + int ntfStatusiD = responseData.getOneByteValue(2); + int ntfIndex = responseData.getOneByteValue(3); + int ntfNodeParameter = responseData.getOneByteValue(4); + int ntfParameterValue = responseData.getTwoByteValue(5); + int ntfRunStatus = responseData.getOneByteValue(7); + int ntfStatusReply = responseData.getOneByteValue(8); + int ntfInformationCode = responseData.getFourByteValue(9); + // Extracting information items + logger.debug("setResponse(): ntfSessionID={} (requested {}).", ntfSessionID, reqSessionID); + logger.debug("setResponse(): ntfStatusiD={}.", ntfStatusiD); + logger.debug("setResponse(): ntfIndex={}.", ntfIndex); + logger.debug("setResponse(): ntfNodeParameter={}.", ntfNodeParameter); + logger.debug("setResponse(): ntfParameterValue={}.", ntfParameterValue); + logger.debug("setResponse(): ntfRunStatus={}.", ntfRunStatus); + logger.debug("setResponse(): ntfStatusReply={}.", ntfStatusReply); + logger.debug("setResponse(): ntfInformationCode={}.", ntfInformationCode); + + if (!KLF200Response.check4matchingSessionID(logger, ntfSessionID, reqSessionID)) { + finished = true; + } + switch (ntfRunStatus) { + case 0: + logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_COMPLETED."); + success = true; + break; + case 1: + logger.info("setResponse(): returned ntfRunStatus: EXECUTION_FAILED."); + finished = true; + break; + case 2: + logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_ACTIVE."); + break; + default: + logger.warn("setResponse(): returned ntfRunStatus={} (not defined).", ntfRunStatus); + finished = true; + break; + } + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + + case GW_COMMAND_REMAINING_TIME_NTF: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 6)) { + finished = true; + break; + } + int timeNtfSessionID = responseData.getTwoByteValue(0); + int timeNtfIndex = responseData.getOneByteValue(2); + int timeNtfNodeParameter = responseData.getOneByteValue(3); + int timeNtfSeconds = responseData.getTwoByteValue(4); + + if (!KLF200Response.check4matchingSessionID(logger, timeNtfSessionID, reqSessionID)) { + finished = true; + } + + // Extracting information items + logger.debug("setResponse(): timeNtfSessionID={}.", timeNtfSessionID); + logger.debug("setResponse(): timeNtfIndex={}.", timeNtfIndex); + logger.debug("setResponse(): timeNtfNodeParameter={}.", timeNtfNodeParameter); + logger.debug("setResponse(): timeNtfSeconds={}.", timeNtfSeconds); + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + + case GW_SESSION_FINISHED_NTF: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 2)) { + break; + } + int finishedNtfSessionID = responseData.getTwoByteValue(0); + if (!KLF200Response.check4matchingSessionID(logger, finishedNtfSessionID, reqSessionID)) { + break; + } + logger.debug("setResponse(): finishedNtfSessionID={}.", finishedNtfSessionID); + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link RunProductCommand} + */ + + @Override + public SCrunProductCommand setNodeAndMainParameter(int nodeId, int value) { + logger.debug("setNodeAndMainParameter({}) called.", nodeId); + this.reqIndexArray01 = nodeId; + this.reqMainParameter = value; + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java new file mode 100644 index 0000000000000..dbbb97d1bbae7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductDiscovery.java @@ -0,0 +1,154 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.RunProductDiscovery; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Ask the Bridge to detect (new) products/actuators + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * There are no methods in addition to the mentioned interface. + * + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCrunProductDiscovery extends RunProductDiscovery implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCrunProductDiscovery.class); + + private static final String DESCRIPTION = "Detect Products/Actuators"; + private static final Command COMMAND = Command.GW_CS_DISCOVER_NODES_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqNodeType = 0; // NO_TYPE (All nodes except controller) + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + logger.trace("getRequestDataAsArrayOfBytes() returns data for detection with type {}.", reqNodeType); + Packet request = new Packet(new byte[1]); + request.setOneByteValue(0, reqNodeType); + requestData = request.toByteArray(); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_CS_DISCOVER_NODES_CFM: + logger.trace("setResponse(): received confirmation for discovery mode."); + break; + + case GW_CS_DISCOVER_NODES_NTF: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 131)) { + break; + } + int ntfDiscoverStatus = responseData.getOneByteValue(130); + switch (ntfDiscoverStatus) { + case 0: + logger.trace("setResponse(): returned status: OK. Discovered nodes. See bit array."); + success = true; + break; + case 5: + logger.warn("setResponse(): returned status: ERROR - Failed. CS not ready."); + break; + case 6: + logger.trace( + "setResponse(): returned status: OK. But some nodes were not added to system table (e.g. System table has reached its limit)."); + break; + case 7: + logger.warn("setResponse(): returned status: ERROR - CS busy with another task."); + break; + default: + logger.warn("setResponse({}): returned status={} (Reserved/unknown).", + Command.get(responseCommand).toString(), ntfDiscoverStatus); + break; + } + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductIdentification.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductIdentification.java new file mode 100644 index 0000000000000..1b62afa82ed25 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductIdentification.java @@ -0,0 +1,204 @@ +/** + * 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.velux.internal.bridge.slip; + +import java.util.Random; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.RunProductIdentification; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Identify Product + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setProductId(int)} to define the intended product.
  • + *
+ * + * @see RunProductIdentification + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCrunProductIdentification extends RunProductIdentification implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCrunProductIdentification.class); + + private static final String DESCRIPTION = "Identify Product"; + private static final Command COMMAND = Command.GW_WINK_SEND_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqSessionID = 0; + private int reqCommandOriginator = 8; // SAAC + private int reqPriorityLevel = 5; // Comfort Level 2 + private int reqWinkState = 1; // Enable wink + private int reqWinkTime = 2; // Winktime = 2 seconds + private int reqIndexArrayCount = 1; // Number of actuators to be winking + private int reqIndexValue0 = 0; // Value for the ONE actuator + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Constructor Method + */ + + /** + * Constructor. + *

+ * Initializes the session id {@link #reqSessionID} with a random start value. + */ + public SCrunProductIdentification() { + logger.debug("SCrunProductIdentification(Constructor) called."); + Random rand = new Random(); + reqSessionID = rand.nextInt(0x0fff); + logger.debug("SCrunProductIdentification(): starting sessions with the random number {}.", reqSessionID); + } + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + Packet request = new Packet(new byte[27]); + reqSessionID = (reqSessionID + 1) & 0xffff; + request.setTwoByteValue(0, reqSessionID); + request.setOneByteValue(2, reqCommandOriginator); + request.setOneByteValue(3, reqPriorityLevel); + request.setOneByteValue(4, reqWinkState); + request.setOneByteValue(5, reqWinkTime); + request.setOneByteValue(6, reqIndexArrayCount); + request.setTwoByteValue(7, reqIndexValue0); + requestData = request.toByteArray(); + logger.trace("getRequestDataAsArrayOfBytes() data is {}.", new Packet(requestData).toString()); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_WINK_SEND_CFM: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 3)) { + break; + } + int cfmSessionID = responseData.getTwoByteValue(0); + int cfmStatus = responseData.getOneByteValue(2); + switch (cfmStatus) { + case 0: + logger.trace("setResponse(): returned status: Error - Wink is rejected."); + break; + case 1: + if (!KLF200Response.check4matchingSessionID(logger, cfmSessionID, reqSessionID)) { + break; + } + logger.trace("setResponse(): returned status: OK – Wink is accepted."); + success = true; + break; + default: + logger.warn("setResponse({}): returned status={} (Reserved/unknown).", + Command.get(responseCommand).toString(), cfmStatus); + break; + } + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link RunProductIdentification} + */ + + /** + * Constructor Addon Method. + *

+ * Passes the intended Actuator Identifier towards this class for building the request. + * + * @param actuatorId as type int describing the scene to be processed. + * @return this of type {@link SCrunProductIdentification} as class itself. + */ + @Override + public SCrunProductIdentification setProductId(int actuatorId) { + logger.trace("setProductId({}) called.", actuatorId); + this.reqIndexValue0 = actuatorId; + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductSearch.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductSearch.java new file mode 100644 index 0000000000000..ee10d5e57d7bf --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunProductSearch.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 + */ +package org.openhab.binding.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.RunProductSearch; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxGwState; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Check for lost Nodes + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * Implementing the protocol-independent class {@link RunProductSearch}. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link SCrunProductSearch#getState} to retrieve the Velux gateway status.
  • + *
+ * + * @see RunProductSearch + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCrunProductSearch extends RunProductSearch implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCrunProductSearch.class); + + private static final String DESCRIPTION = "Check for lost Nodes"; + private static final Command COMMAND = Command.GW_GET_STATE_REQ; + + /* + * Message Objects + */ + + private byte[] requestData = new byte[0]; + private short responseCommand; + private byte[] responseData = new byte[0]; + + /* + * =========================================================== + * Methods required for interface {@link SlipBridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + requestData = new byte[0]; + return requestData; + } + + @Override + public void setResponse(short thisResponseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + logger.trace("setResponseCommand({}, {}) called.", thisResponseCommand, new Packet(thisResponseData)); + responseCommand = thisResponseCommand; + responseData = thisResponseData; + } + + @Override + public boolean isCommunicationFinished() { + return (responseCommand == Command.GW_GET_STATE_CFM.getShort()); + } + + @Override + public boolean isCommunicationSuccessful() { + return (responseCommand == Command.GW_GET_STATE_CFM.getShort()); + } + + /* + * =========================================================== + * Methods in addition to interface {@link BridgeCommunicationProtocol}. + */ + + public VeluxGwState getState() { + byte stateValue = responseData[0]; + byte subStateValue = responseData[1]; + VeluxGwState thisGwState = new VeluxGwState(stateValue, subStateValue); + logger.trace("getState() returns {} ({}).", thisGwState, thisGwState.toDescription()); + return thisGwState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunScene.java new file mode 100644 index 0000000000000..8a221aee1960e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCrunScene.java @@ -0,0 +1,291 @@ +/** + * 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.velux.internal.bridge.slip; + +import java.util.Random; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.RunScene; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Run Scene + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setSceneId} to define the scene to be executed.
  • + *
+ * + * @see RunScene + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCrunScene extends RunScene implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCrunScene.class); + + private static final String DESCRIPTION = "Run Scene"; + private static final Command COMMAND = Command.GW_ACTIVATE_SCENE_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqSessionID = 0; + private int reqCommandOriginator = 8; // SAAC + private int reqPriorityLevel = 5; // Comfort Level 2 + private int reqSceneID = -1; // SceneID as one unsigned byte number + private int reqVelocity = 0; // The product group operates by its default velocity. + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Constructor Method + */ + + /** + * Constructor. + *

+ * Initializes the session id {@link #reqSessionID} with a random start value. + */ + public SCrunScene() { + logger.debug("SCrunScene(Constructor) called."); + Random rand = new Random(); + reqSessionID = rand.nextInt(0x0fff); + logger.debug("SCrunScene(): starting sessions with the random number {}.", reqSessionID); + } + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.trace("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + Packet request = new Packet(new byte[6]); + reqSessionID = (reqSessionID + 1) & 0xffff; + request.setTwoByteValue(0, reqSessionID); + request.setOneByteValue(2, reqCommandOriginator); + request.setOneByteValue(3, reqPriorityLevel); + request.setOneByteValue(4, reqSceneID); + request.setOneByteValue(5, reqVelocity); + requestData = request.toByteArray(); + logger.trace("getRequestCommand(): SessionID={}.", reqSessionID); + logger.trace("getRequestCommand(): CommandOriginator={}.", reqCommandOriginator); + logger.trace("getRequestCommand(): PriorityLevel={}.", reqPriorityLevel); + logger.trace("getRequestCommand(): SceneID={}.", reqSceneID); + logger.trace("getRequestCommand(): Velocity={}.", reqVelocity); + logger.debug("getRequestCommand() returns {} ({}) with SceneID {}.", COMMAND.name(), COMMAND.getCommand(), + reqSceneID); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_ACTIVATE_SCENE_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 3)) { + finished = true; + break; + } + int cfmStatus = responseData.getOneByteValue(0); + int cfmSessionID = responseData.getTwoByteValue(1); + switch (cfmStatus) { + case 0: + logger.trace("setResponse(): returned status: OK - Request accepted."); + if (!KLF200Response.check4matchingSessionID(logger, cfmSessionID, reqSessionID)) { + finished = true; + } else if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + case 1: + finished = true; + logger.trace("setResponse(): returned status: Error – Invalid parameter."); + break; + case 2: + finished = true; + logger.trace("setResponse(): returned status: Error – Request rejected."); + break; + default: + finished = true; + logger.warn("setResponse({}): returned status={} (Reserved/unknown).", + Command.get(responseCommand).toString(), cfmStatus); + break; + } + break; + + case GW_COMMAND_RUN_STATUS_NTF: + logger.trace("setResponse(): received GW_COMMAND_RUN_STATUS_NTF, continuing."); + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 13)) { + logger.trace("setResponse(): GW_COMMAND_RUN_STATUS_NTF received with invalid length."); + finished = true; + break; + } + // Extracting information items + int ntfSessionID = responseData.getTwoByteValue(0); + int ntfStatusID = responseData.getOneByteValue(2); + int ntfIndex = responseData.getOneByteValue(3); + int ntfNodeParameter = responseData.getOneByteValue(4); + int ntfParameterValue = responseData.getTwoByteValue(5); + int ntfRunStatus = responseData.getOneByteValue(7); + int ntfStatusReply = responseData.getOneByteValue(8); + int ntfInformationCode = responseData.getFourByteValue(9); + + logger.trace("setResponse(): SessionID={}.", ntfSessionID); + logger.trace("setResponse(): StatusID={}.", ntfStatusID); + logger.trace("setResponse(): Index={}.", ntfIndex); + logger.trace("setResponse(): NodeParameter={}.", ntfNodeParameter); + logger.trace("setResponse(): ParameterValue={}.", ntfParameterValue); + logger.trace("setResponse(): RunStatus={}.", ntfRunStatus); + logger.trace("setResponse(): StatusReply={}.", ntfStatusReply); + logger.trace("setResponse(): InformationCode={}.", ntfInformationCode); + + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + + case GW_COMMAND_REMAINING_TIME_NTF: + logger.trace("setResponse(): received GW_COMMAND_REMAINING_TIME_NTF, continuing."); + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 6)) { + logger.trace("setResponse(): GW_COMMAND_REMAINING_TIME_NTF received with invalid length."); + finished = true; + break; + } + // Extracting information items + ntfSessionID = responseData.getTwoByteValue(0); + ntfIndex = responseData.getOneByteValue(2); + ntfNodeParameter = responseData.getOneByteValue(3); + int ntfSeconds = responseData.getTwoByteValue(4); + + logger.trace("setResponse(): SessionID={}.", ntfSessionID); + logger.trace("setResponse(): Index={}.", ntfIndex); + logger.trace("setResponse(): NodeParameter={}.", ntfNodeParameter); + logger.trace("setResponse(): Seconds={}.", ntfSeconds); + + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + + case GW_SESSION_FINISHED_NTF: + logger.trace("setResponse(): received GW_SESSION_FINISHED_NTF."); + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 2)) { + logger.trace("setResponse(): GW_SESSION_FINISHED_NTF received with invalid length."); + finished = true; + break; + } + // Extracting information items + ntfSessionID = responseData.getTwoByteValue(0); + + logger.trace("setResponse(): SessionID={}.", ntfSessionID); + + success = true; + finished = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link RunScene} + */ + + @Override + public SCrunScene setSceneId(int sceneId) { + logger.trace("setProductId({}) called.", sceneId); + this.reqSceneID = sceneId; + return this; + } + + @Override + public SCrunScene setVelocity(int velocity) { + logger.trace("setVelocity({}) called.", velocity); + this.reqVelocity = velocity; + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetHouseStatusMonitor.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetHouseStatusMonitor.java new file mode 100644 index 0000000000000..a7d4ec71e3186 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetHouseStatusMonitor.java @@ -0,0 +1,145 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.SetHouseStatusMonitor; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Modify HouseStatusMonitor + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #serviceActivation} to define the new service activation state.
  • + *
+ * + * @see SetHouseStatusMonitor + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCsetHouseStatusMonitor extends SetHouseStatusMonitor implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCsetHouseStatusMonitor.class); + + private static final String DESCRIPTION = "Modify HouseStatusMonitor"; + + /* + * =========================================================== + * Message Content Parameters + */ + + private boolean activateService = false; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + Command command = activateService ? Command.GW_HOUSE_STATUS_MONITOR_ENABLE_REQ + : Command.GW_HOUSE_STATUS_MONITOR_DISABLE_REQ; + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", command.name(), command.getCommand()); + return command.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + logger.debug("getRequestDataAsArrayOfBytes() data is {}.", new Packet(requestData).toString()); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = true; + switch (Command.get(responseCommand)) { + case GW_HOUSE_STATUS_MONITOR_ENABLE_CFM: + logger.trace("setResponse(): service enable confirmed by bridge."); + // returned enabled: successful if enable requested + success = activateService; + break; + case GW_HOUSE_STATUS_MONITOR_DISABLE_CFM: + logger.trace("setResponse(): service disable confirmed by bridge."); + // returned disabled: successful if disable requested + success = !activateService; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link SetHouseStatusMonitor} + */ + + @Override + public SetHouseStatusMonitor serviceActivation(boolean enableService) { + this.activateService = enableService; + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java new file mode 100644 index 0000000000000..6f6626be6498f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetLimitation.java @@ -0,0 +1,320 @@ +/** + * 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.velux.internal.bridge.slip; + +import java.util.Random; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.SetProductLimitation; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Modify Product Limitations + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setActuatorIdAndMinimumLimitation} to set the lower limitation of one specific product.
  • + *
  • {@link #setActuatorIdAndMaximumLimitation} to set the higher limitation of one specific product.
  • + *
  • {@link #setActuatorIdAndResetLimitation} to reset any limitation of one specific product.
  • + *
+ * + * @see SetProductLimitation + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SCsetLimitation extends SetProductLimitation implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCsetLimitation.class); + + private static final String DESCRIPTION = "Modify Actuator Limitation"; + private static final Command COMMAND = Command.GW_SET_LIMITATION_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqSessionID = 0; + private int reqCommandOriginator = 8; // SAAC + private int reqPriorityLevel = 5; // Comfort Level 2 + private int reqIndexArrayCount = 1; // One node will be addressed + private int reqIndexArray01 = 1; // This is the node + private int reqParameterID = 0; // MP = Main parameter + private int reqLimitationValueMin = VeluxProductPosition.VPP_VELUX_IGNORE; // will be set lateron + private int reqLimitationValueMax = VeluxProductPosition.VPP_VELUX_IGNORE; // will be set lateron + private int reqLimitationTime = 0; // 0 = 30 seconds, 1 = 60 seconds, ..., 253 = unlimited, 254 = clear entry for + // the Master, 255 = clear all + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Constructor Method + */ + + public SCsetLimitation() { + logger.debug("SCsetLimitation(Constructor) called."); + Random rand = new Random(); + reqSessionID = rand.nextInt(0x0fff); + logger.debug("SCsetLimitation(): starting sessions with the random number {}.", reqSessionID); + } + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {} ({}).", COMMAND.name(), COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + Packet request = new Packet(new byte[31]); + reqSessionID = (reqSessionID + 1) & 0xffff; + request.setTwoByteValue(0, reqSessionID); + request.setOneByteValue(2, reqCommandOriginator); + request.setOneByteValue(3, reqPriorityLevel); + request.setOneByteValue(4, reqIndexArrayCount); + request.setOneByteValue(5, reqIndexArray01); + request.setOneByteValue(25, reqParameterID); + request.setTwoByteValue(26, reqLimitationValueMin); + request.setTwoByteValue(28, reqLimitationValueMax); + request.setOneByteValue(30, reqLimitationTime); + logger.trace("getRequestDataAsArrayOfBytes(): ntfSessionID={}.", reqSessionID); + logger.trace("getRequestDataAsArrayOfBytes(): reqCommandOriginator={}.", reqCommandOriginator); + logger.trace("getRequestDataAsArrayOfBytes(): reqPriorityLevel={}.", reqPriorityLevel); + logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArrayCount={}.", reqIndexArrayCount); + logger.trace("getRequestDataAsArrayOfBytes(): reqIndexArray01={}.", reqIndexArray01); + logger.trace("getRequestDataAsArrayOfBytes(): reqParameterID={}.", reqParameterID); + logger.trace("getRequestDataAsArrayOfBytes(): reqLimitationValueMin={}.", reqLimitationValueMin); + logger.trace("getRequestDataAsArrayOfBytes(): reqLimitationValueMax={}.", reqLimitationValueMax); + logger.trace("getRequestDataAsArrayOfBytes(): reqLimitationTime={}.", reqLimitationTime); + requestData = request.toByteArray(); + logger.trace("getRequestDataAsArrayOfBytes() data is {}.", new Packet(requestData).toString()); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_SET_LIMITATION_CFM: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 3)) { + finished = true; + break; + } + int cfmSessionID = responseData.getTwoByteValue(0); + int cfmStatus = responseData.getOneByteValue(2); + switch (cfmStatus) { + case 0: + logger.info("setResponse(): returned status: Error – Command rejected."); + finished = true; + break; + case 1: + logger.debug("setResponse(): returned status: OK - Command is accepted."); + if (!KLF200Response.check4matchingSessionID(logger, cfmSessionID, reqSessionID)) { + finished = true; + } + break; + default: + logger.warn("setResponse(): returned status={} (not defined).", cfmStatus); + finished = true; + break; + } + break; + + case GW_LIMITATION_STATUS_NTF: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 10)) { + break; + } + // Extracting information items + int ntfSessionID = responseData.getTwoByteValue(0); + int ntfNodeID = responseData.getOneByteValue(2); + int ntfParameterID = responseData.getOneByteValue(3); + int ntfMinValue = responseData.getTwoByteValue(4); + int ntfMaxValue = responseData.getTwoByteValue(6); + int ntfLimitationOriginator = responseData.getOneByteValue(8); + int ntfLimitationTime = responseData.getOneByteValue(9); + logger.trace("setResponse(): nodeId={}.", ntfNodeID); + logger.trace("setResponse(): ntfParameterID={}.", ntfParameterID); + logger.trace("setResponse(): ntfMinValue={}.", ntfMinValue); + logger.trace("setResponse(): ntfMaxValue={}.", ntfMaxValue); + logger.trace("setResponse(): ntfLimitationOriginator={}.", ntfLimitationOriginator); + logger.trace("setResponse(): ntfLimitationTime={}.", ntfLimitationTime); + + if (!KLF200Response.check4matchingSessionID(logger, ntfSessionID, reqSessionID)) { + finished = true; + break; + } + success = true; + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + finished = true; + } + break; + + case GW_COMMAND_RUN_STATUS_NTF: + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 13)) { + finished = true; + break; + } + ntfSessionID = responseData.getTwoByteValue(0); + int ntfStatusiD = responseData.getOneByteValue(2); + int ntfIndex = responseData.getOneByteValue(3); + int ntfNodeParameter = responseData.getOneByteValue(4); + int ntfParameterValue = responseData.getTwoByteValue(5); + int ntfRunStatus = responseData.getOneByteValue(7); + int ntfStatusReply = responseData.getOneByteValue(8); + int ntfInformationCode = responseData.getFourByteValue(9); + // Extracting information items + logger.trace("setResponse(): ntfSessionID={} (requested {}).", ntfSessionID, reqSessionID); + logger.trace("setResponse(): ntfStatusiD={}.", ntfStatusiD); + logger.trace("setResponse(): ntfIndex={}.", ntfIndex); + logger.trace("setResponse(): ntfNodeParameter={}.", ntfNodeParameter); + logger.trace("setResponse(): ntfParameterValue={}.", ntfParameterValue); + logger.trace("setResponse(): ntfRunStatus={}.", ntfRunStatus); + logger.trace("setResponse(): ntfStatusReply={}.", ntfStatusReply); + logger.trace("setResponse(): ntfInformationCode={}.", ntfInformationCode); + + if (!KLF200Response.check4matchingSessionID(logger, ntfSessionID, reqSessionID)) { + finished = true; + } + switch (ntfRunStatus) { + case 0: + logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_COMPLETED."); + success = true; + break; + case 1: + logger.info("setResponse(): returned ntfRunStatus: EXECUTION_FAILED."); + finished = true; + break; + case 2: + logger.debug("setResponse(): returned ntfRunStatus: EXECUTION_ACTIVE."); + break; + default: + logger.warn("setResponse(): returned ntfRunStatus={} (not defined).", ntfRunStatus); + finished = true; + break; + } + if (!isSequentialEnforced) { + logger.trace( + "setResponse(): skipping wait for more packets as sequential processing is not enforced."); + success = true; + finished = true; + } + break; + + case GW_SESSION_FINISHED_NTF: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 2)) { + break; + } + int finishedNtfSessionID = responseData.getTwoByteValue(0); + if (!KLF200Response.check4matchingSessionID(logger, finishedNtfSessionID, reqSessionID)) { + break; + } + logger.debug("setResponse(): finishedNtfSessionID={}.", finishedNtfSessionID); + success = true; + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link SetProductLimitation} + */ + + @Override + public void setActuatorIdAndMinimumLimitation(int nodeId, int limitation) { + logger.trace("setActuatorIdAndLimitationTypeAndLimitation({},{}) called.", nodeId, limitation); + reqIndexArray01 = nodeId; + reqLimitationValueMin = limitation; + reqLimitationValueMax = VeluxProductPosition.VPP_VELUX_IGNORE; + return; + } + + @Override + public void setActuatorIdAndMaximumLimitation(int nodeId, int limitation) { + logger.trace("setActuatorIdAndLimitationTypeAndLimitation({},{}) called.", nodeId, limitation); + reqIndexArray01 = nodeId; + reqLimitationValueMin = VeluxProductPosition.VPP_VELUX_IGNORE; + reqLimitationValueMax = limitation; + return; + } + + public void setActuatorIdAndResetLimitation(int nodeId) { + logger.trace("setActuatorIdAndResetLimitation({}) called.", nodeId); + reqIndexArray01 = nodeId; + reqLimitationValueMin = VeluxProductPosition.VPP_VELUX_IGNORE; + reqLimitationValueMax = VeluxProductPosition.VPP_VELUX_IGNORE; + return; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetSceneVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetSceneVelocity.java new file mode 100644 index 0000000000000..9c0964f9c722e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SCsetSceneVelocity.java @@ -0,0 +1,175 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.SetSceneVelocity; +import org.openhab.binding.velux.internal.bridge.slip.utils.KLF200Response; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.things.VeluxProductVelocity; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Modify Velocity of an Actuator + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 3rd level class it defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the interface {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeCommunicationProtocol + * SlipBridgeCommunicationProtocol}. + *

+ * Methods in addition to the mentioned interface: + *

    + *
  • {@link #setMode} to define the new silence mode of the intended actuator.
  • + *
+ * + * @see SetSceneVelocity + * @see SlipBridgeCommunicationProtocol + * + * @author Guenther Schreiner - Initial contribution. + */ +// ToDo: THIS MESSAGE EXCHANGE IS AN UNDOCUMENTED FEATURE. Check the updated Velux doc against this implementation. +@NonNullByDefault +class SCsetSceneVelocity extends SetSceneVelocity implements SlipBridgeCommunicationProtocol { + private final Logger logger = LoggerFactory.getLogger(SCsetSceneVelocity.class); + + private static final String DESCRIPTION = "Modify Velocity of an Actuator"; + private static final Command COMMAND = Command.GW_SET_NODE_VELOCITY_REQ; + + /* + * =========================================================== + * Message Content Parameters + */ + + private int reqNodeID = -1; + private int reqNodeVelocity = -1; + + /* + * =========================================================== + * Message Objects + */ + + private byte[] requestData = new byte[0]; + + /* + * =========================================================== + * Result Objects + */ + + private boolean success = false; + private boolean finished = false; + + /* + * =========================================================== + * Methods required for interface {@link BridgeCommunicationProtocol}. + */ + + @Override + public String name() { + return DESCRIPTION; + } + + @Override + public CommandNumber getRequestCommand() { + success = false; + finished = false; + logger.debug("getRequestCommand() returns {}.", COMMAND.getCommand()); + return COMMAND.getCommand(); + } + + @Override + public byte[] getRequestDataAsArrayOfBytes() { + Packet request = new Packet(new byte[2]); + request.setOneByteValue(0, reqNodeID); + request.setOneByteValue(1, reqNodeVelocity); + requestData = request.toByteArray(); + logger.trace("getRequestDataAsArrayOfBytes() data is {}.", new Packet(requestData).toString()); + return requestData; + } + + @Override + public void setResponse(short responseCommand, byte[] thisResponseData, boolean isSequentialEnforced) { + KLF200Response.introLogging(logger, responseCommand, thisResponseData); + success = false; + finished = false; + Packet responseData = new Packet(thisResponseData); + switch (Command.get(responseCommand)) { + case GW_SET_NODE_VELOCITY_CFM: + finished = true; + if (!KLF200Response.isLengthValid(logger, responseCommand, thisResponseData, 3)) { + break; + } + int cfmStatus = responseData.getOneByteValue(0); + switch (cfmStatus) { + case 0: + logger.trace("setResponse(): returned status: Error - Wink is rejected."); + break; + case 1: + logger.trace("setResponse(): returned status: OK – Wink is accepted."); + success = true; + break; + default: + logger.warn("setResponse({}): returned status={} (Reserved/unknown).", + Command.get(responseCommand).toString(), cfmStatus); + break; + } + break; + + default: + KLF200Response.errorLogging(logger, responseCommand); + finished = true; + } + KLF200Response.outroLogging(logger, success, finished); + } + + @Override + public boolean isCommunicationFinished() { + return finished; + } + + @Override + public boolean isCommunicationSuccessful() { + return success; + } + + /* + * =========================================================== + * Methods in addition to the interface {@link BridgeCommunicationProtocol} + * and the abstract class {@link RunProductIdentification} + */ + + /** + * Constructor Addon Method. + *

+ * Passes the intended Actuator Identifier towards this class for building the request lateron. + * + * @param actuatorId as type int describing the scene to be processed. + * @param silent as type boolean describing the silence mode of this node. + * @return this of type {@link SCsetSceneVelocity} as class itself. + */ + @Override + public SCsetSceneVelocity setMode(int actuatorId, boolean silent) { + logger.trace("setProductId({},{}) called.", actuatorId, silent); + this.reqNodeID = actuatorId; + this.reqNodeVelocity = silent ? VeluxProductVelocity.SILENT.getVelocity() + : VeluxProductVelocity.FAST.getVelocity(); + return this; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeAPI.java new file mode 100644 index 0000000000000..c2e6d652bd1fc --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeAPI.java @@ -0,0 +1,214 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.GetDeviceStatus; +import org.openhab.binding.velux.internal.bridge.common.GetFirmware; +import org.openhab.binding.velux.internal.bridge.common.GetHouseStatus; +import org.openhab.binding.velux.internal.bridge.common.GetLANConfig; +import org.openhab.binding.velux.internal.bridge.common.GetProduct; +import org.openhab.binding.velux.internal.bridge.common.GetProductLimitation; +import org.openhab.binding.velux.internal.bridge.common.GetProducts; +import org.openhab.binding.velux.internal.bridge.common.GetScenes; +import org.openhab.binding.velux.internal.bridge.common.GetWLANConfig; +import org.openhab.binding.velux.internal.bridge.common.Login; +import org.openhab.binding.velux.internal.bridge.common.Logout; +import org.openhab.binding.velux.internal.bridge.common.RunProductCommand; +import org.openhab.binding.velux.internal.bridge.common.RunProductDiscovery; +import org.openhab.binding.velux.internal.bridge.common.RunProductIdentification; +import org.openhab.binding.velux.internal.bridge.common.RunProductSearch; +import org.openhab.binding.velux.internal.bridge.common.RunScene; +import org.openhab.binding.velux.internal.bridge.common.SetHouseStatusMonitor; +import org.openhab.binding.velux.internal.bridge.common.SetProductLimitation; +import org.openhab.binding.velux.internal.bridge.common.SetSceneVelocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * SLIP-based 3rd Level I/O interface towards the Velux bridge. + *

+ * It provides the one-and-only protocol specific 1st-level communication class. + * Additionally it provides all methods for different gateway interactions. + *

+ * The following class access methods exist: + *

    + *
  • {@link SlipBridgeAPI#getDeviceStatus} for retrieving the bridge state (i.e. IDLE, BUSY, a.s.o),
  • + *
  • {@link SlipBridgeAPI#getFirmware} for retrieving the firmware version of the bridge,
  • + *
  • {@link SlipBridgeAPI#getHouseStatus} for retrieving the information about device state changes recognized by the + * bridge,
  • + *
  • {@link SlipBridgeAPI#getLANConfig} for retrieving the complete LAN information of the bridge,
  • + *
  • {@link SlipBridgeAPI#getProduct} for retrieving the any information about a device behind the bridge,
  • + *
  • {@link SlipBridgeAPI#getProductLimitation} for retrieving the limitation information about a device behind the + * bridge,
  • + *
  • {@link SlipBridgeAPI#getProducts} for retrieving the any information for all devices behind the bridge,
  • + *
  • {@link SlipBridgeAPI#getScenes} for retrieving the any information for all scenes defined on the bridge,
  • + *
  • {@link SlipBridgeAPI#getWLANConfig} for retrieving the complete WLAN information of the bridge,
  • + *
  • {@link SlipBridgeAPI#login} for establishing a trusted connectivity by authentication,
  • + *
  • {@link SlipBridgeAPI#logout} for tearing down the trusted connectivity by deauthentication,
  • + *
  • {@link SlipBridgeAPI#runProductCommand} for manipulation of a device behind the bridge (i.e. instructing to + * modify a position),
  • + *
  • {@link SlipBridgeAPI#runProductDiscovery} for activation of learning mode of the bridge to discovery new + * products,
  • + *
  • {@link SlipBridgeAPI#runProductIdentification} for human-oriented identification a device behind the bridge (i.e. + * by winking or switching on-and-off),
  • + *
  • {@link SlipBridgeAPI#runProductSearch} for searching for lost products on the bridge,
  • + *
  • {@link SlipBridgeAPI#runScene} for manipulation of a set of devices behind the bridge which are tied together as + * scene,
  • + *
  • {@link SlipBridgeAPI#setHouseStatusMonitor} for activation or deactivation of the house monitoring mode to be + * informed about device state changes recognized by the bridge,
  • + *
  • {@link SlipBridgeAPI#setSceneVelocity} for changes the velocity of a scene defined on the bridge (i.e. silent or + * fast mode).
  • + *
+ *

+ * As most derived class of the several inheritance levels it defines an + * interfacing method which returns the SLIP-protocol-specific communication for gateway interaction. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SlipBridgeAPI implements BridgeAPI { + private final Logger logger = LoggerFactory.getLogger(SlipBridgeAPI.class); + + private static final GetDeviceStatus GETDEVICESTATUS = new SCgetDeviceStatus(); + private static final GetFirmware GETFIRMWARE = new SCgetFirmware(); + private static final GetHouseStatus GETHOUSESTATUS = new SCgetHouseStatus(); + private static final GetLANConfig GETLANCONFIG = new SCgetLANConfig(); + private static final GetProduct GETPRODUCT = new SCgetProduct(); + private static final GetProductLimitation GETPRODUCTLIMITATION = new SCgetLimitation(); + private static final GetProducts GETPRODUCTS = new SCgetProducts(); + private static final GetScenes GETSCENES = new SCgetScenes(); + private static final GetWLANConfig GETWLANCONFIG = new SCgetWLANConfig(); + private static final Login LOGIN = new SClogin(); + private static final Logout LOGOUT = new SClogout(); + private static final RunProductCommand RUNPRODUCTCOMMAND = new SCrunProductCommand(); + private static final RunProductDiscovery RUNPRODUCTDISCOVERY = new SCrunProductDiscovery(); + private static final RunProductIdentification RUNPRODUCTIDENTIFICATION = new SCrunProductIdentification(); + private static final RunProductSearch RUNPRODUCTSEARCH = new SCrunProductSearch(); + private static final RunScene RUNSCENE = new SCrunScene(); + private static final SetHouseStatusMonitor SETHOUSESTATUSMONITOR = new SCsetHouseStatusMonitor(); + private static final SetProductLimitation SETPRODUCTLIMITATION = new SCsetLimitation(); + private static final SetSceneVelocity SETSCENEVELOCITY = new SCsetSceneVelocity(); + + /** + * Constructor. + *

+ * Inherits the initialization of the binding-wide instance for dealing for common information and + * initializes the handler {@link SlipVeluxBridge#bridgeAPI} + * to pass the interface methods. + * + * @param bridgeInstance refers to the binding-wide instance for dealing for common informations. + */ + SlipBridgeAPI(VeluxBridgeInstance bridgeInstance) { + logger.trace("SlipBridgeAPI(constructor) called."); + } + + @Override + public GetDeviceStatus getDeviceStatus() { + return GETDEVICESTATUS; + } + + @Override + public GetFirmware getFirmware() { + return GETFIRMWARE; + } + + @Override + public @Nullable GetHouseStatus getHouseStatus() { + return GETHOUSESTATUS; + } + + @Override + public GetLANConfig getLANConfig() { + return GETLANCONFIG; + } + + @Override + public @Nullable GetProduct getProduct() { + return GETPRODUCT; + } + + @Override + public @Nullable GetProductLimitation getProductLimitation() { + return GETPRODUCTLIMITATION; + } + + @Override + public @Nullable SetProductLimitation setProductLimitation() { + return SETPRODUCTLIMITATION; + } + + @Override + public GetProducts getProducts() { + return GETPRODUCTS; + } + + @Override + public GetScenes getScenes() { + return GETSCENES; + } + + @Override + public GetWLANConfig getWLANConfig() { + return GETWLANCONFIG; + } + + @Override + public Login login() { + return LOGIN; + } + + @Override + public Logout logout() { + return LOGOUT; + } + + @Override + public @Nullable RunProductCommand runProductCommand() { + return RUNPRODUCTCOMMAND; + } + + @Override + public RunProductDiscovery runProductDiscovery() { + return RUNPRODUCTDISCOVERY; + } + + @Override + public RunProductIdentification runProductIdentification() { + return RUNPRODUCTIDENTIFICATION; + } + + @Override + public RunProductSearch runProductSearch() { + return RUNPRODUCTSEARCH; + } + + @Override + public RunScene runScene() { + return RUNSCENE; + } + + @Override + public @Nullable SetHouseStatusMonitor setHouseStatusMonitor() { + return SETHOUSESTATUSMONITOR; + } + + @Override + public SetSceneVelocity setSceneVelocity() { + return SETSCENEVELOCITY; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeCommunicationProtocol.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeCommunicationProtocol.java new file mode 100644 index 0000000000000..265cc719eb223 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipBridgeCommunicationProtocol.java @@ -0,0 +1,85 @@ +/** + * 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.velux.internal.bridge.slip; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; + +/** + * Protocol specific bridge communication supported by the Velux bridge: + * Authentication + *

+ * Common Message semantic: Communication with the bridge and (optionally) storing returned information within the class + * itself. + *

+ * As 2nd level interface it defines the methods to help in sending a query and + * processing the received answer. + *

+ * (Additional) Methods in this interface for the appropriate interaction: + *

    + *
  • {@link getRequestCommand} to return the intended command to be sent.
  • + *
  • {@link getRequestDataAsArrayOfBytes} to return the intended data part to be sent.
  • + *
  • {@link setResponse} to store the response already separated into response command and data part.
  • + *
  • {@link isCommunicationFinished} to signal the completeness of the interaction (only available + * after storing the response).
  • + *
+ *

+ * Other mandatory methods are inherited from {@link BridgeCommunicationProtocol}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +interface SlipBridgeCommunicationProtocol extends BridgeCommunicationProtocol { + + /** + * Provides an empty array of bytes. + * + */ + public static final byte[] EMPTYDATA = new byte[0]; + + /** + * Returning the command part of the request object for further SLIP serialization. + * + * @return commandNumber + * is of type {@link CommandNumber}. + */ + public CommandNumber getRequestCommand(); + + /** + * Returning the data part of the request object for further SLIP serialization. + * + * @return dataAsArrayOfBytes + * is an Array of byte. + */ + public byte[] getRequestDataAsArrayOfBytes(); + + /** + * Storing the command and the data part of the response object for further checks. + * + * @param thisResponseCommand of type short: command part of the response packet. + * @param thisResponseData of type Array of bytes: data part of response packet to be processed. + * @param isSequentialEnforced of type boolean: enforces the strict handshake sequence even for long duration + * interactions. + */ + public void setResponse(short thisResponseCommand, byte[] thisResponseData, boolean isSequentialEnforced); + + /** + * Returning the communication status included within the response message.. + * + * @return isFinished + * is a boolean signaling the end of this transaction. + */ + public boolean isCommunicationFinished(); + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipVeluxBridge.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipVeluxBridge.java new file mode 100644 index 0000000000000..55db3eb064627 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/SlipVeluxBridge.java @@ -0,0 +1,385 @@ +/** + * 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.velux.internal.bridge.slip; + +import java.text.ParseException; +import java.util.TreeSet; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.bridge.VeluxBridge; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.bridge.slip.io.Connection; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.openhab.binding.velux.internal.bridge.slip.utils.SlipEncoding; +import org.openhab.binding.velux.internal.bridge.slip.utils.SlipRFC1055; +import org.openhab.binding.velux.internal.development.Threads; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * SLIP-based 2nd Level I/O interface towards the Velux bridge. + *

+ * It provides methods for pre- and postcommunication + * as well as a common method for the real communication. + *

+ * In addition to the generic {@link VeluxBridge} methods, i.e. + *

    + *
  • {@link VeluxBridge#bridgeLogin} for pre-communication,
  • + *
  • {@link VeluxBridge#bridgeLogout} for post-communication,
  • + *
  • {@link VeluxBridge#bridgeCommunicate} as method for the common communication,
  • + *
  • {@link VeluxBridge#bridgeAPI} as interfacing method to all interaction prototypes,
  • + *
+ * the following class access methods provides the protocol-specific implementation: + *
    + *
  • {@link #bridgeDirectCommunicate} for SLIP-based communication.
  • + *
  • {@link #bridgeAPI} for return all defined protocol-specific implementations which are provided by + * {@link org.openhab.binding.velux.internal.bridge.slip.SlipBridgeAPI SlipBridgeAPI}.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class SlipVeluxBridge extends VeluxBridge { + private final Logger logger = LoggerFactory.getLogger(SlipVeluxBridge.class); + + /* + * *************************** + * ***** Private Objects ***** + */ + + /** + * Timeout for sequence of one request with (optional multiple) responses. + *

+ * This can only happen if there is an unexpected (undocumented) occurrence of events + * which has to be discussed along the Velux API documentation. + */ + static final long COMMUNICATION_TIMEOUT_MSECS = 60000L; + /** + * Wait interval within sequence after the request and no immediate response. + *

+ * This can happen if the bridge is busy due to a specific command to query and collect information from other + * devices via the io-homecontrol protocol. + */ + static final long COMMUNICATION_RETRY_MSECS = 5000L; + + /* + * *************************** + * ***** Private Objects ***** + */ + + private final byte[] emptyPacket = new byte[0]; + private Connection connection = new Connection(); + + /** + * Handler passing the interface methods to other classes. + * Can be accessed via method {@link org.openhab.binding.velux.internal.bridge.common.BridgeAPI BridgeAPI}. + */ + private BridgeAPI bridgeAPI; + + /** + * Constructor. + *

+ * Inherits the initialization of the binding-wide instance for dealing for common informations and + * initializes the Velux bridge connection settings. + * + * @param bridgeInstance refers to the binding-wide instance for dealing for common informations. + */ + public SlipVeluxBridge(VeluxBridgeInstance bridgeInstance) { + super(bridgeInstance); + logger.trace("SlipVeluxBridge(constructor) called."); + bridgeAPI = new SlipBridgeAPI(bridgeInstance); + supportedProtocols = new TreeSet(); + supportedProtocols.add("slip"); + logger.trace("SlipVeluxBridge(constructor) done."); + } + + /** + * Destructor. + *

+ * De-initializes the binding-wide instance. + */ + @Override + public void shutdown() { + logger.trace("shutdown() called."); + connection.resetConnection(); + logger.trace("shutdown() done."); + } + + /** + * Provides information about the base-level communication method and + * any kind of available gateway interactions. + *

+ * Note: the implementation within this class {@link SlipVeluxBridge} as inherited from {@link VeluxBridge} + * will return the protocol-specific class implementations. + *

+ * The information will be initialized by the corresponding API class {@link SlipBridgeAPI}. + * + * @return bridgeAPI of type {@link BridgeAPI} contains all possible methods. + */ + @Override + public BridgeAPI bridgeAPI() { + logger.trace("bridgeAPI() called."); + return bridgeAPI; + } + + /** + * Method as implementation of abstract superclass method. + *

+ * Initializes a client/server communication towards Velux veluxBridge + * based on the Basic I/O interface {@link Connection#io} and parameters + * passed as arguments (see below). + * + * @param communication Structure of interface type {@link SlipBridgeCommunicationProtocol} describing the intended + * communication, that is request and response interactions as well as appropriate URL definition. + * @param useAuthentication boolean flag to decide whether to use authenticated communication. + * @return success of type boolean which signals the success of the communication. + * + */ + @Override + protected boolean bridgeDirectCommunicate(BridgeCommunicationProtocol communication, boolean useAuthentication) { + logger.trace("bridgeDirectCommunicate(BCP: {},{}authenticated) called.", communication.name(), + useAuthentication ? "" : "un"); + return bridgeDirectCommunicate((SlipBridgeCommunicationProtocol) communication, useAuthentication); + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last (potentially faulty) communication. + * + * @return timestamp in milliseconds. + */ + @Override + public long lastCommunication() { + return connection.lastCommunication(); + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last successful communication. + * + * @return timestamp in milliseconds. + */ + @Override + public long lastSuccessfulCommunication() { + return connection.lastSuccessfulCommunication(); + } + + /** + * Initializes a client/server communication towards Velux veluxBridge + * based on the Basic I/O interface {@link Connection#io} and parameters + * passed as arguments (see below). + * + * @param communication Structure of interface type {@link SlipBridgeCommunicationProtocol} describing the + * intended communication, that is request and response interactions as well as appropriate URL + * definition. + * @param useAuthentication boolean flag to decide whether to use authenticated communication. + * @return success of type boolean which signals the success of the communication. + */ + private synchronized boolean bridgeDirectCommunicate(SlipBridgeCommunicationProtocol communication, + boolean useAuthentication) { + logger.trace("bridgeDirectCommunicate({},{}authenticated) called.", communication.name(), + useAuthentication ? "" : "un"); + + assert this.bridgeInstance.veluxBridgeConfiguration().protocol.contentEquals("slip"); + + long communicationStartInMSecs = System.currentTimeMillis(); + + boolean isSequentialEnforced = this.bridgeInstance.veluxBridgeConfiguration().isSequentialEnforced; + boolean isProtocolTraceEnabled = this.bridgeInstance.veluxBridgeConfiguration().isProtocolTraceEnabled; + + // From parameters + short command = communication.getRequestCommand().toShort(); + byte[] data = communication.getRequestDataAsArrayOfBytes(); + // For further use at different logging statements + String commandString = Command.get(command).toString(); + + if (isProtocolTraceEnabled) { + Threads.findDeadlocked(); + } + + logger.debug("bridgeDirectCommunicate({},{}authenticated) initiated by {}.", commandString, + useAuthentication ? "" : "un", Thread.currentThread()); + boolean success = false; + + communication: do { + if (communicationStartInMSecs + COMMUNICATION_TIMEOUT_MSECS < System.currentTimeMillis()) { + logger.warn( + "{} bridgeDirectCommunicate({}): communication handshake failed (unexpected sequence of requests/responses).", + VeluxBindingConstants.BINDING_VALUES_SEPARATOR, communication.name()); + break; + } + + // Special handling + if (Command.get(command) == Command.GW_OPENHAB_CLOSE) { + logger.trace("bridgeDirectCommunicate(): special command: shutting down connection."); + connection.resetConnection(); + success = true; + continue; + } + + // Normal processing + logger.trace("bridgeDirectCommunicate(): working on request {} with {} bytes of data.", commandString, + data.length); + byte[] sendBytes = emptyPacket; + if (Command.get(command) == Command.GW_OPENHAB_RECEIVEONLY) { + logger.trace( + "bridgeDirectCommunicate(): special command: determine whether there is any message waiting."); + logger.trace("bridgeDirectCommunicate(): check for a waiting message."); + if (!connection.isMessageAvailable()) { + logger.trace("bridgeDirectCommunicate(): no message waiting, aborting."); + break communication; + } + logger.trace("bridgeDirectCommunicate(): there is a message waiting."); + } else { + SlipEncoding t = new SlipEncoding(command, data); + if (!t.isValid()) { + logger.warn("bridgeDirectCommunicate(): SlipEncoding() failed, aborting."); + break; + } + logger.trace("bridgeDirectCommunicate(): transportEncoding={}.", t.toString()); + sendBytes = new SlipRFC1055().encode(t.toMessage()); + } + do { + if (communicationStartInMSecs + COMMUNICATION_TIMEOUT_MSECS < System.currentTimeMillis()) { + logger.warn("bridgeDirectCommunicate(): receive takes too long. Please report to maintainer."); + break communication; + } + byte[] receivedPacket; + try { + if (sendBytes.length > 0) { + logger.trace("bridgeDirectCommunicate(): sending {} bytes.", sendBytes.length); + if (isProtocolTraceEnabled) { + logger.info("Sending command {}.", commandString); + } + } else { + logger.trace("bridgeDirectCommunicate(): initiating receive-only."); + } + // (Optionally) Send and receive packet. + receivedPacket = connection.io(this.bridgeInstance, sendBytes); + // Once being sent, it should never be sent again + sendBytes = emptyPacket; + } catch (Exception e) { + logger.warn("bridgeDirectCommunicate(): connection.io returns {}", e.getMessage()); + break communication; + } + logger.trace("bridgeDirectCommunicate(): received packet {}.", new Packet(receivedPacket).toString()); + byte[] response; + try { + response = new SlipRFC1055().decode(receivedPacket); + } catch (ParseException e) { + logger.warn("bridgeDirectCommunicate(): method SlipRFC1055() raised a decoding error: {}.", + e.getMessage()); + break communication; + } + SlipEncoding tr = new SlipEncoding(response); + if (!tr.isValid()) { + logger.warn("bridgeDirectCommunicate(): method SlipEncoding() raised a decoding error."); + break communication; + } + short responseCommand = tr.getCommand(); + byte[] responseData = tr.getData(); + logger.debug("bridgeDirectCommunicate(): working on response {} with {} bytes of data.", + Command.get(responseCommand).toString(), responseData.length); + if (isProtocolTraceEnabled) { + logger.info("Received answer {}.", Command.get(responseCommand).toString()); + } + // Handle some common (unexpected) answers + switch (Command.get(responseCommand)) { + case GW_NODE_INFORMATION_CHANGED_NTF: + logger.trace("bridgeDirectCommunicate(): received GW_NODE_INFORMATION_CHANGED_NTF."); + logger.trace("bridgeDirectCommunicate(): continue with receiving."); + continue; + case GW_NODE_STATE_POSITION_CHANGED_NTF: + logger.trace( + "bridgeDirectCommunicate(): received GW_NODE_STATE_POSITION_CHANGED_NTF, special processing of this packet."); + SCgetHouseStatus receiver = new SCgetHouseStatus(); + receiver.setResponse(responseCommand, responseData, isSequentialEnforced); + if (receiver.isCommunicationSuccessful()) { + logger.trace("bridgeDirectCommunicate(): existingProducts().update() called."); + bridgeInstance.existingProducts().update(new ProductBridgeIndex(receiver.getNtfNodeID()), + receiver.getNtfState(), receiver.getNtfCurrentPosition(), receiver.getNtfTarget()); + } + logger.trace("bridgeDirectCommunicate(): continue with receiving."); + continue; + case GW_ERROR_NTF: + switch (responseData[0]) { + case 0: + logger.warn( + "bridgeDirectCommunicate(): received GW_ERROR_NTF on {} (Not further defined error), aborting.", + commandString); + break communication; + case 1: + logger.warn( + "bridgeDirectCommunicate(): received GW_ERROR_NTF (Unknown Command or command is not accepted at this state) on {}, aborting.", + commandString); + break communication; + case 2: + logger.warn( + "bridgeDirectCommunicate(): received GW_ERROR_NTF (ERROR on Frame Structure) on {}, aborting.", + commandString); + break communication; + case 7: + logger.trace( + "bridgeDirectCommunicate(): received GW_ERROR_NTF (Busy. Try again later) on {}, retrying.", + commandString); + sendBytes = emptyPacket; + continue; + case 8: + logger.warn( + "bridgeDirectCommunicate(): received GW_ERROR_NTF (Bad system table index) on {}, aborting.", + commandString); + break communication; + case 12: + logger.warn( + "bridgeDirectCommunicate(): received GW_ERROR_NTF (Not authenticated) on {}, aborting.", + commandString); + resetAuthentication(); + break communication; + default: + logger.warn("bridgeDirectCommunicate(): received GW_ERROR_NTF ({}) on {}, aborting.", + responseData[0], commandString); + break communication; + } + case GW_ACTIVATION_LOG_UPDATED_NTF: + logger.info("bridgeDirectCommunicate(): received GW_ACTIVATION_LOG_UPDATED_NTF."); + logger.trace("bridgeDirectCommunicate(): continue with receiving."); + continue; + + case GW_COMMAND_RUN_STATUS_NTF: + case GW_COMMAND_REMAINING_TIME_NTF: + case GW_SESSION_FINISHED_NTF: + if (!isSequentialEnforced) { + logger.trace( + "bridgeDirectCommunicate(): response ignored due to activated parallelism, continue with receiving."); + continue; + } + + default: + } + logger.trace("bridgeDirectCommunicate(): passes back command {} and data {}.", + new CommandNumber(responseCommand).toString(), new Packet(responseData).toString()); + communication.setResponse(responseCommand, responseData, isSequentialEnforced); + } while (!communication.isCommunicationFinished()); + success = communication.isCommunicationSuccessful(); + } while (false); // communication + logger.debug("bridgeDirectCommunicate({}) returns {}.", commandString, success ? "success" : "failure"); + return success; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/Connection.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/Connection.java new file mode 100644 index 0000000000000..634c5cafb67c4 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/Connection.java @@ -0,0 +1,241 @@ +/** + * 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.velux.internal.bridge.slip.io; + +import java.io.IOException; +import java.net.ConnectException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.internal.bridge.slip.utils.Packet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 2nd Level I/O interface towards the Velux bridge. + * It provides methods for a pure client/server communication. + *

+ * The following class access methods exist: + *

    + *
  • {@link Connection#io} for a complete pair of request and response messages,
  • + *
  • {@link Connection#isAlive} to check the presence of a connection,
  • + *
  • {@link Connection#isMessageAvailable} to check the presence of an incoming message,
  • + *
  • {@link Connection#lastSuccessfulCommunication} returns the timestamp of the last successful communication,
  • + *
  • {@link Connection#lastCommunication} returns the timestamp of the last communication,
  • + *
  • {@link Connection#resetConnection} for resetting the current connection.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class Connection { + private final Logger logger = LoggerFactory.getLogger(Connection.class); + + /* + * *************************** + * ***** Private Objects ***** + */ + + /** + * Timestamp of last successful communication in milliseconds. + */ + private long lastSuccessfulCommunicationInMSecs = 0; + /** + * Timestamp of last communication in milliseconds. + */ + private long lastCommunicationInMSecs = 0; + /** + * SSL socket for communication. + */ + private SSLconnection connectivity = SSLconnection.UNKNOWN; + + /* + * ************************** + * ***** Public Methods ***** + */ + + /** + * Base level communication with the SlipVeluxBridg. + * + * @param bridgeInstance describing the Service Access Point location i.e. hostname and TCP port. + * @param request as Array of bytes representing the structure of the message to be send. + * @return response of type Array of byte containing all received informations. + * @throws java.net.ConnectException in case of unrecoverable communication failures. + * @throws java.io.IOException in case of continuous communication I/O failures. + */ + public synchronized byte[] io(VeluxBridgeInstance bridgeInstance, byte[] request) + throws ConnectException, IOException { + logger.trace("io() called."); + + lastCommunicationInMSecs = System.currentTimeMillis(); + + /** Local handles */ + int retryCount = 0; + IOException lastIOE = new IOException("Unexpected I/O exception."); + + do { + try { + if (!connectivity.isReady()) { + try { + // From configuration + String host = bridgeInstance.veluxBridgeConfiguration().ipAddress; + int port = bridgeInstance.veluxBridgeConfiguration().tcpPort; + int timeoutMsecs = bridgeInstance.veluxBridgeConfiguration().timeoutMsecs; + + logger.trace("io(): connecting to {}:{}.", host, port); + connectivity = new SSLconnection(host, port); + connectivity.setTimeout(timeoutMsecs); + } catch (ConnectException ce) { + throw new ConnectException(String + .format("raised a non-recoverable error during connection setup: %s", ce.getMessage())); + } catch (IOException e) { + logger.warn("io(): raised an error during connection setup: {}.", e.getMessage()); + lastIOE = new IOException(String.format("error during connection setup: %s.", e.getMessage())); + continue; + } + } + if (request.length > 0) { + try { + if (logger.isTraceEnabled()) { + logger.trace("io(): sending packet with {} bytes: {}", request.length, new Packet(request)); + } else { + logger.debug("io(): sending packet of size {}.", request.length); + } + if (connectivity.isReady()) { + connectivity.send(request); + } + } catch (IOException e) { + logger.info("io(): raised an error during sending: {}.", e.getMessage()); + break; + } + + // Give the bridge some time to breathe + if (bridgeInstance.veluxBridgeConfiguration().timeoutMsecs > 0) { + logger.trace("io(): wait time {} msecs.", + bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + try { + Thread.sleep(bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + } catch (InterruptedException ie) { + logger.trace("io() wait interrupted."); + } + } + } + byte[] packet = new byte[0]; + logger.trace("io(): receiving bytes."); + if (connectivity.isReady()) { + packet = connectivity.receive(); + } + if (logger.isTraceEnabled()) { + logger.trace("io(): received packet with {} bytes: {}", packet.length, new Packet(packet)); + } else { + logger.debug("io(): received packet with {} bytes.", packet.length); + } + lastSuccessfulCommunicationInMSecs = System.currentTimeMillis(); + lastCommunicationInMSecs = lastSuccessfulCommunicationInMSecs; + logger.trace("io() finished."); + return packet; + } catch (IOException ioe) { + logger.info("io(): Exception occurred during I/O: {}.", ioe.getMessage()); + lastIOE = ioe; + // Error Retries with Exponential Backoff + long waitTime = ((long) Math.pow(2, retryCount) + * bridgeInstance.veluxBridgeConfiguration().timeoutMsecs); + logger.trace("io(): wait time {} msecs.", waitTime); + try { + Thread.sleep(waitTime); + } catch (InterruptedException ie) { + logger.trace("io(): wait interrupted."); + } + } + } while (retryCount++ < bridgeInstance.veluxBridgeConfiguration().retries); + if (retryCount >= bridgeInstance.veluxBridgeConfiguration().retries) { + logger.info("io(): socket I/O failed {} times.", bridgeInstance.veluxBridgeConfiguration().retries); + } + logger.trace("io(): shutting down connection."); + if (connectivity.isReady()) { + connectivity.close(); + } + logger.trace("io() finishes with failure by throwing exception."); + throw lastIOE; + } + + /** + * Returns the status of the current connection. + * + * @return state as boolean. + */ + public boolean isAlive() { + logger.trace("isAlive(): called."); + return connectivity.isReady(); + } + + /** + * Returns the availability of an incoming message. + * + * @return state as boolean. + */ + public synchronized boolean isMessageAvailable() { + logger.trace("isMessageAvailable(): called."); + try { + if ((connectivity.isReady()) && (connectivity.available())) { + logger.trace("isMessageAvailable(): there is a message waiting."); + return true; + } + } catch (IOException e) { + logger.trace("isMessageAvailable(): lost connection due to {}.", e.getMessage()); + resetConnection(); + } + logger.trace("isMessageAvailable(): no message waiting."); + return false; + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last successful communication. + * + * @return timestamp in milliseconds. + */ + public long lastSuccessfulCommunication() { + return lastSuccessfulCommunicationInMSecs; + } + + /** + * Returns the timestamp in milliseconds since Unix epoch + * of last communication. + * + * @return timestamp in milliseconds. + */ + public long lastCommunication() { + return lastCommunicationInMSecs; + } + + /** + * Resets an open connectivity by closing the socket and resetting the authentication information. + */ + public synchronized void resetConnection() { + logger.trace("resetConnection() called."); + if (connectivity.isReady()) { + logger.trace("resetConnection(): shutting down connection."); + try { + if (connectivity.isReady()) { + connectivity.close(); + } + } catch (IOException e) { + logger.info("resetConnection(): raised an error during connection close: {}.", e.getMessage()); + } + logger.trace("resetConnection(): clearing authentication token."); + } + logger.trace("resetConnection() done."); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/DataInputStreamWithTimeout.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/DataInputStreamWithTimeout.java new file mode 100644 index 0000000000000..1a2db47e5c31e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/DataInputStreamWithTimeout.java @@ -0,0 +1,137 @@ +/** + * 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.velux.internal.bridge.slip.io; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * This is an extension of {@link java.io.DataInputStream}, which adds timeouts to receive operation. + *

+ * A data input stream lets an application read primitive Java data + * types from an underlying input stream in a machine-independent + * way. An application uses a data output stream to write data that + * can later be read by a data input stream. + *

+ * For an in-depth discussion, see: + * https://stackoverflow.com/questions/804951/is-it-possible-to-read-from-a-inputstream-with-a-timeout + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class DataInputStreamWithTimeout extends DataInputStream { + + /* + * *************************** + * ***** Private Objects ***** + */ + + /** + * Executor for asynchronous read command + */ + ExecutorService executor = Executors.newFixedThreadPool(2); + + /** + * Creates a DataInputStreamWithTimeout that uses the specified + * underlying DataInputStream. + * + * @param in the specified input stream + */ + public DataInputStreamWithTimeout(InputStream in) { + super(in); + } + + /** + * Reads up to len bytes of data from the contained + * input stream into an array of bytes. An attempt is made to read + * as many as len bytes, but a smaller number may be read, + * possibly zero. The number of bytes actually read is returned as an + * integer. + * + *

+ * This method blocks until input data is available, end of file is + * detected, or an exception is thrown until the given timeout. + * + *

+ * If len is zero, then no bytes are read and + * 0 is returned; otherwise, there is an attempt to read at + * least one byte. If no byte is available because the stream is at end of + * file, the value -1 is returned; otherwise, at least one + * byte is read and stored into b. + * + *

+ * The first byte read is stored into element b[off], the + * next one into b[off+1], and so on. The number of bytes read + * is, at most, equal to len. Let k be the number of + * bytes actually read; these bytes will be stored in elements + * b[off] through b[off+k-1], + * leaving elements b[off+k] through + * b[off+len-1] unaffected. + * + *

+ * In every case, elements b[0] through + * b[off] and elements b[off+len] through + * b[b.length-1] are unaffected. + * + * @param b the buffer into which the data is read. + * @param off the start offset in the destination array b + * @param len the maximum number of bytes read. + * @param timeoutMSecs the maximum duration of this read before throwing a TimeoutException. + * @return the total number of bytes read into the buffer, or + * -1 if there is no more data because the end + * of the stream has been reached. + * @exception NullPointerException If b is null. + * @exception IndexOutOfBoundsException If off is negative, + * len is negative, or len is greater than + * b.length - off + * @exception IOException if the first byte cannot be read for any reason + * other than end of file, the stream has been closed and the underlying + * input stream does not support reading after close, or another I/O + * error occurs. Additionally it will occur when the timeout happens. + * @see java.io.DataInputStream#read + */ + public synchronized int read(byte b[], int off, int len, int timeoutMSecs) throws IOException { + // Definition of Method which encapsulates the Read of data + Callable readTask = new Callable() { + @Override + public Integer call() throws IOException { + return in.read(b, off, len); + } + }; + try { + Future future = executor.submit(readTask); + return future.get(timeoutMSecs, TimeUnit.MILLISECONDS); + } catch (RejectedExecutionException e) { + throw new IOException("executor failed", e); + } catch (ExecutionException e) { + throw new IOException("execution failed", e); + } catch (InterruptedException e) { + throw new IOException("read interrupted", e); + } catch (TimeoutException e) { + throw new IOException("read timeout", e); + } + + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/SSLconnection.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/SSLconnection.java new file mode 100644 index 0000000000000..b1c177b1ac3c0 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/SSLconnection.java @@ -0,0 +1,258 @@ +/** + * 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.velux.internal.bridge.slip.io; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.ConnectException; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Transport layer supported by the Velux bridge. + *

+ * SLIP-based 2nd Level I/O interface towards the Velux bridge. + *

+ * It provides methods for pre- and post-communication + * as well as a common method for the real communication. + *

    + *
  • {@link SSLconnection#SSLconnection} for establishing the connection,
  • + *
  • {@link SSLconnection#send} for sending a message to the bridge,
  • + *
  • {@link SSLconnection#available} for observation whether there are bytes available,
  • + *
  • {@link SSLconnection#receive} for receiving a message from the bridge,
  • + *
  • {@link SSLconnection#close} for tearing down the connection.
  • + *
  • {@link SSLconnection#setTimeout} for adapting communication parameters.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +class SSLconnection { + private final Logger logger = LoggerFactory.getLogger(SSLconnection.class); + + // Public definition + public static final SSLconnection UNKNOWN = new SSLconnection(); + + /* + * *************************** + * ***** Private Objects ***** + */ + + private static final int CONNECTION_BUFFER_SIZE = 4096; + + private boolean ready = false; + private @Nullable SSLSocket socket; + private @Nullable DataOutputStream dOut; + private @Nullable DataInputStreamWithTimeout dIn; + private int ioTimeoutMSecs = 60000; + + /** + * Fake trust manager to suppress any certificate errors, + * used within {@link #SSLconnection} for seamless operation + * even on self-signed certificates like provided by Velux. + */ + private final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { + @Override + public X509Certificate @Nullable [] getAcceptedIssuers() { + return null; + } + + @Override + public void checkClientTrusted(X509Certificate @Nullable [] arg0, @Nullable String arg1) + throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate @Nullable [] arg0, @Nullable String arg1) + throws CertificateException { + } + } }; + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * Constructor for initialization of an unfinished connectivity. + */ + SSLconnection() { + logger.debug("SSLconnection() called."); + ready = false; + logger.trace("SSLconnection() finished."); + } + + /** + * Constructor to setup and establish a connection. + * + * @param host as String describing the Service Access Point location i.e. hostname. + * @param port as String describing the Service Access Point location i.e. TCP port. + * @throws java.net.ConnectException in case of unrecoverable communication failures. + * @throws java.io.IOException in case of continuous communication I/O failures. + * @throws java.net.UnknownHostException in case of continuous communication I/O failures. + */ + SSLconnection(String host, int port) throws ConnectException, IOException, UnknownHostException { + logger.debug("SSLconnection({},{}) called.", host, port); + logger.info("Starting {} bridge connection.", VeluxBindingConstants.BINDING_ID); + SSLContext ctx = null; + try { + ctx = SSLContext.getInstance("SSL"); + ctx.init(null, trustAllCerts, null); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new IOException(String.format("create of an empty trust store failed: %s.", e.getMessage())); + } + logger.trace("SSLconnection(): creating socket..."); + // Just for avoidance of Potential null pointer access + SSLSocket socketX = (SSLSocket) ctx.getSocketFactory().createSocket(host, port); + logger.trace("SSLconnection(): starting SSL handshake..."); + if (socketX != null) { + socketX.startHandshake(); + dOut = new DataOutputStream(socketX.getOutputStream()); + dIn = new DataInputStreamWithTimeout(socketX.getInputStream()); + ready = true; + socket = socketX; + } + logger.trace("SSLconnection() finished."); + } + + /* + * ************************** + * ***** Public Methods ***** + */ + + /** + * Method to query the readiness of the connection. + * + * @return ready as boolean for an established connection. + */ + synchronized boolean isReady() { + return ready; + } + + /** + * Method to pass a message towards the bridge. + * + * @param packet as Array of bytes to be transmitted towards the bridge via the established connection. + * @throws java.io.IOException in case of a communication I/O failure. + */ + @SuppressWarnings("null") + synchronized void send(byte[] packet) throws IOException { + logger.trace("send() called, writing {} bytes.", packet.length); + if (!ready || (dOut == null)) { + throw new IOException(); + } + dOut.write(packet, 0, packet.length); + if (logger.isTraceEnabled()) { + StringBuilder sb = new StringBuilder(); + for (byte b : packet) { + sb.append(String.format("%02X ", b)); + } + logger.trace("send() finished after having send {} bytes: {}", packet.length, sb.toString()); + } + } + + /** + * Method to verify that there is message from the bridge. + * + * @return true if there are any bytes ready to be queried using {@link SSLconnection#receive}. + * @throws java.io.IOException in case of a communication I/O failure. + */ + synchronized boolean available() throws IOException { + logger.trace("available() called."); + if (!ready || (dIn == null)) { + throw new IOException(); + } + @SuppressWarnings("null") + int availableBytes = dIn.available(); + logger.trace("available(): found {} bytes ready to be read (> 0 means true).", availableBytes); + return availableBytes > 0; + } + + /** + * Method to get a message from the bridge. + * + * @return packet as Array of bytes as received from the bridge via the established connection. + * @throws java.io.IOException in case of a communication I/O failure. + */ + synchronized byte[] receive() throws IOException { + logger.trace("receive() called."); + if (!ready || (dIn == null)) { + throw new IOException(); + } + byte[] message = new byte[CONNECTION_BUFFER_SIZE]; + @SuppressWarnings("null") + int messageLength = dIn.read(message, 0, message.length, ioTimeoutMSecs); + byte[] packet = new byte[messageLength]; + System.arraycopy(message, 0, packet, 0, messageLength); + if (logger.isTraceEnabled()) { + StringBuilder sb = new StringBuilder(); + for (byte b : packet) { + sb.append(String.format("%02X ", b)); + } + logger.trace("receive() finished after having read {} bytes: {}", messageLength, sb.toString()); + } + return packet; + } + + /** + * Destructor to tear down a connection. + * + * @throws java.io.IOException in case of a communication I/O failure. + */ + synchronized void close() throws IOException { + logger.debug("close() called."); + ready = false; + logger.info("Shutting down Velux bridge connection."); + // Just for avoidance of Potential null pointer access + DataInputStreamWithTimeout dInX = dIn; + if (dInX != null) { + dInX.close(); + } + // Just for avoidance of Potential null pointer access + DataOutputStream dOutX = dOut; + if (dOutX != null) { + dOutX.close(); + } + // Just for avoidance of Potential null pointer access + SSLSocket socketX = socket; + if (socketX != null) { + socketX.close(); + } + logger.trace("close() finished."); + } + + /** + * Parameter modification. + * + * @param timeoutMSecs the maximum duration in milliseconds for read operations. + */ + void setTimeout(int timeoutMSecs) { + logger.debug("setTimeout() set timeout to {} milliseconds.", timeoutMSecs); + ioTimeoutMSecs = timeoutMSecs; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/package-info.java new file mode 100644 index 0000000000000..be39bb832264f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/io/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * SLIP-protocol I/O with the bridge. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.bridge.slip.io; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/package-info.java new file mode 100644 index 0000000000000..cac9f3a6114ca --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * SLIP-protocol specific implementations of the interactions to IO-homecontrolled devices. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.bridge.slip; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/KLF200Response.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/KLF200Response.java new file mode 100644 index 0000000000000..d29aa9717f88f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/KLF200Response.java @@ -0,0 +1,164 @@ +/** + * 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.velux.internal.bridge.slip.utils; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.Command; +import org.openhab.binding.velux.internal.things.VeluxKLFAPI.CommandNumber; +import org.slf4j.Logger; + +/** + * Utility class for handling of KLF200 response packets. + * + *

+ * Methods available: + *

+ * Static methods are: + *

    + *
  • {@link #introLogging} for logging used at the beginning of the response handling.
  • + *
  • {@link #errorLogging} for error logging during processing.
  • + *
  • {@link #outroLogging} logging used at the end of the response handling.
  • + *
  • {@link #isLengthValid} for validation of length of the data part of the message.
  • + *
  • {@link #check4matchingNodeID} for validation of node identifier.
  • + *
  • {@link #check4matchingSessionID} for validation of session identifier.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class KLF200Response { + + /** + * Provides user-oriented logging in two log levels for monitoring KLF behavior. + *

+ * Introduction logging used at the beginning of the response handling. + * + * @param logger Instantiated logging class to be used for output. + * @param responseCommand The command byte of the response packet. + * @param thisResponseData The array of bytes which are passed back within the response package. + */ + public static void introLogging(Logger logger, short responseCommand, byte[] thisResponseData) { + logger.debug("setResponse({} with {} bytes of data) called.", Command.get(responseCommand).toString(), + thisResponseData.length); + logger.trace("setResponse(): handling response {} ({}).", Command.get(responseCommand).toString(), + new CommandNumber(responseCommand).toString()); + } + + /** + * Provides user-oriented logging in two log levels for monitoring KLF behavior. + *

+ * Error logging used at the point where an unexpected or unrecognized command has been received. + * + * @param logger Instantiated logging class to be used for output. + * @param responseCommand The command byte of the response packet. + */ + public static void errorLogging(Logger logger, short responseCommand) { + logger.trace("setResponse(): cannot handle response {} ({}).", Command.get(responseCommand).toString(), + responseCommand); + logger.warn("Gateway response {} ({}) cannot be handled at this point of interaction.", + Command.get(responseCommand).toString(), responseCommand); + } + + /** + * Provides user-oriented logging in two log levels for monitoring KLF behavior. + *

+ * Conclusion logging used at the end of the response handling. + * + * @param logger Instantiated logging class to be used for output. + * @param success Describes the success of the response processing. + * @param finished Describes whether the response processing has come to an end. + */ + public static void outroLogging(Logger logger, boolean success, boolean finished) { + logger.trace("setResponse(): finished={},success={}.", finished, success); + } + + /** + * Provides user-oriented logging in two log levels for monitoring KLF behavior. + *

+ * Check the intended length of the response packet. + * + * @param logger Instantiated logging class to be used for output. + * @param responseCommand The command byte of the response packet. + * @param thisResponseData The array of bytes which are passed back within the response package. + * @param requiredResponseDataLength The expected size of the array of bytes which are passed back within the + * response package. + * @return isLengthValid of type boolean which signals the validity of the packet length. + */ + public static boolean isLengthValid(Logger logger, short responseCommand, byte[] thisResponseData, + int requiredResponseDataLength) { + logger.trace("isLengthValid() called for {} ({}) with {} bytes of data.", + Command.get(responseCommand).toString(), new CommandNumber(responseCommand).toString(), + thisResponseData.length); + if (thisResponseData.length != requiredResponseDataLength) { + logger.warn( + "Gateway response {} ({}) consists of a malformed packet (effective length is {}, required length is {}).", + Command.get(responseCommand).toString(), new CommandNumber(responseCommand).toString(), + thisResponseData.length, requiredResponseDataLength); + return false; + } + logger.trace("isLengthValid() returns {}.", true); + return true; + } + + /** + * Provides user-oriented logging in two log levels for monitoring KLF behavior. + *

+ * Internal support method to match two values for equality. + * + * @param logger Instantiated logging class to be used for output. + * @param idName String describing the type of values being compared. + * @param requestID Value of type int have been replaced within the request. + * @param responseID Value of type int being received within the response. + * @return check4matchingAnyID of type boolean which signals the equality. + */ + private static boolean check4matchingAnyID(Logger logger, String idName, int requestID, int responseID) { + logger.trace("check4matchingAnyID() called for request{} {} and response{} {}.", idName, requestID, idName, + responseID); + if (requestID != responseID) { + logger.warn("Gateway response with {} {} unexpected as query asked for {} {}.", idName, requestID, idName, + responseID); + return false; + } + logger.trace("check4matchingAnyID() returns {}.", true); + return true; + } + + /** + * Compare the node identifier of the request with the node identifier within the response + * with user-oriented logging in two log levels for monitoring KLF behavior. + * + * @param logger Instantiated logging class to be used for output. + * @param reqNodeID Node identifier of the request. + * @param cfmNodeID Node identifier of the response. + * @return success of type boolean which signals the success of the communication. + */ + public static boolean check4matchingNodeID(Logger logger, int reqNodeID, int cfmNodeID) { + logger.trace("check4matchingNodeID() called for requestNodeID {} and responseNodeID {}.", reqNodeID, cfmNodeID); + return check4matchingAnyID(logger, "NodeID", reqNodeID, cfmNodeID); + } + + /** + * Compare the session identifier of the request with the session identifier within the response + * with user-oriented logging in two log levels for monitoring KLF behavior. + * + * @param logger Instantiated logging class to be used for output. + * @param reqSessionID Session identifier of the request. + * @param cfmSessionID Session identifier of the response. + * @return success of type boolean which signals the success of the communication. + */ + public static boolean check4matchingSessionID(Logger logger, int reqSessionID, int cfmSessionID) { + logger.trace("check4matchingSessionID() called for requestNodeID {} and responseNodeID {}.", reqSessionID, + cfmSessionID); + return check4matchingAnyID(logger, "SessionID", reqSessionID, cfmSessionID); + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/Packet.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/Packet.java new file mode 100644 index 0000000000000..b351d0e05ee16 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/Packet.java @@ -0,0 +1,280 @@ +/** + * 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.velux.internal.bridge.slip.utils; + +import java.util.Arrays; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Utility class for handling of packets i.e. array of bytes. + * + *

+ * Methods available: + *

    + *
  • {@link #Packet(byte[])} creates a Packet object based on the array of bytes.
  • + *
  • {@link #length()} returns the number of bytes contained within this Packet.
  • + *
  • {@link #toByteArray} returns the complete Packet as array of bytes.
  • + *
  • {@link #getByteArray} returns the a range of bytes as array of bytes.
  • + *
  • {@link #toString} returns the complete packet as human-readable String.
  • + *
  • {@link #getOneByteValue} returns the value of one byte as int.
  • + *
  • {@link #setOneByteValue} sets the value of one byte within the Packet.
  • + *
  • {@link #getTwoByteValue} returns the value of two bytes as int.
  • + *
  • {@link #setTwoByteValue} sets the value of two bytes within the Packet.
  • + *
  • {@link #getFourByteValue(int)} returns the value of four bytes as int.
  • + *
  • {@link #setFourByteValue} sets the value of four bytes within the Packet.
  • + *
  • {@link #getString} returns the value of a range of bytes as String.
  • + *
  • {@link #setString} sets the value of a range of bytes within the Packet.
  • + *
+ * Static methods are: + *
    + *
  • {@link #shortToString} converts a byte into a human-readable hex-String.
  • + *
  • {@link #byteArrayToInt} converts a range of four bytes into an int.
  • + *
  • {@link #intToIPAddressString} converts an int into a String.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class Packet { + + /* + * =========================================================== + * Internal Objects + */ + + private static final String BLANK = " "; + + private byte[] data; + + /* + * =========================================================== + * Constructor Method + */ + + /** + * Constructor: Create a {@link org.openhab.binding.velux.internal.bridge.slip.utils.Packet Packet} out of a + * sequence of + * bytes. + * + * @param thisData Packet as Array of bytes. + */ + public Packet(byte[] thisData) { + this.data = thisData; + } + + /* + * =========================================================== + * Access Methods + */ + + /** + * Returns the length of the {@link org.openhab.binding.velux.internal.bridge.slip.utils.Packet Packet}.. + * + * @return packetLength + * of type int. + */ + public int length() { + return data.length; + } + + /** + * Returns the complete {@link org.openhab.binding.velux.internal.bridge.slip.utils.Packet Packet} as sequence of + * bytes. + * + * @return packet + * of type Array-of-byte. + */ + public byte[] toByteArray() { + return data; + } + + /** + * Returns a part of the {@link org.openhab.binding.velux.internal.bridge.slip.utils.Packet Packet} as sequence of + * bytes + * starting at position (n) up to the position (n+length-1). + * + * @param position Position (n) within the packet. + * @param length Length of the intended slice as int. + * @return packet of type Array-of-byte. + */ + public byte[] getByteArray(int position, int length) { + return Arrays.copyOfRange(data, position, position + length); + } + + /** + * Returns the complete {@link org.openhab.binding.velux.internal.bridge.slip.utils.Packet Packet} + * as human-readable sequence of hex bytes each separated by the given separator. + * + * @param separator as of Type String. + * @return packetString of type String. + */ + public String toString(String separator) { + StringBuilder sb = new StringBuilder(); + for (byte b : this.data) { + sb.append(String.format("%02X", b)); + sb.append(separator); + } + if (sb.lastIndexOf(separator) > 0) { + sb.deleteCharAt(sb.lastIndexOf(separator)); + } + return (sb.toString()); + } + + /** + * Returns the complete {@link org.openhab.binding.velux.internal.bridge.slip.utils.Packet Packet} + * as human-readable sequence of hex bytes each separated by a blank. + * + * @return packetString of type String. + */ + @Override + public String toString() { + return this.toString(BLANK); + } + + /** + * Returns the value of the byte at (n)th position as int value for convenience. + * + * @param position Position (n) within the packet. + * @return value of type int. + */ + public int getOneByteValue(int position) { + return (data[position] & 0xff); + } + + /** + * Modifies the value of the byte at (n)th position by setting it to the value passed as int. + * + * @param position Position (n) within the packet. + * @param value of type int. + */ + public void setOneByteValue(int position, int value) { + data[position] = (byte) value; + } + + /** + * Returns the value of the bytes at the positions (n)th and (n+1) as int value for convenience. + *

+ * Note: Big-endian LSB-0 encoding. + *

+ * + * @param position Position (n) within the packet. + * @return value of type int. + */ + public int getTwoByteValue(int position) { + return 0x00 << 24 | 0x00 << 16 | (data[position] & 0xff) << 8 | (data[position + 1] & 0xff); + } + + /** + * Modifies the value of the bytes at the positions (n) and (n+1) by setting it to the value passed as int. + *

+ * Note: Big-endian LSB-0 encoding. + *

+ * + * @param position Position (n) within the packet. + * @param value of type int. + */ + public void setTwoByteValue(int position, int value) { + data[position] = (byte) ((value >>> 8) & 0xFF); + data[position + 1] = (byte) (value & 0xFF); + } + + /** + * Returns the value of the bytes at the positions (n)th to (n+3) as int value for convenience. + *

+ * Note: Big-endian LSB-0 encoding. + *

+ * + * @param position Position (n) within the packet. + * @return value of type int. + */ + public int getFourByteValue(int position) { + return data[position] << 24 | (data[position + 1] & 0xff) << 16 | (data[position + 2] & 0xff) << 8 + | (data[position + 3] & 0xff); + } + + /** + * Modifies the value of the bytes at the positions (n) to (n+3) by setting it to the value passed as int. + *

+ * Note: Big-endian LSB-0 encoding. + *

+ * + * @param position Position (n) within the packet. + * @param value of type int. + */ + public void setFourByteValue(int position, int value) { + data[position] = (byte) ((value >>> 24) & 0xFF); + data[position + 1] = (byte) ((value >>> 16) & 0xFF); + data[position + 2] = (byte) ((value >>> 8) & 0xFF); + data[position + 3] = (byte) (value & 0xFF); + } + + /** + * Returns the char string converted byte-by-byte starting at the position (n) up to (n+length+1). + *

+ * Note: Any trailing null char will be eliminated. + *

+ * + * @param position Position (n) within the packet. + * @param length Length of the intended slice as int. + * @return value of type String. + */ + public String getString(int position, int length) { + return new String(Arrays.copyOfRange(data, position, position + length - 1)).replace("\0", ""); + } + + /** + * Modifies the value of the bytes starting at the position (n) by setting it to the character values passed as + * String. + *

+ * Note: The trailing null char will not be stored. + *

+ * + * @param position Position (n) within the packet. + * @param text of type String. + */ + public void setString(int position, String text) { + System.arraycopy(text, 0, data, 0, text.length()); + } + + /* + * =========================================================== + * Conversion Methods + */ + + /** + * Returns the hex char string representing the byte. + * + * @param oneByte of type byte to be converted. + * @return hexByteString of type String. + */ + static String shortToString(int oneByte) { + return String.format("%02X", oneByte); + } + + static int byteArrayToInt(byte[] data) { + return data[0] << 24 | (data[1] & 0xff) << 16 | (data[2] & 0xff) << 8 | (data[3] & 0xff); + } + + /** + * Returns the dotted string representing an IP address. + * + * @param ipAddress of type int to be converted. + * @return ipAddressString of type String. + */ + public static String intToIPAddressString(int ipAddress) { + return String.format("%d.%d.%d.%d", ((ipAddress >>> 24) & 0xFF), ((ipAddress >>> 16) & 0xFF), + ((ipAddress >>> 8) & 0xFF), (ipAddress & 0xFF)); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipEncoding.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipEncoding.java new file mode 100644 index 0000000000000..afe9b6564518c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipEncoding.java @@ -0,0 +1,170 @@ +/** + * 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.velux.internal.bridge.slip.utils; + +import java.nio.ByteBuffer; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Transport layer supported by the Velux bridge. + *

+ * Module semantic: encoding and decoding of frames according to RFC 1055. + *

+ * It defines informations how to send query and receive answer through the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider VeluxBridgeProvider} + * as described by the {@link org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol + * BridgeCommunicationProtocol}. + *

+ * Methods available: + *

    + *
  • {@link #SlipEncoding(short command, byte[] data) } builds a message based on command and data.
  • + *
  • {@link #SlipEncoding(byte[] thisPacket) } splits a message into command and data.
  • + *
  • {@link #isValid} returns the number of bytes contained within this Packet.
  • + *
  • {@link #getCommand} returns the Command part of the message.
  • + *
  • {@link #getData} returns the data part of the message.
  • + *
  • {@link #toMessage} returns the complete message.
  • + *
  • {@link #toString} returns the message in a human-readable way.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class SlipEncoding { + + private final Logger logger = LoggerFactory.getLogger(SlipEncoding.class); + + private static final byte PROTOCOL_ID = 0; + private static boolean encodingValid = false; + private static byte[] message = new byte[0]; + + /** + * Builds a message based on command and parameters. + * + * @param command Message type as short. + * @param data Parameters as Array of bytes. + */ + public SlipEncoding(short command, byte[] data) { + logger.trace("SlipEncoding(constructor) for command 0x{} with data size {} called.", + Integer.toHexString(new Short(command).intValue()), data.length); + if (data.length > 250) { + logger.warn("SlipEncoding(constructor) called with data size {}: too big, aborting.", data.length); + encodingValid = false; + } else { + byte checksum = 0; + message = new byte[data.length + 5]; + message[0] = PROTOCOL_ID; + message[1] = (byte) (3 + data.length); + message[2] = (byte) (command >>> 8); + message[3] = (byte) command; + message[4 + data.length] = 0; + System.arraycopy(data, 0, message, 4, data.length); + for (byte b : message) { + checksum = (byte) (checksum ^ b); + } + message[4 + data.length] = checksum; + logger.trace("SlipEncoding(constructor) successfully initialized, storing bytes: {}.", this.toString()); + encodingValid = true; + } + } + + /** + * Validates a message based on transfer syntax as Array-of-bytes. + * + * @param thisPacket Message as Array of bytes. + */ + + public SlipEncoding(byte[] thisPacket) { + logger.trace("SlipEncoding(constructor) called for decoding a packet with size {}.", thisPacket.length); + message = thisPacket; + encodingValid = false; + do { + // ProtocolID:Length:Command:Data(0-250):Checksum + if (message.length < 5) { + logger.warn("SlipEncoding(constructor) called with data size {}: Packet too short.", message.length); + break; + } + if (message[0] != PROTOCOL_ID) { + logger.warn("SlipEncoding(constructor) called: Unexpected PROTOCOL_ID (got {}).", + Packet.shortToString(message[0])); + break; + } + byte checksum = 0; + for (int i = 0; i < message.length - 1; i++) { + checksum = (byte) (checksum ^ message[i]); + } + if (message[message.length - 1] != checksum) { + logger.warn("SlipEncoding(constructor) Invalid packet checksum (got {} != calculated {}).", + Packet.shortToString(message[message.length - 1]), Packet.shortToString(checksum)); + logger.debug("SlipEncoding(constructor) packet is {}.", new Packet(message).toString(":")); + break; + } + logger.trace("SlipEncoding(constructor) successfully initialized with command 0x{} and data {}.", + Packet.shortToString(this.getCommand()), new Packet(this.getData()).toString()); + encodingValid = true; + } while (false); + } + + /** + * Returns the validity of the message content. + * + * @return encodingValid + * of type boolean as status of the encoding or decoding. + */ + public boolean isValid() { + return encodingValid; + } + + /** + * Extracts the command. + * + * @return command + * of type short as encoded within the message. + */ + public short getCommand() { + short command = ByteBuffer.wrap(new byte[] { message[2], message[3] }).getShort(); + logger.trace("getCommand() returns 0x{}.", String.format("%02X ", command)); + return command; + } + + /** + * Extracts the data i.e. parameters to the command. + * + * @return data + * of type Array-of-byte as encoded within the message. + */ + public byte[] getData() { + byte[] data = new byte[message.length - 5]; + System.arraycopy(message, 4, data, 0, message.length - 5); + logger.trace("getData() returns {} bytes: {}.", data.length, new Packet(data).toString()); + return data; + } + + /** + * Returns the complete message. + * + * @return message + * of type Array-of-byte. + */ + public byte[] toMessage() { + return message; + } + + @Override + public String toString() { + return new Packet(message).toString(); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipRFC1055.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipRFC1055.java new file mode 100644 index 0000000000000..accc7eccad750 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/SlipRFC1055.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.velux.internal.bridge.slip.utils; + +import java.text.ParseException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Transport layer supported by the Velux bridge: + * SLIP wrapping supported by the Velux bridge. + *

+ * Methods available: + *

    + *
  • {@link #encode(byte[] payload) } converts a given payload into transfer byte encoding.
  • + *
  • {@link #decode(byte[] packet) } converts a given transfer byte encoding into a payload.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class SlipRFC1055 { + + private final Logger logger = LoggerFactory.getLogger(SlipRFC1055.class); + + private static final byte SLIP_BYTE_END = (byte) 0xC0; + private static final byte SLIP_BYTE_ESC = (byte) 0xDB; + private static final byte SLIP_BYTE_ESC_END = (byte) 0xDC; + private static final byte SLIP_BYTE_ESC_ESC = (byte) 0xDD; + + /** + * Converts a given payload into transfer byte encoding. + * + * @param payload Array of bytes to be transmitted. + * @return packet + * of type Array-of-byte as encoded payload. + */ + public byte[] encode(byte[] payload) { + logger.trace("encode() for data size {} called.", payload.length); + + int additional = 2; + for (byte b : payload) { + if ((b == SLIP_BYTE_ESC) || (b == SLIP_BYTE_END)) { + additional++; + } + } + byte[] packet = new byte[payload.length + additional]; + int packetIndex = 0; + packet[packetIndex++] = SLIP_BYTE_END; + + for (byte b : payload) { + if (b == SLIP_BYTE_ESC) { + packet[packetIndex++] = SLIP_BYTE_ESC; + packet[packetIndex++] = SLIP_BYTE_ESC_ESC; + } else if (b == SLIP_BYTE_END) { + packet[packetIndex++] = SLIP_BYTE_ESC; + packet[packetIndex++] = SLIP_BYTE_ESC_END; + } else { + packet[packetIndex++] = b; + } + } + packet[packetIndex++] = SLIP_BYTE_END; + assert (packetIndex == packet.length); + logger.trace("encode() provides transfer encoding: {}.", new Packet(packet)); + return packet; + } + + /** + * Converts a given transfer byte encoding into a payload. + * + * @param packet Array of bytes as being received. + * @return payload + * of type Array-of-byte as decoded payload. + * @throws ParseException in case of decoding errors. + */ + public byte[] decode(byte[] packet) throws ParseException { + logger.trace("decode() for packet size {} called.", packet.length); + if (packet.length < 3) { + throw new ParseException("Packet too short", 0); + } + if (packet[0] != SLIP_BYTE_END) { + throw new ParseException("Unexpected byte at 1st position", 0); + } + if (packet[packet.length - 1] != SLIP_BYTE_END) { + throw new ParseException("Unexpected byte at last position", 0); + } + int additional = -2; + for (int i = 0; i < packet.length; i++) { + if (packet[i] == SLIP_BYTE_ESC) { + additional--; + } + } + byte[] payload = new byte[packet.length + additional]; + + int packetIndex = 0; + for (int i = 0; i < packet.length; i++) { + if ((i == 0) || (i == packet.length - 1)) { + continue; + } + if ((packet[i] == SLIP_BYTE_ESC) && (packet[i + 1] == SLIP_BYTE_ESC_ESC)) { + payload[packetIndex++] = SLIP_BYTE_ESC; + i++; + } else if ((packet[i] == SLIP_BYTE_ESC) && (packet[i + 1] == SLIP_BYTE_ESC_END)) { + payload[packetIndex++] = SLIP_BYTE_END; + i++; + } else { + payload[packetIndex++] = packet[i]; + } + } + logger.trace("decode() provides payload: {}.", new Packet(payload)); + return payload; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/package-info.java new file mode 100644 index 0000000000000..a4503a79b83e1 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/bridge/slip/utils/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * Utility classes for the SLIP-protocol. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.bridge.slip.utils; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.java new file mode 100644 index 0000000000000..dbcef9ef5cfd2 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxBridgeConfiguration.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.velux.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link VeluxBridgeConfiguration} is a wrapper for + * configuration settings needed to access the + * {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider} + * device. + *

+ * It contains the factory default values as well. + *

    + *
  • {@link VeluxBridgeConfiguration#protocol protocol} protocol type + * (one of http or https or slip),
  • + *
  • {@link VeluxBridgeConfiguration#ipAddress ipAddress} bridge IP address,
  • + *
  • {@link VeluxBridgeConfiguration#tcpPort tcpPort} bridge TCP port,
  • + *
  • {@link VeluxBridgeConfiguration#password password} bridge password,
  • + *
  • {@link VeluxBridgeConfiguration#timeoutMsecs timeoutMsecs} communication timeout in milliseconds,
  • + *
  • {@link VeluxBridgeConfiguration#retries retries} number of retries (with exponential backoff algorithm),
  • + *
  • {@link VeluxBridgeConfiguration#refreshMSecs refreshMSecs} refreshMSecs interval for retrieval of bridge + * information.
  • + *
  • {@link VeluxBridgeConfiguration#isBulkRetrievalEnabled isBulkRetrievalEnabled} flag to use bulk product
  • + *
  • {@link VeluxBridgeConfiguration#isSequentialEnforced isSequentialEnforced} flag to enforce sequential control on + * actuators.
  • + *
  • {@link VeluxBridgeConfiguration#isProtocolTraceEnabled isProtocolTraceEnabled} flag to enable protocol logging + * (via loglevel INFO).
  • + *
+ *

+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBridgeConfiguration { + public static final String BRIDGE_PROTOCOL = "protocol"; + public static final String BRIDGE_IPADDRESS = "ipAddress"; + public static final String BRIDGE_TCPPORT = "tcpPort"; + public static final String BRIDGE_PASSWORD = "password"; + public static final String BRIDGE_TIMEOUT_MSECS = "timeoutMsecs"; + public static final String BRIDGE_RETRIES = "retries"; + public static final String BRIDGE_REFRESH_MSECS = "refreshMsecs"; + public static final String BRIDGE_IS_BULK_RETRIEVAL_ENABLED = "isBulkRetrievalEnabled"; + public static final String BRIDGE_IS_SEQUENTIAL_ENFORCED = "isSequentialEnforced"; + public static final String BRIDGE_PROTOCOL_TRACE_ENABLED = "isProtocolTraceEnabled"; + + /* + * Value to flag any changes towards the getter. + */ + public boolean hasChanged = true; + + /* + * Default values - should not be modified + */ + public String protocol = "slip"; + public String ipAddress = "192.168.1.1"; + public int tcpPort = 51200; + public String password = "velux123"; + public int timeoutMsecs = 1000; // one second + public int retries = 5; + public long refreshMSecs = 10000L; // 10 seconds + public boolean isBulkRetrievalEnabled = true; + public boolean isSequentialEnforced = false; + public boolean isProtocolTraceEnabled = false; +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java new file mode 100644 index 0000000000000..df3b51f7ed3c0 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/VeluxThingConfiguration.java @@ -0,0 +1,117 @@ +/** + * 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.velux.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.config.core.Configuration; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; + +/** + * The {@link VeluxThingConfiguration} is a wrapper for + * configuration settings needed to access the Velux device. + *

+ * It contains the factory default values as well. + *

+ * There are three parts. Information for: + *

    + *
  • {@link #sceneName} Name of a scene,
  • + *
  • {@link #serial} Unique identification of type actuator, rollershutter and window,
  • + *
  • {@link #name} Alternate Unique identification of type actuator, rollershutter and window,
  • + *
  • {@link #inverted} Value inversion for an actuator, rollershutter or window,
  • + *
  • {@link #velocity} Speed value for a scene,
  • + *
  • {@link #sceneLevels} Virtual shutter definition as a set of scenes imitating a shutter,
  • + *
  • {@link #currentLevel} Initial state of Virtual shutter definition.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + * @author Andrew Fiddian-Green - adapted. + */ +@NonNullByDefault +public class VeluxThingConfiguration extends Configuration { + + /** + * {@link #sceneName} of type {@link String}, identifying a Velux scene by human-readable name. + *

+ * Configuration for the channel scene: + *

+ *
    + *
  • {@link #sceneName} for identification of a set of settings, so called scene.
  • + *
+ */ + String sceneName = VeluxBindingConstants.UNKNOWN; + + /** + * {@link #serial} of type {@link String}, identifying a io-homecontrol device by its serial number (i.e. + * 43:12:14:5A:12:1C:05:5F). + *

+ * Configuration for the channels actuator, rollershutter and window: + *

+ *
    + *
  • {@link #serial} for identification of a io-homecontrol device,
  • + *
  • {@link #name} for alternate identification of a io-homecontrol device,
  • + *
  • {@link #inverted} for modified value behavior.
  • + *
  • {@link #velocity} for modified action speed.
  • + *
+ */ + public String serial = VeluxProductSerialNo.UNKNOWN; + /** + * {@link #name} of type {@link String}, identifying a io-homecontrol device by its registration name especially + * for somfy as they do not provide a valid serial number. + *

+ * Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}. + *

+ */ + String name = VeluxBindingConstants.UNKNOWN; + /** + * {@link #inverted} of type {@link Boolean}, inverts each Channel value. This means 0% will be handled as 100%, + * and vice versa, 100% will be handled as 0%. + *

+ * Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}. + *

+ */ + Boolean inverted = false; + /** + * {@link #velocity} of type {@link String}, describes the intended speed of action. + * Possible values are defined within VeluxProductVelocity. + *

+ * Part of the {@link #serial Configuration for the channels actuator, rollershutter and window}. + *

+ */ + String velocity = VeluxBindingConstants.UNKNOWN; + + /** + * {@link #sceneLevels} of type {@link String}, identifying a number of Velux scenes which act together as a virtual + * shutter. Each scene is defined to a corresponding shutter level. + *

+ * Configuration for the channel virtualshutter: + *

+ *
    + *
  • {@link #sceneLevels} for identification of a set of settings, so called scene.
  • + *
+ *

+ * Additionally it contains an internal variable for keeping the actual virtual shutter level. + *

+ *
    + *
  • {@link #currentLevel} for identification of a set of settings, so called scene.
  • + *
+ */ + String sceneLevels = VeluxBindingConstants.UNKNOWN; + /** + * {@link #currentLevel} of type {@link int}, which represents the current shutter level. + *

+ * Private part of the {@link #sceneLevels Configuration for the channel virtualshutter}. + *

+ */ + int currentLevel = 0; +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/package-info.java new file mode 100644 index 0000000000000..61c03559a5d25 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/config/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * Class with default openHAB configuration definitions. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.config; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/Threads.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/Threads.java new file mode 100644 index 0000000000000..5166206eeee01 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/Threads.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.velux.internal.development; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is a helper class for dealing with multiple threads and synchronization. + * + * It provides the following methods: + *
    + *
  • {@link #findDeadlocked} to print the current locking situation.
  • + *
+ *

+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class Threads { + private static final Logger LOGGER = LoggerFactory.getLogger(Threads.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * Suppress default constructor for creating a non-instantiable class. + */ + private Threads() { + throw new AssertionError(); + } + + // Class access methods + + /** + * Print the current established locks with the associated threads. + *

+ * Finds cycles of threads that are in deadlock waiting to acquire + * object monitors or ownable synchronizers. + * + * Threads are deadlocked in a cycle waiting for a lock of + * these two types if each thread owns one lock while + * trying to acquire another lock already held + * by another thread in the cycle. + *

+ * This method is designed for troubleshooting use, but not for + * synchronization control. It might be an expensive operation. + * + * @see ThreadMXBean#findDeadlockedThreads + */ + public static void findDeadlocked() { + ThreadMXBean tmx = ManagementFactory.getThreadMXBean(); + long[] ids = tmx.findDeadlockedThreads(); + if (ids != null) { + ThreadInfo[] infos = tmx.getThreadInfo(ids, true, true); + LOGGER.warn("findDeadlocked() The following threads are deadlocked:"); + for (ThreadInfo ti : infos) { + LOGGER.warn("findDeadlocked(): {}.", ti); + } + LOGGER.warn("findDeadlocked() done."); + } + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/package-info.java new file mode 100644 index 0000000000000..14c6e4454edc3 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/development/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * Generic classes to support development of bindings. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.development; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/VeluxDiscoveryService.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/VeluxDiscoveryService.java new file mode 100644 index 0000000000000..081ebd25ee378 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/VeluxDiscoveryService.java @@ -0,0 +1,273 @@ +/** + * 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.velux.internal.discovery; + +import static org.openhab.binding.velux.internal.VeluxBindingConstants.*; + +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.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.i18n.LocaleProvider; +import org.eclipse.smarthome.core.i18n.LocationProvider; +import org.eclipse.smarthome.core.i18n.TranslationProvider; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.handler.VeluxBridgeHandler; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; +import org.openhab.binding.velux.internal.things.VeluxScene; +import org.openhab.binding.velux.internal.utils.Localization; +import org.openhab.binding.velux.internal.utils.ManifestInformation; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxDiscoveryService} is responsible for discovering actuators and scenes on the current Velux Bridge. + * + * @author Guenther Schreiner - Initial contribution. + */ +// +// To-be-discussed: check whether an immediate activation is preferable. +// Might be activated by: +// @Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.velux") +// +@NonNullByDefault +@Component(service = DiscoveryService.class, configurationPid = "discovery.velux") +public class VeluxDiscoveryService extends AbstractDiscoveryService implements Runnable { + private final Logger logger = LoggerFactory.getLogger(VeluxDiscoveryService.class); + + // Class internal + + private static final int DISCOVER_TIMEOUT_SECONDS = 300; + + private @NonNullByDefault({}) LocaleProvider localeProvider; + private @NonNullByDefault({}) TranslationProvider i18nProvider; + private Localization localization = Localization.UNKNOWN; + private static @Nullable VeluxBridgeHandler bridgeHandler = null; + + // Private + + private void updateLocalization() { + if (localization == Localization.UNKNOWN && localeProvider != null && i18nProvider != null) { + logger.trace("updateLocalization(): creating Localization based on locale={},translation={}).", + localeProvider, i18nProvider); + localization = new Localization(localeProvider, i18nProvider); + } + } + + /** + * Constructor + *

+ * Initializes the {@link VeluxDiscoveryService} without any further information. + */ + public VeluxDiscoveryService() { + super(VeluxBindingConstants.SUPPORTED_THINGS_ITEMS, DISCOVER_TIMEOUT_SECONDS); + logger.trace("VeluxDiscoveryService(without Bridge) just initialized."); + } + + @Reference + protected void setLocaleProvider(final LocaleProvider givenLocaleProvider) { + logger.trace("setLocaleProvider(): provided locale={}.", givenLocaleProvider); + localeProvider = givenLocaleProvider; + updateLocalization(); + } + + @Reference + protected void setTranslationProvider(TranslationProvider givenI18nProvider) { + logger.trace("setTranslationProvider(): provided translation={}.", givenI18nProvider); + i18nProvider = givenI18nProvider; + updateLocalization(); + } + + /** + * Constructor + *

+ * Initializes the {@link VeluxDiscoveryService} with a reference to the well-prepared environment with a + * {@link VeluxBridgeHandler}. + * + * @param bridge Initialized Velux bridge handler. + * @param localizationHandler Initialized localization handler. + */ + public VeluxDiscoveryService(VeluxBridgeHandler bridge, Localization localizationHandler) { + super(VeluxBindingConstants.SUPPORTED_THINGS_ITEMS, DISCOVER_TIMEOUT_SECONDS); + logger.trace("VeluxDiscoveryService(bridge={},locale={},i18n={}) just initialized.", bridge, localeProvider, + i18nProvider); + + if (bridgeHandler == null) { + logger.trace("VeluxDiscoveryService(): registering bridge {} for lateron use for Discovery.", bridge); + } else if (!bridge.equals(bridgeHandler)) { + logger.trace("VeluxDiscoveryService(): replacing already registered bridge {} by {}.", bridgeHandler, + bridge); + } + bridgeHandler = bridge; + localization = localizationHandler; + } + + /** + * Constructor + *

+ * Initializes the {@link VeluxDiscoveryService} with a reference to the well-prepared environment with a + * {@link VeluxBridgeHandler}. + * + * @param bridge Initialized Velux bridge handler. + * @param locationProvider Provider for a location. + * @param localeProvider Provider for a locale. + * @param i18nProvider Provider for the internationalization. + */ + public VeluxDiscoveryService(VeluxBridgeHandler bridge, LocationProvider locationProvider, + LocaleProvider localeProvider, TranslationProvider i18nProvider) { + this(bridge, new Localization(localeProvider, i18nProvider)); + logger.trace("VeluxDiscoveryService(bridge={},locale={},i18n={}) finished.", bridge, localeProvider, + i18nProvider); + } + + @Override + public void deactivate() { + logger.trace("deactivate() called."); + super.deactivate(); + } + + @Override + protected synchronized void startScan() { + logger.trace("startScan() called."); + + logger.debug("startScan(): creating a thing of type binding."); + ThingUID thingUID = new ThingUID(THING_TYPE_BINDING, "org_openhab_binding_velux"); + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) + .withProperty(VeluxBindingProperties.PROPERTY_BINDING_BUNDLEVERSION, + ManifestInformation.getBundleVersion()) + .withLabel(localization.getText("discovery.velux.binding...label")).build(); + logger.debug("startScan(): registering new thing {}.", discoveryResult); + thingDiscovered(discoveryResult); + + if (bridgeHandler == null) { + logger.debug("startScan(): VeluxDiscoveryService cannot proceed due to missing Velux bridge."); + } else { + logger.debug("startScan(): Starting Velux discovery scan for scenes and actuators."); + discoverScenes(); + discoverProducts(); + } + logger.trace("startScan() done."); + } + + @Override + public synchronized void stopScan() { + logger.trace("stopScan() called."); + super.stopScan(); + removeOlderResults(getTimestampOfLastScan()); + logger.trace("stopScan() done."); + } + + @Override + public void run() { + logger.trace("run() called."); + } + + /** + * Discover the gateway-defined scenes. + */ + private void discoverScenes() { + logger.trace("discoverScenes() called."); + // Just for avoidance of Potential null pointer access + VeluxBridgeHandler bridgeHandlerX = bridgeHandler; + if (bridgeHandlerX == null) { + logger.debug("discoverScenes(): VeluxDiscoveryService.bridgeHandler not initialized, aborting discovery."); + return; + } + ThingUID bridgeUID = bridgeHandlerX.getThing().getUID(); + logger.debug("discoverScenes(): discovering all scenes."); + for (VeluxScene scene : bridgeHandlerX.existingScenes().values()) { + String sceneName = scene.getName().toString(); + logger.trace("discoverScenes(): found scene {}.", sceneName); + + String label = sceneName.replaceAll("\\P{Alnum}", "_"); + logger.trace("discoverScenes(): using label {}.", label); + + ThingTypeUID thingTypeUID = THING_TYPE_VELUX_SCENE; + ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, label); + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID) + .withProperty(VeluxBindingProperties.PROPERTY_SCENE_NAME, sceneName) + .withRepresentationProperty(VeluxBindingProperties.PROPERTY_SCENE_NAME).withBridge(bridgeUID) + .withLabel(label).build(); + logger.debug("discoverScenes(): registering new thing {}.", discoveryResult); + thingDiscovered(discoveryResult); + } + logger.trace("discoverScenes() finished."); + } + + /** + * Discover the gateway-defined products/actuators. + */ + private void discoverProducts() { + logger.trace("discoverProducts() called."); + // Just for avoidance of Potential null pointer access + VeluxBridgeHandler bridgeHandlerX = bridgeHandler; + if (bridgeHandlerX == null) { + logger.debug("discoverScenes() VeluxDiscoveryService.bridgeHandlerR not initialized, aborting discovery."); + return; + } + ThingUID bridgeUID = bridgeHandlerX.getThing().getUID(); + logger.debug("discoverProducts(): discovering all actuators."); + for (VeluxProduct product : bridgeHandlerX.existingProducts().values()) { + String serialNumber = product.getSerialNumber(); + String actuatorName = product.getProductName().toString(); + logger.trace("discoverProducts() found actuator {} (name {}).", serialNumber, actuatorName); + String identifier; + if (serialNumber.equals(VeluxProductSerialNo.UNKNOWN)) { + identifier = actuatorName; + } else { + identifier = serialNumber; + } + String label = actuatorName.replaceAll("\\P{Alnum}", "_"); + logger.trace("discoverProducts(): using label {}.", label); + ThingTypeUID thingTypeUID; + boolean isInverted = false; + logger.trace("discoverProducts() dealing with {} (type {}).", product, product.getProductType()); + switch (product.getProductType()) { + case SLIDER_WINDOW: + logger.trace("discoverProducts(): creating window."); + thingTypeUID = THING_TYPE_VELUX_WINDOW; + isInverted = true; + break; + + case SLIDER_SHUTTER: + logger.trace("discoverProducts(): creating rollershutter."); + thingTypeUID = THING_TYPE_VELUX_ROLLERSHUTTER; + break; + + default: + logger.trace("discoverProducts(): creating actuator."); + thingTypeUID = THING_TYPE_VELUX_ACTUATOR; + } + ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, label); + + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID) + .withProperty(VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER, identifier) + .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_NAME, actuatorName) + .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED, isInverted) + .withRepresentationProperty(VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER) + .withBridge(bridgeUID).withLabel(actuatorName).build(); + logger.debug("discoverProducts(): registering new thing {}.", discoveryResult); + thingDiscovered(discoveryResult); + } + logger.trace("discoverProducts() finished."); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/package-info.java new file mode 100644 index 0000000000000..bdfa9e4337563 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/discovery/package-info.java @@ -0,0 +1,19 @@ +/** + * 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 + */ +/** + * Classes to provide the discovery of things automatically and user-initiated after defining a valid Velux bridge + * configuration. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.discovery; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/VeluxHandlerFactory.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/VeluxHandlerFactory.java new file mode 100644 index 0000000000000..4fe4a637f86b6 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/VeluxHandlerFactory.java @@ -0,0 +1,207 @@ +/** + * 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.velux.internal.factory; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.i18n.LocaleProvider; +import org.eclipse.smarthome.core.i18n.TranslationProvider; +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.ThingUID; +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.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.discovery.VeluxDiscoveryService; +import org.openhab.binding.velux.internal.handler.VeluxBindingHandler; +import org.openhab.binding.velux.internal.handler.VeluxBridgeHandler; +import org.openhab.binding.velux.internal.handler.VeluxHandler; +import org.openhab.binding.velux.internal.utils.Localization; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link VeluxHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +@Component(service = ThingHandlerFactory.class, name = "binding.velux") +public class VeluxHandlerFactory extends BaseThingHandlerFactory { + private final Logger logger = LoggerFactory.getLogger(VeluxHandlerFactory.class); + + // Class internal + + private final Map> discoveryServiceRegistrations = new HashMap<>(); + + private Set veluxBindingHandlers = new HashSet(); + private Set veluxBridgeHandlers = new HashSet(); + private Set veluxHandlers = new HashSet(); + + private @NonNullByDefault({}) LocaleProvider localeProvider; + private @NonNullByDefault({}) TranslationProvider i18nProvider; + private Localization localization = Localization.UNKNOWN; + + // Private + + private void registerDeviceDiscoveryService(VeluxBridgeHandler bridgeHandler) { + VeluxDiscoveryService discoveryService = new VeluxDiscoveryService(bridgeHandler, localization); + ServiceRegistration discoveryServiceReg = bundleContext.registerService(DiscoveryService.class.getName(), + discoveryService, new Hashtable()); + discoveryServiceRegistrations.put(bridgeHandler.getThing().getUID(), discoveryServiceReg); + + } + + // Even if the compiler tells, that the value of cannot be null, it is possible! + // Therefore a @SuppressWarnings("null") is needed to suppress the warning + @SuppressWarnings("null") + private synchronized void unregisterDeviceDiscoveryService(ThingUID thingUID) { + logger.trace("unregisterDeviceDiscoveryService({}) called.", thingUID); + ServiceRegistration remove = discoveryServiceRegistrations.remove(thingUID); + if (remove != null) { + remove.unregister(); + } + } + + private @Nullable ThingHandler createBindingHandler(Thing thing) { + logger.trace("createBindingHandler({}) called for thing named '{}'.", thing.getUID(), thing.getLabel()); + VeluxBindingHandler veluxBindingHandler = new VeluxBindingHandler(thing, localization); + veluxBindingHandlers.add(veluxBindingHandler); + return veluxBindingHandler; + } + + private @Nullable ThingHandler createBridgeHandler(Thing thing) { + logger.trace("createBridgeHandler({}) called for thing named '{}'.", thing.getUID(), thing.getLabel()); + VeluxBridgeHandler veluxBridgeHandler = new VeluxBridgeHandler((Bridge) thing, localization); + veluxBridgeHandlers.add(veluxBridgeHandler); + registerDeviceDiscoveryService(veluxBridgeHandler); + return veluxBridgeHandler; + } + + private @Nullable ThingHandler createThingHandler(Thing thing) { + logger.trace("createThingHandler({}) called for thing named '{}'.", thing.getUID(), thing.getLabel()); + VeluxHandler veluxHandler = new VeluxHandler(thing, localization); + veluxHandlers.add(veluxHandler); + return veluxHandler; + } + + private void updateBindingState() { + veluxBindingHandlers.forEach((VeluxBindingHandler veluxBindingHandler) -> { + veluxBindingHandler.updateBindingState(veluxBridgeHandlers.size(), veluxHandlers.size()); + }); + } + + private void updateLocalization() { + if (localization == Localization.UNKNOWN && localeProvider != null && i18nProvider != null) { + logger.trace("updateLocalization(): creating Localization based on locale={},translation={}).", + localeProvider, i18nProvider); + localization = new Localization(localeProvider, i18nProvider); + } + } + + // Constructor + + @Activate + public VeluxHandlerFactory(final @Reference LocaleProvider givenLocaleProvider, + final @Reference TranslationProvider givenI18nProvider) { + logger.trace("VeluxHandlerFactory(locale={},translation={}) called.", givenLocaleProvider, givenI18nProvider); + localeProvider = givenLocaleProvider; + i18nProvider = givenI18nProvider; + + } + + @Reference + protected void setLocaleProvider(final LocaleProvider givenLocaleProvider) { + logger.trace("setLocaleProvider(): provided locale={}.", givenLocaleProvider); + localeProvider = givenLocaleProvider; + updateLocalization(); + } + + @Reference + protected void setTranslationProvider(TranslationProvider givenI18nProvider) { + logger.trace("setTranslationProvider(): provided translation={}.", givenI18nProvider); + i18nProvider = givenI18nProvider; + updateLocalization(); + } + + // Utility methods + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + boolean result = VeluxBindingConstants.SUPPORTED_THINGS_BINDING.contains(thingTypeUID) + || VeluxBindingConstants.SUPPORTED_THINGS_BRIDGE.contains(thingTypeUID) + || VeluxBindingConstants.SUPPORTED_THINGS_ITEMS.contains(thingTypeUID); + logger.trace("supportsThingType({}) called and returns {}.", thingTypeUID, result); + return result; + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingHandler resultHandler = null; + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + // Handle Binding creation + if (VeluxBindingConstants.SUPPORTED_THINGS_BINDING.contains(thingTypeUID)) { + resultHandler = createBindingHandler(thing); + } else + // Handle Bridge creation + if (VeluxBindingConstants.SUPPORTED_THINGS_BRIDGE.contains(thingTypeUID)) { + resultHandler = createBridgeHandler(thing); + } else + // Handle creation of Things behind the Bridge + if (VeluxBindingConstants.SUPPORTED_THINGS_ITEMS.contains(thingTypeUID)) { + resultHandler = createThingHandler(thing); + } else { + logger.warn("createHandler({}) failed: ThingHandler not found for {}.", thingTypeUID, thing.getLabel()); + } + updateBindingState(); + return resultHandler; + } + + @Override + protected void removeHandler(ThingHandler thingHandler) { + // Handle Binding removal + if (thingHandler instanceof VeluxBindingHandler) { + logger.trace("removeHandler() removing information element '{}'.", thingHandler.toString()); + veluxBindingHandlers.remove(thingHandler); + } else + // Handle Bridge removal + if (thingHandler instanceof VeluxBridgeHandler) { + logger.trace("removeHandler() removing bridge '{}'.", thingHandler.toString()); + veluxBridgeHandlers.remove(thingHandler); + unregisterDeviceDiscoveryService(thingHandler.getThing().getUID()); + } else + // Handle removal of Things behind the Bridge + if (thingHandler instanceof VeluxHandler) { + logger.trace("removeHandler() removing thing '{}'.", thingHandler.toString()); + veluxHandlers.remove(thingHandler); + } + updateBindingState(); + super.removeHandler(thingHandler); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/package-info.java new file mode 100644 index 0000000000000..a5a7fd46c35d1 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/factory/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * Classes to provide the initiation of all handlers i.e. the factory for all services and things. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.factory; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/BridgeChannels.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/BridgeChannels.java new file mode 100644 index 0000000000000..2496b2e0fdf14 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/BridgeChannels.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.velux.internal.handler; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.openhab.binding.velux.internal.handler.utils.ExtendedBaseBridgeHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * The class {@link BridgeChannels} provides methods for dealing with + * properties. + *

    + *
  • {@link #getAllChannelUIDs}
  • + *
  • {@link #getAllLinkedChannelUIDs}
  • + *
+ *

+ * Noninstantiable utility class + *

+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +final class BridgeChannels { + private static final Logger LOGGER = LoggerFactory.getLogger(BridgeChannels.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private BridgeChannels() { + throw new AssertionError(); + } + + /* + * ************************** + * ***** Public Methods ***** + */ + + /** + * Return the Channel identifiers of all child things and the bridge things. + *

+ * + * @param bridge which will be scrutinized for things. + * @return channelUIDs of type {@link Set} of {@link ChannelUID}s. + */ + static Set getAllChannelUIDs(ExtendedBaseBridgeHandler bridge) { + Set channelUIDs = new HashSet(); + Set things = new HashSet(bridge.getThing().getThings()); + things.add(bridge.getThing()); + for (Thing thing : things) { + for (Channel channel : thing.getChannels()) { + channelUIDs.add(channel.getUID()); + } + } + LOGGER.trace("getAllChannelUIDs() returns {}.", channelUIDs); + return channelUIDs; + } + + /** + * Return the Channel identifiers of all child things and the bridge things, + * which are linked. + *

+ * + * @param bridge which will be scrutinized for things. + * @return channelUIDs of type {@link Set} of {@link ChannelUID}s. + */ + static Set getAllLinkedChannelUIDs(ExtendedBaseBridgeHandler bridge) { + Set channelUIDs = new HashSet(); + Set things = new HashSet(bridge.getThing().getThings()); + things.add(bridge.getThing()); + for (Thing thing : things) { + for (Channel channel : thing.getChannels()) { + if (bridge.isLinked(channel.getUID())) { + channelUIDs.add(channel.getUID()); + } + } + } + LOGGER.trace("getAllLinkedChannelUIDs() returns {}.", channelUIDs); + return channelUIDs; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorLimitation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorLimitation.java new file mode 100644 index 0000000000000..27a9e5d39b637 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorLimitation.java @@ -0,0 +1,210 @@ +/** + * 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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeGetLimitation; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeSetLimitation; +import org.openhab.binding.velux.internal.handler.utils.ThingConfiguration; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel position of the Thing actuator: + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelActuatorLimitation extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelActuatorLimitation.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelActuatorLimitation() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + do { // just for common exit + boolean setMinimum = channelId.length() == 0; + if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleRefresh(): there are some existing products."); + } + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)) { + LOGGER.trace("handleRefresh(): aborting processing as {} is not set.", + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER); + break; + } + String actuatorSerial = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER); + LOGGER.trace("handleRefresh(): actuatorSerial={}", actuatorSerial); + + // Handle value inversion + boolean propertyInverted = false; + if (ThingConfiguration.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { + propertyInverted = (boolean) ThingConfiguration.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); + } + boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); + LOGGER.trace("handleRefresh(): isInverted={}.", isInverted); + actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); + + if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .isRegistered(actuatorSerial)) { + LOGGER.info("handleRefresh(): cannot work on unknown actuator with serial {}.", actuatorSerial); + break; + } + LOGGER.trace("handleRefresh(): fetching actuator for {}.", actuatorSerial); + VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .get(actuatorSerial); + LOGGER.trace("handleRefresh(): found actuator {}.", thisProduct); + + VeluxBridgeGetLimitation getLimitation = new VeluxBridgeGetLimitation(); + boolean success; + if (setMinimum) { + success = getLimitation.getMinimumLimitation(thisBridgeHandler.thisBridge, + thisProduct.getBridgeProductIndex().toInt()); + } else { + success = getLimitation.getMaximumLimitation(thisBridgeHandler.thisBridge, + thisProduct.getBridgeProductIndex().toInt()); + } + if (!success) { + LOGGER.info("handleRefresh(): retrieval failed."); + break; + } + VeluxProductPosition position = getLimitation.getLimitation(); + if (position.isValid()) { + PercentType positionAsPercent = position.getPositionAsPercentType(isInverted); + LOGGER.trace("handleRefresh(): found limitation of actuator at level {}.", positionAsPercent); + newState = positionAsPercent; + } else { + LOGGER.trace("handleRefresh(): limitation level of actuator is unknown."); + } + + } while (false); // common exit + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); + Command newValue = null; + do { // just for common exit + boolean setMinimum = channelId.length() == 0; + if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleCommand(): there are some existing products."); + } + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)) { + LOGGER.trace("handleCommand(): aborting processing as {} is not set.", + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER); + break; + } + String actuatorSerial = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER); + LOGGER.trace("handleCommand(): actuatorSerial={}", actuatorSerial); + + // Handle value inversion + boolean propertyInverted = false; + if (ThingConfiguration.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { + propertyInverted = (boolean) ThingConfiguration.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); + } + boolean isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); + LOGGER.trace("handleCommand(): isInverted={}.", isInverted); + actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); + + if (!thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .isRegistered(actuatorSerial)) { + LOGGER.info("handleCommand(): cannot work on unknown actuator with serial {}.", actuatorSerial); + break; + } + LOGGER.trace("handleCommand(): fetching actuator for {}.", actuatorSerial); + VeluxProduct thisProduct = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts + .get(actuatorSerial); + LOGGER.trace("handleCommand(): found actuator {}.", thisProduct); + + if (!(command instanceof PercentType)) { + LOGGER.trace("handleCommand(): aborting processing as command is not of type PercentType."); + } + + LOGGER.trace("handleCommand(): found command of type PercentType."); + VeluxProductPosition posCommand = new VeluxProductPosition((PercentType) command, isInverted); + LOGGER.trace("handleCommand(): found command to set level to {}.", posCommand); + + if (setMinimum) { + new VeluxBridgeSetLimitation().setMinimumLimitation(thisBridgeHandler.thisBridge, + thisProduct.getBridgeProductIndex().toInt(), posCommand); + } else { + new VeluxBridgeSetLimitation().setMaximumLimitation(thisBridgeHandler.thisBridge, + thisProduct.getBridgeProductIndex().toInt(), posCommand); + } + } while (false); // common exit + LOGGER.trace("handleCommand() returns {}.", newValue); + + return newValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java new file mode 100644 index 0000000000000..6b3134ca16dc6 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelActuatorPosition.java @@ -0,0 +1,179 @@ +/** + * 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.velux.internal.handler; + +import static org.openhab.binding.velux.internal.VeluxBindingConstants.CHANNEL_ACTUATOR_POSITION; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +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.UpDownType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeRunProductCommand; +import org.openhab.binding.velux.internal.bridge.common.GetProduct; +import org.openhab.binding.velux.internal.handler.utils.Thing2VeluxActuator; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel position of the Thing actuator: + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelActuatorPosition extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelActuatorPosition.class); + + // Constructors + + /** + * Suppress default constructor for non-instantiability. + */ + private ChannelActuatorPosition() { + throw new AssertionError(); + } + + // Public methods + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + do { // just for common exit + if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleRefresh(): there are some existing products."); + } + Thing2VeluxActuator veluxActuator = thisBridgeHandler.channel2VeluxActuator.get(channelUID); + GetProduct bcp = thisBridgeHandler.thisBridge.bridgeAPI().getProduct(); + if (bcp == null) { + LOGGER.trace("handleRefresh(): aborting processing as handler is null."); + break; + } + bcp.setProductId(veluxActuator.getProductBridgeIndex().toInt()); + if (thisBridgeHandler.thisBridge.bridgeCommunicate(bcp) && bcp.isCommunicationSuccessful()) { + try { + VeluxProductPosition position = new VeluxProductPosition(bcp.getProduct().getCurrentPosition()); + if (position.isValid()) { + PercentType positionAsPercent = position.getPositionAsPercentType(veluxActuator.isInverted()); + LOGGER.trace("handleRefresh(): found actuator at level {}.", positionAsPercent); + newState = positionAsPercent; + } else { + LOGGER.trace("handleRefresh(): level of actuator is unknown."); + } + } catch (Exception e) { + LOGGER.warn("handleRefresh(): getProducts() exception: {}.", e.getMessage()); + } + } + } while (false); // common exit + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); + Command newValue = null; + do { // just for common exit + assert thisBridgeHandler.bridgeParameters.actuators != null : "VeluxBridgeHandler.bridgeParameters.actuators not initialized."; + if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleCommand(): there are some existing products."); + } + Thing2VeluxActuator veluxActuator = thisBridgeHandler.channel2VeluxActuator.get(channelUID); + VeluxProductPosition targetLevel = VeluxProductPosition.UNKNOWN; + if (channelId.equals(CHANNEL_ACTUATOR_POSITION)) { + if ((command instanceof UpDownType) && (command == UpDownType.UP)) { + LOGGER.trace("handleCommand(): found UP command."); + targetLevel = veluxActuator.isInverted() ? new VeluxProductPosition(PercentType.HUNDRED) + : new VeluxProductPosition(PercentType.ZERO); + } else if ((command instanceof UpDownType) && (command == UpDownType.DOWN)) { + LOGGER.trace("handleCommand(): found DOWN command."); + targetLevel = veluxActuator.isInverted() ? new VeluxProductPosition(PercentType.ZERO) + : new VeluxProductPosition(PercentType.HUNDRED); + } else if ((command instanceof StopMoveType) && (command == StopMoveType.STOP)) { + LOGGER.trace("handleCommand(): found STOP command."); + targetLevel = new VeluxProductPosition(); + } else if (command instanceof PercentType) { + LOGGER.trace("handleCommand(): found command of type PercentType."); + PercentType ptCommand = (PercentType) command; + if (veluxActuator.isInverted()) { + ptCommand = new PercentType(PercentType.HUNDRED.intValue() - ptCommand.intValue()); + } + LOGGER.trace("handleCommand(): found command to set level to {}.", ptCommand); + targetLevel = new VeluxProductPosition(ptCommand); + } else { + LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command); + break; + } + } else { + if ((command instanceof OnOffType) && (command == OnOffType.ON)) { + LOGGER.trace("handleCommand(): found ON command."); + targetLevel = veluxActuator.isInverted() ? new VeluxProductPosition(PercentType.HUNDRED) + : new VeluxProductPosition(PercentType.ZERO); + } else if ((command instanceof OnOffType) && (command == OnOffType.OFF)) { + LOGGER.trace("handleCommand(): found OFF command."); + targetLevel = veluxActuator.isInverted() ? new VeluxProductPosition(PercentType.ZERO) + : new VeluxProductPosition(PercentType.HUNDRED); + } else { + LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command); + break; + } + } + LOGGER.debug("handleCommand(): sending command with target level {}.", targetLevel); + new VeluxBridgeRunProductCommand().sendCommand(thisBridgeHandler.thisBridge, + veluxActuator.getProductBridgeIndex().toInt(), targetLevel); + LOGGER.trace("handleCommand(): The new shutter level will be send through the home monitoring events."); + + if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleCommand(): position of actuators are updated."); + } + } while (false); // common exit + return newValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeCheck.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeCheck.java new file mode 100644 index 0000000000000..b25dbb02947c3 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeCheck.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.velux.internal.handler; + +import java.util.ArrayList; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.handler.utils.StateUtils; +import org.openhab.binding.velux.internal.handler.utils.ThingConfiguration; +import org.openhab.binding.velux.internal.things.VeluxScene; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel check of the Thing klf200 : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeCheck extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeCheck.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeCheck() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + do { // just for common exit + LOGGER.trace("handleCommand(): loop through all existing scenes."); + ArrayList unusedScenes = new ArrayList(); + for (VeluxScene scene : thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes.values()) { + boolean found = false; + LOGGER.trace("handleCommand(): .loop through all handled channels."); + for (ChannelUID thisChannelUID : BridgeChannels.getAllChannelUIDs(thisBridgeHandler)) { + LOGGER.trace("handleCommand(): evaluating ChannelUID {}.", thisChannelUID); + VeluxItemType thisItemType = VeluxItemType.getByThingAndChannel( + thisBridgeHandler.thingTypeUIDOf(thisChannelUID), thisChannelUID.getId()); + if (!thisItemType.equals(VeluxItemType.SCENE_ACTION)) { + LOGGER.trace("handleCommand(): ignoring non SCENE_ACTION."); + continue; + } + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_SCENE_NAME)) { + LOGGER.trace("handleCommand(): aborting processing as scene name is not set."); + break; + } + String sceneName = (String) ThingConfiguration.getValue(thisBridgeHandler, thisChannelUID, + VeluxBindingProperties.PROPERTY_SCENE_NAME); + LOGGER.trace("handleCommand(): comparing {} with {}.", scene.getName().toString(), sceneName); + if (scene.getName().toString().equals(sceneName)) { + LOGGER.trace("handleCommand(): scene {} used within item {}.", scene.getName(), + thisChannelUID.getAsString()); + found = true; + } + } + if (!found) { + unusedScenes.add(scene.getName().toString()); + LOGGER.trace("handleCommand(): scene {} is currently unused.", scene.getName()); + } + } + String result; + if (unusedScenes.size() > 0) { + result = thisBridgeHandler.localization.getText("channelValue.check-integrity-failed") + .concat(unusedScenes.toString()); + } else { + result = thisBridgeHandler.localization.getText("channelValue.check-integrity-ok"); + } + LOGGER.info("{}", result); + newState = StateUtils.createState(result); + } while (false); // common exit + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeDoDetection.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeDoDetection.java new file mode 100644 index 0000000000000..1617df3c13086 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeDoDetection.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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeDetectProducts; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel doDetection of the Thing klf200 : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeDoDetection extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeDoDetection.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeDoDetection() { + throw new AssertionError(); + } + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); + if (command == OnOffType.ON) { + LOGGER.trace("handleCommand(): about to activate veluxBridge detection mode."); + new VeluxBridgeDetectProducts().detectProducts(thisBridgeHandler.thisBridge); + } else { + LOGGER.trace("handleCommand(): ignoring OFF command."); + } + return null; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeFirmware.java new file mode 100644 index 0000000000000..40a89c8460003 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeFirmware.java @@ -0,0 +1,72 @@ +/** + * 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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeGetFirmware; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel firmware of the Thing klf200 : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeFirmware extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeFirmware.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeFirmware() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + thisBridgeHandler.bridgeParameters.firmware = new VeluxBridgeGetFirmware() + .retrieve(thisBridgeHandler.thisBridge); + if (thisBridgeHandler.bridgeParameters.firmware.isRetrieved) { + newState = thisBridgeHandler.bridgeParameters.firmware.firmwareVersion; + } + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java new file mode 100644 index 0000000000000..b50fdeede3b5f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeLANconfig.java @@ -0,0 +1,100 @@ +/** + * 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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeLANConfig; +import org.openhab.binding.velux.internal.handler.utils.StateUtils; +import org.openhab.binding.velux.internal.handler.utils.ThingProperty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channels ipAddress, subnetMask, defaultGW and DHCP of the Thing + * klf200 : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeLANconfig extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeLANconfig.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeLANconfig() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + thisBridgeHandler.bridgeParameters.lanConfig = new VeluxBridgeLANConfig() + .retrieve(thisBridgeHandler.thisBridge); + if (thisBridgeHandler.bridgeParameters.lanConfig.isRetrieved) { + VeluxItemType itemType = VeluxItemType.getByThingAndChannel(thisBridgeHandler.thingTypeUIDOf(channelUID), + channelUID.getId()); + switch (itemType) { + case BRIDGE_IPADDRESS: + newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress); + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_IPADDRESS, + thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress.toString()); + break; + case BRIDGE_SUBNETMASK: + newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask); + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_SUBNETMASK, + thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask.toString()); + break; + case BRIDGE_DEFAULTGW: + newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABdefaultGW); + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_DEFAULTGW, + thisBridgeHandler.bridgeParameters.lanConfig.openHABdefaultGW.toString()); + break; + case BRIDGE_DHCP: + newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABenabledDHCP); + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_DHCP, + thisBridgeHandler.bridgeParameters.lanConfig.openHABenabledDHCP.toString()); + default: + } + } + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeProducts.java new file mode 100644 index 0000000000000..4abe789afa620 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeProducts.java @@ -0,0 +1,74 @@ +/** + * 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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.handler.utils.StateUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel products of the Thing klf200: + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeProducts extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeProducts.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeProducts() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + if (thisBridgeHandler.bridgeParameters.actuators.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleCommand(): there are some existing products."); + } + String products = thisBridgeHandler.bridgeParameters.actuators.getChannel().existingProducts.toString(); + LOGGER.trace("handleCommand(): found products {}.", products); + products = products.replaceAll("[^\\p{Punct}\\w]", "_"); + newState = StateUtils.createState(products); + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeScenes.java new file mode 100644 index 0000000000000..126306b891aff --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeScenes.java @@ -0,0 +1,74 @@ +/** + * 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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.handler.utils.StateUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel scenes of the Thing klf200: + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeScenes extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeScenes.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeScenes() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + if (thisBridgeHandler.bridgeParameters.scenes.autoRefresh(thisBridgeHandler.thisBridge)) { + LOGGER.trace("handleCommand(): there are some existing scenes."); + } + String sceneInfo = thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes.toString(); + LOGGER.trace("handleCommand(): found scenes {}.", sceneInfo); + sceneInfo = sceneInfo.replaceAll("[^\\p{Punct}\\w]", "_"); + newState = StateUtils.createState(sceneInfo); + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeStatus.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeStatus.java new file mode 100644 index 0000000000000..22f59948e5f53 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeStatus.java @@ -0,0 +1,72 @@ +/** + * 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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeDeviceStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel status of the Thing klf200: + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeStatus extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeStatus.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeStatus() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + thisBridgeHandler.bridgeParameters.gateway = new VeluxBridgeDeviceStatus() + .retrieve(thisBridgeHandler.thisBridge); + if (thisBridgeHandler.bridgeParameters.gateway.isRetrieved) { + newState = thisBridgeHandler.bridgeParameters.gateway.gwState; + } + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.java new file mode 100644 index 0000000000000..5d542e00c9b3f --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelBridgeWLANconfig.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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeWLANConfig; +import org.openhab.binding.velux.internal.handler.utils.StateUtils; +import org.openhab.binding.velux.internal.handler.utils.ThingProperty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channels WLANSSID and WLANPassword of the Thing klf200 : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelBridgeWLANconfig extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelBridgeWLANconfig.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelBridgeWLANconfig() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + State newState = null; + thisBridgeHandler.bridgeParameters.wlanConfig = new VeluxBridgeWLANConfig() + .retrieve(thisBridgeHandler.thisBridge); + if (thisBridgeHandler.bridgeParameters.wlanConfig.isRetrieved) { + VeluxItemType itemType = VeluxItemType.getByThingAndChannel(thisBridgeHandler.thingTypeUIDOf(channelUID), + channelUID.getId()); + switch (itemType) { + case BRIDGE_WLANSSID: + newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABipAddress); + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_WLANSSID, + thisBridgeHandler.bridgeParameters.wlanConfig.openHABwlanSSID.toString()); + break; + case BRIDGE_WLANPASSWORD: + newState = StateUtils.createState(thisBridgeHandler.bridgeParameters.lanConfig.openHABsubnetMask); + ThingProperty.setValue(thisBridgeHandler, VeluxBindingConstants.PROPERTY_BRIDGE_WLANPASSWORD, + thisBridgeHandler.bridgeParameters.wlanConfig.openHABwlanPassword.toString()); + break; + default: + } + } + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelHandlerTemplate.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelHandlerTemplate.java new file mode 100644 index 0000000000000..6f23e63462f58 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelHandlerTemplate.java @@ -0,0 +1,72 @@ +/** + * 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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel VShutter : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +abstract class ChannelHandlerTemplate { + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue The value retrieved for the passed channel, or null in case if there is no (new) + * value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + throw new IllegalStateException("handleRefresh hasn't been set up in the subclass"); + } + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue value to be assigned to the channel by BaseThingHandler.postUpdate, or null, if + * not desired. + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + throw new IllegalStateException("handleRefresh hasn't been set up in the subclass"); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneAction.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneAction.java new file mode 100644 index 0000000000000..07bc016d3dafd --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneAction.java @@ -0,0 +1,122 @@ +/** + * 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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeRunScene; +import org.openhab.binding.velux.internal.handler.utils.ThingConfiguration; +import org.openhab.binding.velux.internal.things.VeluxProductVelocity; +import org.openhab.binding.velux.internal.things.VeluxScene; +import org.openhab.binding.velux.internal.things.VeluxScene.SceneName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel action of the Thing scene : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelSceneAction extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelSceneAction.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelSceneAction() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); + Command newValue = null; + do { // just for common exit + if (command != OnOffType.ON) { + LOGGER.trace("handleCommand(): ignoring OFF command."); + break; + } + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME)) { + LOGGER.trace("handleCommand(): aborting processing as scene name is not set."); + break; + } + String sceneName = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_SCENE_NAME); + if (!thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes + .isRegistered(new SceneName(sceneName))) { + LOGGER.info("handleCommand({},{}): cannot activate unknown scene: {}.", channelUID.getAsString(), + command, sceneName); + break; + } + String velocityName = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_SCENE_VELOCITY); + LOGGER.debug("handleCommand(): activating known scene {}.", sceneName); + VeluxScene thisScene = thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes + .get(new SceneName(sceneName)); + LOGGER.trace("handleCommand(): execution of scene {}.", thisScene); + if (VeluxProductVelocity.getByName(velocityName) == VeluxProductVelocity.UNDEFTYPE) { + new VeluxBridgeRunScene().execute(thisBridgeHandler.thisBridge, + thisScene.getBridgeSceneIndex().toInt()); + } else { + LOGGER.trace("handleCommand(): using velocity {}.", + VeluxProductVelocity.getByName(velocityName).toString()); + new VeluxBridgeRunScene().execute(thisBridgeHandler.thisBridge, thisScene.getBridgeSceneIndex().toInt(), + VeluxProductVelocity.getByName(velocityName).getVelocity()); + } + LOGGER.trace("handleCommand(): execution of scene finished."); + } while (false); // common exit + return newValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneSilentmode.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneSilentmode.java new file mode 100644 index 0000000000000..89f3d204ba91a --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelSceneSilentmode.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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeSetSceneVelocity; +import org.openhab.binding.velux.internal.handler.utils.ThingConfiguration; +import org.openhab.binding.velux.internal.things.VeluxScene; +import org.openhab.binding.velux.internal.things.VeluxScene.SceneName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel silentMode of the Thing scene : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelSceneSilentmode extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelSceneSilentmode.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelSceneSilentmode() { + throw new AssertionError(); + } + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); + Command newValue = null; + do { // just for common exit + assert thisBridgeHandler.bridgeParameters.scenes + .getChannel().existingScenes != null : "VeluxBridgeHandler.existingScenes not initialized."; + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_SCENE_NAME)) { + LOGGER.trace("handleCommand(): aborting processing as scene name is not set."); + break; + } + String sceneName = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_SCENE_NAME); + if (!thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes + .isRegistered(new SceneName(sceneName))) { + LOGGER.info("handleCommand({},{}): cannot modify unknown scene: {}.", channelUID.getAsString(), command, + sceneName); + break; + } + boolean silentMode = command.equals(OnOffType.ON); + LOGGER.debug("handleCommand(): setting silent mode to {}.", silentMode); + + VeluxScene thisScene = thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes + .get(new SceneName(sceneName)); + LOGGER.trace("handleCommand(): working on scene {}.", thisScene); + int sceneNumber = thisScene.getBridgeSceneIndex().toInt(); + new VeluxBridgeSetSceneVelocity().setSilentMode(thisBridgeHandler.thisBridge, sceneNumber, silentMode); + LOGGER.trace("handleCommand(): change of velocity done."); + } while (false); // common exit + return newValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelVShutterPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelVShutterPosition.java new file mode 100644 index 0000000000000..35cdfddad01da --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/ChannelVShutterPosition.java @@ -0,0 +1,168 @@ +/** + * 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.velux.internal.handler; + +import static org.openhab.binding.velux.internal.VeluxBindingConstants.CHANNEL_VSHUTTER_POSITION; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.VeluxRSBindingConfig; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeRunScene; +import org.openhab.binding.velux.internal.handler.utils.ThingConfiguration; +import org.openhab.binding.velux.internal.handler.utils.ThingProperty; +import org.openhab.binding.velux.internal.things.VeluxScene; +import org.openhab.binding.velux.internal.things.VeluxScene.SceneName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel position of the Thing vshutter : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ *
    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates by method {@link #handleCommand}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +final class ChannelVShutterPosition extends ChannelHandlerTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelVShutterPosition.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private ChannelVShutterPosition() { + throw new AssertionError(); + } + + /** + * Communication method to retrieve information to update the channel value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newState The value retrieved for the passed channel, or null in case if there is no (new) value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleRefresh({},{},{}) called.", channelUID, channelId, thisBridgeHandler); + assert (channelId == CHANNEL_VSHUTTER_POSITION); + State newState = null; + do { // just for common exit + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL)) { + LOGGER.trace("handleRefresh(): aborting processing as current scene level is not set."); + break; + } + // Don't know why OH2 returns BigDecimal. + BigDecimal rollershutterLevelBC = (BigDecimal) ThingConfiguration.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL); + int rollershutterLevel = rollershutterLevelBC.intValue(); + LOGGER.trace("handleRefresh(): current level is {}.", rollershutterLevel); + newState = new PercentType(rollershutterLevel); + } while (false); // common exit + LOGGER.trace("handleRefresh() returns {}.", newState); + return newState; + } + + /** + * Communication method to update the real world according to the passed channel value (or command). + * + * @param channelUID The item passed as type {@link ChannelUID} for which to following command is addressed to. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param command The command passed as type {@link Command} for the mentioned item. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue ... + */ + static @Nullable Command handleCommand(ChannelUID channelUID, String channelId, Command command, + VeluxBridgeHandler thisBridgeHandler) { + LOGGER.debug("handleCommand({},{},{},{}) called.", channelUID, channelId, command, thisBridgeHandler); + Command newValue = null; + // ThingProperty sceneLevels + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_VSHUTTER_SCENELEVELS)) { + LOGGER.trace("handleCommand(): aborting processing as scene levels are not set."); + return newValue; + } + String sceneLevels = (String) ThingConfiguration.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_VSHUTTER_SCENELEVELS); + // ThingProperty currentLevel + if (!ThingConfiguration.exists(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL)) { + LOGGER.trace("handleCommand(): aborting processing as current scene level is not set."); + return newValue; + } + // Don't know why OH2 returns BigDecimal. + BigDecimal rollershutterLevelBC = (BigDecimal) ThingConfiguration.getValue(thisBridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL); + int currentLevel = rollershutterLevelBC.intValue(); + LOGGER.trace("handleCommand(): current level is {}.", currentLevel); + + VeluxRSBindingConfig thisRSBindingConfig = new VeluxRSBindingConfig(VeluxItemType.VSHUTTER_POSITION, + sceneLevels, currentLevel); + + if ((UpDownType) command == UpDownType.UP) { + currentLevel = thisRSBindingConfig.getNextDescendingLevel(); + } else if ((UpDownType) command == UpDownType.DOWN) { + currentLevel = thisRSBindingConfig.getNextAscendingLevel(); + } else { + LOGGER.info("handleCommand({},{}): ignoring command.", channelUID.getAsString(), command); + return newValue; + } + LOGGER.trace("handleCommand(): next level is {}.", currentLevel); + String sceneName = thisRSBindingConfig.getSceneName(); + LOGGER.trace("handleCommand(): scene name is {}.", sceneName); + VeluxScene thisScene2 = thisBridgeHandler.bridgeParameters.scenes.getChannel().existingScenes + .get(new SceneName(sceneName)); + if (thisScene2 == VeluxScene.UNKNOWN) { + LOGGER.warn( + "handleCommand(): aborting command as scene with name {} is not registered; please check your KLF scene definitions.", + sceneName); + return newValue; + } + newValue = new PercentType(currentLevel); + LOGGER.trace("handleCommand(): executing scene {} with index {}.", thisScene2, + thisScene2.getBridgeSceneIndex().toInt()); + new VeluxBridgeRunScene().execute(thisBridgeHandler.thisBridge, thisScene2.getBridgeSceneIndex().toInt()); + + LOGGER.trace("handleCommand(): updating level to {}.", currentLevel); + ThingProperty.setValue(thisBridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_VSHUTTER_CURRENTLEVEL, + thisRSBindingConfig.getLevel().toString()); + LOGGER.trace("handleCommand() returns {}.", newValue); + return newValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/PropertyHandlerTemplate.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/PropertyHandlerTemplate.java new file mode 100644 index 0000000000000..c9ff9047a0857 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/PropertyHandlerTemplate.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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; + +/** + * Channel-specific retrieval and modification. + *

+ * This class implements the Channel VShutter : + *

    + *
  • Velux bridgeOpenHAB: + *

    + * Information retrieval by method {@link #handleRefresh}.

  • + *
+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +abstract class PropertyHandlerTemplate { + + /** + * Communication method to retrieve information to update the property value. + * + * @param channelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + * @param channelId The same item passed as type {@link String} for which a refresh is intended. + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @return newValue The value retrieved for the passed channel, or null in case if there is no (new) + * value. + */ + static @Nullable State handleRefresh(ChannelUID channelUID, String channelId, + VeluxBridgeHandler thisBridgeHandler) { + throw new IllegalStateException("handleRefresh hasn't been set up in the subclass"); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBindingHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBindingHandler.java new file mode 100644 index 0000000000000..fae5a31ea9c36 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBindingHandler.java @@ -0,0 +1,241 @@ +/** + * 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.velux.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +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.ThingTypeUID; +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.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.handler.utils.ExtendedBaseThingHandler; +import org.openhab.binding.velux.internal.handler.utils.StateUtils; +import org.openhab.binding.velux.internal.handler.utils.ThingProperty; +import org.openhab.binding.velux.internal.utils.Localization; +import org.openhab.binding.velux.internal.utils.ManifestInformation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * The class is responsible for representing the overall status of the Velux binding. + *

+ * Beside the normal thing handling introduced by {@link ExtendedBaseThingHandler}, it provides a method: + *

    + *
  • {@link #updateBindingState} to enable other classes to modify the number of activated Velux bridges and + * Things.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxBindingHandler extends ExtendedBaseThingHandler { + private final Logger logger = LoggerFactory.getLogger(VeluxBindingHandler.class); + + /* + * *************************** + * ***** Private Objects ***** + */ + private Thing thing; + private Localization localization; + private Integer currentNumberOfBridges = 0; + private Integer currentNumberOfThings = 0; + + /* + * ************************ + * ***** Constructors ***** + */ + + public VeluxBindingHandler(Thing thing, final Localization localization) { + super(thing); + this.thing = thing; + this.localization = localization; + logger.trace("VeluxBindingHandler(constructor) called."); + } + + /* + * *************************** + * ***** Private Methods ***** + */ + + /** + * Provide the ThingType for a given Channel. + *

+ * Separated into this private method to deal with the deprecated method. + *

+ * + * @param channelUID for type {@link ChannelUID}. + * @return thingTypeUID of type {@link ThingTypeUID}. + */ + @SuppressWarnings("deprecation") + private ThingTypeUID thingTypeUIDOf(ChannelUID channelUID) { + return channelUID.getThingUID().getThingTypeUID(); + } + + /** + * Returns a human-readable representation of the binding state. This should help especially unexperienced user to + * blossom up the introduction of the Velux binding. + * + * @return bindingInformation of type {@link String}. + */ + private String bridgeCountToString() { + String information; + switch (currentNumberOfBridges) { + case 0: + information = localization.getText("runtime.no-bridge"); + break; + case 1: + information = localization.getText("runtime.one-bridge"); + break; + default: + information = localization.getText("runtime.multiple-bridges"); + break; + } + return information; + } + + /** + * Modifies all informations within openHAB to inform the user about the current state of this binding. That is: + *
    + *
  • Update the properties about bundle version, number of bridges and things,
  • + *
  • Usability of the binding in respect to defined bridges within the ThingStatus, and
  • + *
  • Information of the binding state as channel value.
  • + *
+ */ + private void updateVisibleInformation() { + logger.trace("updateVisibleInformation(): updating properties."); + ThingProperty.setValue(thing, VeluxBindingProperties.PROPERTY_BINDING_BUNDLEVERSION, + ManifestInformation.getBundleVersion()); + ThingProperty.setValue(thing, VeluxBindingProperties.PROPERTY_BINDING_NOOFBRIDGES, + currentNumberOfBridges.toString()); + ThingProperty.setValue(thing, VeluxBindingProperties.PROPERTY_BINDING_NOOFTHINGS, + currentNumberOfThings.toString()); + + // BaseThingHandler is sensitive during initialization phase. Therefore, to avoid (wrong) warnings about: + // "tried updating the thing status although the handler was already disposed." + if (this.isInitialized()) { + logger.trace("updateVisibleInformation(): updating thing status."); + if (currentNumberOfBridges < 1) { + updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING, bridgeCountToString()); + } else { + updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, bridgeCountToString()); + } + logger.trace("updateVisibleInformation(): updating all channels."); + for (Channel channel : thing.getChannels()) { + handleCommand(channel.getUID(), RefreshType.REFRESH); + } + } + } + + /* + * ******************************************************************* + * ***** Objects and Methods for abstract class BaseThingHandler ***** + */ + + @Override + public void initialize() { + logger.debug("initialize() called."); + logger.info("Initializing VeluxBindingHandler for '{}'.", getThing().getUID()); + // The framework requires you to return from this method quickly. + // Setting the thing status to UNKNOWN temporarily and let the background task decide for the real status. + updateStatus(ThingStatus.UNKNOWN); + // Take care of unusual situations... + if (scheduler.isShutdown()) { + logger.warn("initialize(): scheduler is shutdown, aborting the initialization of this bridge."); + return; + } + logger.trace("initialize(): preparing background initialization task."); + // Background initialization... + scheduler.execute(() -> { + logger.trace("initialize.scheduled(): Setting ThingStatus to ONLINE."); + updateStatus(ThingStatus.ONLINE); + updateVisibleInformation(); + logger.trace("initialize.scheduled(): done."); + }); + logger.trace("initialize() done."); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.trace("handleCommand({},{}) called.", channelUID.getAsString(), command); + /* + * =========================================================== + * Common part + */ + String channelId = channelUID.getId(); + State newState = null; + String itemName = channelUID.getAsString(); + VeluxItemType itemType = VeluxItemType.getByThingAndChannel(thingTypeUIDOf(channelUID), channelUID.getId()); + + if (command instanceof RefreshType) { + /* + * =========================================================== + * Refresh part + */ + logger.trace("handleCommand(): refreshing item {}.", itemName); + switch (itemType) { + case BINDING_INFORMATION: + newState = StateUtils.createState(bridgeCountToString()); + break; + default: + logger.trace("handleCommand(): cannot handle REFRESH on channel {} as it is of type {}.", itemName, + channelId); + } + if (newState != null) { + logger.debug("handleCommand(): updating {} ({}) to {}.", itemName, channelUID, newState); + updateState(channelUID, newState); + } else { + logger.info("handleCommand({},{}): updating of item {} failed.", channelUID.getAsString(), command, + itemName); + } + } else { + /* + * =========================================================== + * Modification part + */ + switch (channelId) { + default: + logger.warn("handleCommand() cannot handle command {} on channel {} (type {}).", command, itemName, + itemType); + } + } + logger.trace("handleCommand() done."); + } + + /* + * ********************************** + * ***** (Other) Public Methods ***** + */ + + /** + * Update the information about bridges and things. + *

+ * Provided for instrumentation of factory class to update this set of information. + *

+ * + * @param veluxBridgeCount describing the number of initialized bridges. + * @param veluxThingCount describing the number of initialized things (in addition to Thing of type + * BindingInformation). + */ + public void updateBindingState(Integer veluxBridgeCount, Integer veluxThingCount) { + currentNumberOfBridges = veluxBridgeCount; + currentNumberOfThings = veluxThingCount; + updateVisibleInformation(); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java new file mode 100644 index 0000000000000..5b847434f0dc3 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxBridgeHandler.java @@ -0,0 +1,701 @@ +/** + * 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.velux.internal.handler; + +import java.util.concurrent.ConcurrentHashMap; +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.eclipse.smarthome.core.common.ThreadPoolManager; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +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.velux.internal.VeluxBinding; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.VeluxItemType; +import org.openhab.binding.velux.internal.bridge.VeluxBridge; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeActuators; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeDeviceStatus; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeGetFirmware; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeGetHouseStatus; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeInstance; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeLANConfig; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeProvider; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeScenes; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeSetHouseStatusMonitor; +import org.openhab.binding.velux.internal.bridge.VeluxBridgeWLANConfig; +import org.openhab.binding.velux.internal.bridge.common.BridgeAPI; +import org.openhab.binding.velux.internal.bridge.common.BridgeCommunicationProtocol; +import org.openhab.binding.velux.internal.bridge.json.JsonVeluxBridge; +import org.openhab.binding.velux.internal.bridge.slip.SlipVeluxBridge; +import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration; +import org.openhab.binding.velux.internal.development.Threads; +import org.openhab.binding.velux.internal.handler.utils.ExtendedBaseBridgeHandler; +import org.openhab.binding.velux.internal.handler.utils.Thing2VeluxActuator; +import org.openhab.binding.velux.internal.handler.utils.ThingProperty; +import org.openhab.binding.velux.internal.things.VeluxExistingProducts; +import org.openhab.binding.velux.internal.things.VeluxExistingScenes; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; +import org.openhab.binding.velux.internal.things.VeluxProductPosition; +import org.openhab.binding.velux.internal.utils.Localization; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Common interaction with the Velux bridge. + *

+ * It implements the communication between OpenHAB and the Velux Bridge: + *

    + *
  • OpenHAB Event Bus → Velux bridge + *

    + * Sending commands and value updates.

  • + *
+ *
    + *
  • Velux bridgeOpenHAB: + *

    + * Retrieving information by sending a Refresh command.

  • + *
+ *

+ * Entry point for this class is the method + * {@link VeluxBridgeHandler#handleCommand handleCommand}. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class VeluxBridgeHandler extends ExtendedBaseBridgeHandler implements VeluxBridgeInstance, VeluxBridgeProvider { + private final Logger logger = LoggerFactory.getLogger(VeluxBridgeHandler.class); + + // Class internal + + /** + * Scheduler for continuous refresh by scheduleWithFixedDelay. + */ + private @Nullable ScheduledFuture refreshJob = null; + + /** + * Counter of refresh invocations by {@link refreshJob}. + */ + private int refreshCounter = 0; + + /** + * Dedicated thread pool for the long-running bridge communication threads. + */ + private ScheduledExecutorService handleScheduler = ThreadPoolManager + .getScheduledPool(VeluxBindingConstants.BINDING_ID); + + private VeluxBridge myJsonBridge = new JsonVeluxBridge(this); + private VeluxBridge mySlipBridge = new SlipVeluxBridge(this); + + /* + * ************************************** + * ***** Default visibility Objects ***** + */ + + VeluxBridge thisBridge = myJsonBridge; + public BridgeParameters bridgeParameters = new BridgeParameters(); + Localization localization; + + /** + * Mapping from ChannelUID to class Thing2VeluxActuator, which return Velux device information, probably cached. + */ + ConcurrentHashMap channel2VeluxActuator = new ConcurrentHashMap(); + + /** + * Information retrieved by {@link VeluxBinding#VeluxBinding}. + */ + private VeluxBridgeConfiguration veluxBridgeConfiguration = new VeluxBridgeConfiguration(); + + /* + * ************************ + * ***** Constructors ***** + */ + + public VeluxBridgeHandler(final Bridge bridge, Localization localization) { + super(bridge); + logger.trace("VeluxBridgeHandler(constructor with bridge={}, localization={}) called.", bridge, localization); + this.localization = localization; + logger.debug("Creating a VeluxBridgeHandler for thing '{}'.", getThing().getUID()); + } + + // Private classes + + /** + *

+ * Set of information retrieved from the bridge/gateway: + *

+ *
    + *
  • {@link #actuators} - Already known actuators,
  • + *
  • {@link #scenes} - Already on the gateway defined scenes,
  • + *
  • {@link #gateway} - Current status of the gateway status,
  • + *
  • {@link #firmware} - Information about the gateway firmware revision,
  • + *
  • {@link #lanConfig} - Information about the gateway configuration,
  • + *
  • {@link #wlanConfig} - Information about the gateway configuration.
  • + *
+ */ + @NonNullByDefault + public class BridgeParameters { + /** Information retrieved by {@link VeluxBridgeActuators#getProducts} */ + public VeluxBridgeActuators actuators = new VeluxBridgeActuators(); + + /** Information retrieved by {@link org.openhab.binding.velux.internal.bridge.VeluxBridgeScenes#getScenes} */ + VeluxBridgeScenes scenes = new VeluxBridgeScenes(); + + /** Information retrieved by {@link VeluxBridgeDeviceStatus#retrieve} */ + VeluxBridgeDeviceStatus.Channel gateway = new VeluxBridgeDeviceStatus().getChannel(); + + /** Information retrieved by {@link VeluxBridgeGetFirmware#retrieve} */ + VeluxBridgeGetFirmware.Channel firmware = new VeluxBridgeGetFirmware().getChannel(); + + /** Information retrieved by {@link VeluxBridgeLANConfig#retrieve} */ + VeluxBridgeLANConfig.Channel lanConfig = new VeluxBridgeLANConfig().getChannel(); + + /** Information retrieved by {@link VeluxBridgeWLANConfig#retrieve} */ + VeluxBridgeWLANConfig.Channel wlanConfig = new VeluxBridgeWLANConfig().getChannel(); + } + + // Private methods + + /** + * Provide the ThingType for a given Channel. + *

+ * Separated into this private method to deal with the deprecated method. + *

+ * + * @param channelUID for type {@link ChannelUID}. + * @return thingTypeUID of type {@link ThingTypeUID}. + */ + @SuppressWarnings("deprecation") + ThingTypeUID thingTypeUIDOf(ChannelUID channelUID) { + return channelUID.getThingUID().getThingTypeUID(); + } + + // Objects and Methods for interface VeluxBridgeInstance + + /** + * Information retrieved by ... + */ + @Override + public VeluxBridgeConfiguration veluxBridgeConfiguration() { + return veluxBridgeConfiguration; + }; + + /** + * Information retrieved by {@link VeluxBridgeActuators#getProducts} + */ + @Override + public VeluxExistingProducts existingProducts() { + return bridgeParameters.actuators.getChannel().existingProducts; + }; + + /** + * Information retrieved by {@link VeluxBridgeScenes#getScenes} + */ + @Override + public VeluxExistingScenes existingScenes() { + return bridgeParameters.scenes.getChannel().existingScenes; + } + + // Objects and Methods for interface VeluxBridgeProvider ***** + + @Override + public boolean bridgeCommunicate(BridgeCommunicationProtocol communication) { + logger.warn("bridgeCommunicate() called. Should never be called (as implemented by protocol-specific layers)."); + return false; + } + + @Override + public @Nullable BridgeAPI bridgeAPI() { + logger.warn("bridgeAPI() called. Should never be called (as implemented by protocol-specific layers)."); + return null; + } + + // Provisioning/Deprovisioning methods ***** + + @Override + public void initialize() { + logger.info("Initializing Velux Bridge '{}'.", getThing().getUID()); + // The framework requires you to return from this method quickly. + // Setting the thing status to UNKNOWN temporarily and let the background task decide for the real status. + logger.trace("initialize() called."); + updateStatus(ThingStatus.UNKNOWN); + // Take care of unusual situations... + if (scheduler.isShutdown()) { + logger.warn("initialize(): scheduler is shutdown, aborting the initialization of this bridge."); + return; + } + if (handleScheduler.isShutdown()) { + logger.trace("initialize(): handleScheduler is shutdown, aborting the initialization of this bridge."); + return; + } + logger.trace("initialize(): preparing background initialization task."); + // Background initialization... + scheduler.execute(() -> { + logger.trace("initialize.scheduled(): Further work within scheduler.execute()."); + logger.trace("initialize.scheduled(): Initializing bridge configuration parameters."); + this.veluxBridgeConfiguration = new VeluxBinding(getConfigAs(VeluxBridgeConfiguration.class)).checked(); + logger.trace("initialize.scheduled(): work on updated bridge configuration parameters."); + bridgeParamsUpdated(); + + logger.debug("initialize.scheduled(): activated scheduler with {} milliseconds.", + this.veluxBridgeConfiguration.refreshMSecs); + refreshJob = scheduler.scheduleWithFixedDelay(() -> { + try { + refreshOpenHAB(); + } catch (RuntimeException e) { + logger.warn("Exception occurred during activated refresh scheduler: {}.", e.getMessage()); + } + }, this.veluxBridgeConfiguration.refreshMSecs, this.veluxBridgeConfiguration.refreshMSecs, + TimeUnit.MILLISECONDS); + logger.trace("initialize.scheduled(): done."); + }); + logger.trace("initialize() done."); + } + + /** + * NOTE: It takes care about shutting down the connections before removal of this binding. + */ + @Override + public synchronized void dispose() { + logger.info("Shutting down Velux Bridge '{}'.", getThing().getUID()); + logger.trace("dispose(): shutting down continous refresh."); + // Just for avoidance of Potential null pointer access + ScheduledFuture currentRefreshJob = refreshJob; + if (currentRefreshJob != null) { + logger.trace("dispose(): stopping the refresh."); + currentRefreshJob.cancel(true); + } + // Background execution of dispose + scheduler.execute(() -> { + logger.trace("dispose.scheduled(): (synchronous) logout initiated."); + thisBridge.bridgeLogout(); + logger.trace("dispose.scheduled(): shutting down JSON bridge."); + myJsonBridge.shutdown(); + logger.trace("dispose.scheduled(): shutting down SLIP bridge."); + mySlipBridge.shutdown(); + }); + logger.trace("dispose(): calling super class."); + super.dispose(); + logger.trace("dispose() done."); + } + + /** + * NOTE: It takes care by calling {@link #handleCommand} with the REFRESH command, that every used channel is + * initialized. + */ + @Override + public void channelLinked(ChannelUID channelUID) { + if (thing.getStatus() == ThingStatus.ONLINE) { + channel2VeluxActuator.put(channelUID, new Thing2VeluxActuator(this, channelUID)); + logger.trace("channelLinked({}) refreshing channel value with help of handleCommand as Thing is online.", + channelUID.getAsString()); + handleCommand(channelUID, RefreshType.REFRESH); + } else { + logger.trace("channelLinked({}) doing nothing as Thing is not online.", channelUID.getAsString()); + } + } + + @Override + public void channelUnlinked(ChannelUID channelUID) { + logger.trace("channelUnlinked({}) called.", channelUID.getAsString()); + } + + // Reconfiguration methods + + private void bridgeParamsUpdated() { + logger.debug("bridgeParamsUpdated() called."); + + // Determine the appropriate bridge communication channel + boolean validBridgeFound = false; + if (myJsonBridge.supportedProtocols.contains(veluxBridgeConfiguration.protocol)) { + logger.debug("bridgeParamsUpdated(): choosing JSON as communication method."); + thisBridge = myJsonBridge; + validBridgeFound = true; + } + if (mySlipBridge.supportedProtocols.contains(veluxBridgeConfiguration.protocol)) { + logger.debug("bridgeParamsUpdated(): choosing SLIP as communication method."); + thisBridge = mySlipBridge; + validBridgeFound = true; + } + if (!validBridgeFound) { + logger.debug("No valid protocol selected, aborting this {} binding.", VeluxBindingConstants.BINDING_ID); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/runtime.bridge-offline-no-valid-bridgeProtocol-selected"); + logger.trace("bridgeParamsUpdated() done."); + return; + } + + logger.trace("bridgeParamsUpdated(): Trying to authenticate towards bridge."); + + if (!thisBridge.bridgeLogin()) { + logger.warn("{} bridge login sequence failed; expecting bridge is OFFLINE.", + VeluxBindingConstants.BINDING_ID); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/runtime.bridge-offline-login-sequence-failed"); + logger.trace("bridgeParamsUpdated() done."); + return; + } + + logger.trace("bridgeParamsUpdated(): Querying bridge state."); + bridgeParameters.gateway = new VeluxBridgeDeviceStatus().retrieve(thisBridge); + + logger.trace("bridgeParamsUpdated(): Fetching existing scenes."); + bridgeParameters.scenes.getScenes(thisBridge); + logger.info("Found {} scenes:\n\t{}", VeluxBindingConstants.BINDING_ID, + bridgeParameters.scenes.getChannel().existingScenes.toString(false, "\n\t")); + logger.trace("bridgeParamsUpdated(): Fetching existing actuators/products."); + bridgeParameters.actuators.getProducts(thisBridge); + logger.info("Found {} actuators:\n\t{}", VeluxBindingConstants.BINDING_ID, + bridgeParameters.actuators.getChannel().existingProducts.toString(false, "\n\t")); + + if (thisBridge.bridgeAPI().setHouseStatusMonitor() != null) { + logger.trace("bridgeParamsUpdated(): Activating HouseStatusMonitor."); + if (new VeluxBridgeSetHouseStatusMonitor().modifyHSM(thisBridge, true)) { + logger.trace("bridgeParamsUpdated(): HSM activated."); + } else { + logger.warn("Activation of House-Status-Monitoring failed (might lead to a lack of status updates)."); + } + } + + veluxBridgeConfiguration.hasChanged = false; + logger.info("{} Bridge is online with {} scenes and {} actuators, now.", VeluxBindingConstants.BINDING_ID, + bridgeParameters.scenes.getChannel().existingScenes.getNoMembers(), + bridgeParameters.actuators.getChannel().existingProducts.getNoMembers()); + logger.debug("Velux veluxBridge is online, now."); + updateStatus(ThingStatus.ONLINE); + logger.trace("bridgeParamsUpdated() successfully finished."); + } + + // Continuous synchronization methods + + private synchronized void refreshOpenHAB() { + logger.debug("refreshOpenHAB() initiated by {} starting cycle {}.", Thread.currentThread(), refreshCounter); + + if (handleScheduler.isShutdown()) { + logger.trace("refreshOpenHAB(): handleScheduler is shutdown, recreating a scheduler pool."); + handleScheduler = ThreadPoolManager.getScheduledPool(VeluxBindingConstants.BINDING_ID); + } + + logger.trace("refreshOpenHAB(): processing of possible HSM messages."); + // Background execution of bridge related I/O + handleScheduler.execute(() -> { + logger.trace("refreshOpenHAB.scheduled() initiated by {} will process HouseStatus.", + Thread.currentThread()); + if (new VeluxBridgeGetHouseStatus().evaluateState(thisBridge)) { + logger.trace("refreshOpenHAB.scheduled(): successfully processed of GetHouseStatus()"); + } + logger.trace("refreshOpenHAB.scheduled() initiated by {} has finished.", Thread.currentThread()); + }); + + logger.trace( + "refreshOpenHAB(): looping through all (both child things and bridge) linked channels for a need of refresh."); + for (ChannelUID channelUID : BridgeChannels.getAllLinkedChannelUIDs(this)) { + if (VeluxItemType.isToBeRefreshedNow(refreshCounter, thingTypeUIDOf(channelUID), channelUID.getId())) { + logger.trace("refreshOpenHAB(): refreshing channel {}.", channelUID); + handleCommand(channelUID, RefreshType.REFRESH); + } + } + logger.trace("refreshOpenHAB(): looping through properties for a need of refresh."); + for (VeluxItemType veluxItem : VeluxItemType.getPropertyEntriesByThing(getThing().getThingTypeUID())) { + if (VeluxItemType.isToBeRefreshedNow(refreshCounter, getThing().getThingTypeUID(), + veluxItem.getIdentifier())) { + logger.trace("refreshOpenHAB(): refreshing property {}.", veluxItem.getIdentifier()); + handleCommand(new ChannelUID(getThing().getUID(), veluxItem.getIdentifier()), RefreshType.REFRESH); + } + } + logger.debug("refreshOpenHAB() initiated by {} finished cycle {}.", Thread.currentThread(), refreshCounter); + refreshCounter++; + } + + /** + * In case of recognized changes in the real world, the method will + * update the corresponding states via openHAB event bus. + */ + private void syncChannelsWithProducts() { + if (!bridgeParameters.actuators.getChannel().existingProducts.isDirty()) { + return; + } + logger.trace("syncChannelsWithProducts(): there are some existing products with changed parameters."); + outer: for (VeluxProduct product : bridgeParameters.actuators.getChannel().existingProducts + .valuesOfModified()) { + logger.trace("syncChannelsWithProducts(): actuator {} has changed values.", product.getProductName()); + ProductBridgeIndex productPbi = product.getBridgeProductIndex(); + logger.trace("syncChannelsWithProducts(): bridge index is {}.", productPbi); + for (ChannelUID channelUID : BridgeChannels.getAllLinkedChannelUIDs(this)) { + if (!channel2VeluxActuator.containsKey(channelUID)) { + logger.trace("syncChannelsWithProducts(): channel {} not found.", channelUID); + continue; + } + ProductBridgeIndex channelPbi = channel2VeluxActuator.get(channelUID).getProductBridgeIndex(); + if (!channelPbi.equals(productPbi)) { + continue; + } + // Handle value inversion + boolean isInverted = channel2VeluxActuator.get(channelUID).isInverted(); + logger.trace("syncChannelsWithProducts(): isInverted is {}.", isInverted); + VeluxProductPosition position = new VeluxProductPosition(product.getCurrentPosition()); + if (position.isValid()) { + PercentType positionAsPercent = position.getPositionAsPercentType(isInverted); + logger.debug("syncChannelsWithProducts(): updating channel {} to position {}%.", channelUID, + positionAsPercent); + updateState(channelUID, positionAsPercent); + } else { + logger.trace("syncChannelsWithProducts(): update of channel {} to position {} skipped.", channelUID, + position); + } + break outer; + } + } + logger.trace("syncChannelsWithProducts(): resetting dirty flag."); + bridgeParameters.actuators.getChannel().existingProducts.resetDirtyFlag(); + logger.trace("syncChannelsWithProducts() done."); + } + + // Processing of openHAB events + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.trace("handleCommand({}): command {} on channel {} will be scheduled.", Thread.currentThread(), command, + channelUID.getAsString()); + logger.debug("handleCommand({},{}) called.", channelUID.getAsString(), command); + + // Background execution of bridge related I/O + handleScheduler.execute(() -> { + logger.trace("handleCommand.scheduled({}) Start work with calling handleCommandScheduled().", + Thread.currentThread()); + handleCommandScheduled(channelUID, command); + logger.trace("handleCommand.scheduled({}) done.", Thread.currentThread()); + }); + logger.trace("handleCommand({}) done.", Thread.currentThread()); + } + + /** + * Normally called by {@link #handleCommand} to handle a command for a given channel with possibly long execution + * time. + *

+ * NOTE: This method is to be called as separated thread to ensure proper openHAB framework in parallel. + *

+ * + * @param channelUID the {@link ChannelUID} of the channel to which the command was sent, + * @param command the {@link Command}. + */ + private synchronized void handleCommandScheduled(ChannelUID channelUID, Command command) { + logger.trace("handleCommandScheduled({}): command {} on channel {}.", Thread.currentThread(), command, + channelUID.getAsString()); + logger.debug("handleCommandScheduled({},{}) called.", channelUID.getAsString(), command); + + /* + * =========================================================== + * Common part + */ + + if (veluxBridgeConfiguration.isProtocolTraceEnabled) { + Threads.findDeadlocked(); + } + + String channelId = channelUID.getId(); + State newState = null; + String itemName = channelUID.getAsString(); + VeluxItemType itemType = VeluxItemType.getByThingAndChannel(thingTypeUIDOf(channelUID), channelUID.getId()); + + if (itemType == VeluxItemType.UNKNOWN) { + logger.warn("{} Cannot determine type of Channel {}, ignoring command {}.", + VeluxBindingConstants.LOGGING_CONTACT, channelUID, command); + logger.trace("handleCommandScheduled() aborting."); + return; + } + + // Build cache + if (!channel2VeluxActuator.containsKey(channelUID)) { + channel2VeluxActuator.put(channelUID, new Thing2VeluxActuator(this, channelUID)); + } + + if (veluxBridgeConfiguration.hasChanged) { + logger.trace("handleCommandScheduled(): work on updated bridge configuration parameters."); + bridgeParamsUpdated(); + } + + syncChannelsWithProducts(); + + if (command instanceof RefreshType) { + /* + * =========================================================== + * Refresh part + */ + logger.trace("handleCommandScheduled(): work on refresh."); + if (!itemType.isReadable()) { + logger.debug("handleCommandScheduled(): received a Refresh command for a non-readable item."); + } else { + logger.trace("handleCommandScheduled(): refreshing item {} (type {}).", itemName, itemType); + try { // expecting an IllegalArgumentException for unknown Velux device + switch (itemType) { + // Bridge channels + case BRIDGE_STATUS: + newState = ChannelBridgeStatus.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_DOWNTIME: + newState = new DecimalType( + thisBridge.lastCommunication() - thisBridge.lastSuccessfulCommunication()); + break; + case BRIDGE_FIRMWARE: + newState = ChannelBridgeFirmware.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_IPADDRESS: + case BRIDGE_SUBNETMASK: + case BRIDGE_DEFAULTGW: + case BRIDGE_DHCP: + newState = ChannelBridgeLANconfig.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_WLANSSID: + case BRIDGE_WLANPASSWORD: + newState = ChannelBridgeWLANconfig.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_SCENES: + newState = ChannelBridgeScenes.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_PRODUCTS: + newState = ChannelBridgeProducts.handleRefresh(channelUID, channelId, this); + break; + case BRIDGE_CHECK: + newState = ChannelBridgeCheck.handleRefresh(channelUID, channelId, this); + break; + // Actuator channels + case ACTUATOR_POSITION: + case ACTUATOR_STATE: + case ROLLERSHUTTER_POSITION: + case WINDOW_POSITION: + newState = ChannelActuatorPosition.handleRefresh(channelUID, channelId, this); + break; + case ACTUATOR_LIMIT_MINIMUM: + case ROLLERSHUTTER_LIMIT_MINIMUM: + case WINDOW_LIMIT_MINIMUM: + newState = ChannelActuatorLimitation.handleRefresh(channelUID, "", this); + break; + case ACTUATOR_LIMIT_MAXIMUM: + case ROLLERSHUTTER_LIMIT_MAXIMUM: + case WINDOW_LIMIT_MAXIMUM: + newState = ChannelActuatorLimitation.handleRefresh(channelUID, channelId, this); + break; + + // VirtualShutter channels + case VSHUTTER_POSITION: + newState = ChannelVShutterPosition.handleRefresh(channelUID, channelId, this); + break; + + default: + logger.trace( + "handleCommandScheduled(): cannot handle REFRESH on channel {} as it is of type {}.", + itemName, channelId); + } + } catch (IllegalArgumentException e) { + logger.warn("Cannot handle REFRESH on channel {} as it isn't (yet) known to the bridge.", itemName); + } + if (newState != null) { + if (itemType.isChannel()) { + logger.debug("handleCommandScheduled(): updating channel {} to {}.", channelUID, newState); + updateState(channelUID, newState); + } + if (itemType.isProperty()) { + logger.debug("handleCommandScheduled(): updating property {} to {}.", channelUID, newState); + ThingProperty.setValue(this, itemType.getIdentifier(), newState.toString()); + + } + } else { + logger.info("handleCommandScheduled({},{}): updating of item {} (type {}) failed.", + channelUID.getAsString(), command, itemName, itemType); + } + } + } else { + /* + * =========================================================== + * Modification part + */ + logger.trace("handleCommandScheduled(): working on item {} (type {}) with COMMAND {}.", itemName, itemType, + command); + Command newValue = null; + try { // expecting an IllegalArgumentException for unknown Velux device + switch (itemType) { + // Bridge channels + case BRIDGE_RELOAD: + if (command == OnOffType.ON) { + logger.trace("handleCommandScheduled(): about to reload informations from veluxBridge."); + bridgeParamsUpdated(); + } else { + logger.trace("handleCommandScheduled(): ignoring OFF command."); + } + break; + case BRIDGE_DO_DETECTION: + ChannelBridgeDoDetection.handleCommand(channelUID, channelId, command, this); + break; + + // Scene channels + case SCENE_ACTION: + ChannelSceneAction.handleCommand(channelUID, channelId, command, this); + break; + case SCENE_SILENTMODE: + ChannelSceneSilentmode.handleCommand(channelUID, channelId, command, this); + break; + + // Actuator channels + case ACTUATOR_POSITION: + case ACTUATOR_STATE: + case ROLLERSHUTTER_POSITION: + case WINDOW_POSITION: + ChannelActuatorPosition.handleCommand(channelUID, channelId, command, this); + break; + case ACTUATOR_LIMIT_MINIMUM: + case ROLLERSHUTTER_LIMIT_MINIMUM: + case WINDOW_LIMIT_MINIMUM: + ChannelActuatorLimitation.handleCommand(channelUID, channelId, command, this); + break; + case ACTUATOR_LIMIT_MAXIMUM: + case ROLLERSHUTTER_LIMIT_MAXIMUM: + case WINDOW_LIMIT_MAXIMUM: + ChannelActuatorLimitation.handleCommand(channelUID, channelId, command, this); + break; + + // VirtualShutter channels + case VSHUTTER_POSITION: + newValue = ChannelVShutterPosition.handleCommand(channelUID, channelId, command, this); + break; + + default: + logger.warn("{} Cannot handle command {} on channel {} (type {}).", + VeluxBindingConstants.LOGGING_CONTACT, command, itemName, itemType); + } + } catch (IllegalArgumentException e) { + logger.warn("Cannot handle command on channel {} as it isn't (yet) known to the bridge.", itemName); + } + if (newValue != null) { + postCommand(channelUID, newValue); + } + } + ThingProperty.setValue(this, VeluxBindingConstants.PROPERTY_BRIDGE_TIMESTAMP_ATTEMPT, + new java.util.Date(thisBridge.lastCommunication()).toString()); + ThingProperty.setValue(this, VeluxBindingConstants.PROPERTY_BRIDGE_TIMESTAMP_SUCCESS, + new java.util.Date(thisBridge.lastSuccessfulCommunication()).toString()); + logger.trace("handleCommandScheduled({}) done.", Thread.currentThread()); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxHandler.java new file mode 100644 index 0000000000000..bc0b780b36a26 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/VeluxHandler.java @@ -0,0 +1,128 @@ +/** + * 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.velux.internal.handler; + +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.config.core.Configuration; +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.BridgeHandler; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.openhab.binding.velux.internal.config.VeluxThingConfiguration; +import org.openhab.binding.velux.internal.handler.utils.ExtendedBaseThingHandler; +import org.openhab.binding.velux.internal.utils.Localization; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * The{@link VeluxHandler} is responsible for handling commands, which are + * sent via {@link VeluxBridgeHandler} to one of the channels. + * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class VeluxHandler extends ExtendedBaseThingHandler { + private final Logger logger = LoggerFactory.getLogger(VeluxHandler.class); + + VeluxThingConfiguration configuration = new VeluxThingConfiguration(); + + public VeluxHandler(Thing thing, Localization localization) { + super(thing); + logger.trace("VeluxHandler(thing={},localization={}) constructor called.", thing, localization); + } + + @Override + public void initialize() { + logger.trace("initialize() called."); + Bridge thisBridge = getBridge(); + logger.debug("initialize(): Initializing thing {} in combination with bridge {}.", getThing().getUID(), + thisBridge); + if (thisBridge == null) { + logger.trace("initialize() updating ThingStatus to OFFLINE/CONFIGURATION_PENDING."); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING); + + } else if (thisBridge.getStatus() == ThingStatus.ONLINE) { + logger.trace("initialize() updating ThingStatus to ONLINE."); + updateStatus(ThingStatus.ONLINE); + initializeProperties(); + } else { + logger.trace("initialize() updating ThingStatus to OFFLINE/BRIDGE_OFFLINE."); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + } + logger.trace("initialize() done."); + } + + private synchronized void initializeProperties() { + configuration = getConfigAs(VeluxThingConfiguration.class); + logger.trace("initializeProperties() done."); + } + + @Override + public void dispose() { + logger.trace("dispose() called."); + super.dispose(); + } + + @Override + public void channelLinked(ChannelUID channelUID) { + logger.trace("channelLinked({}) called.", channelUID.getAsString()); + + if (thing.getStatus() == ThingStatus.ONLINE) { + handleCommand(channelUID, RefreshType.REFRESH); + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.trace("handleCommand({},{}) initiated by {}.", channelUID.getAsString(), command, + Thread.currentThread()); + Bridge bridge = getBridge(); + if (bridge == null) { + logger.trace("handleCommand() nothing yet to do as there is no bridge available."); + } else { + BridgeHandler handler = bridge.getHandler(); + if (handler == null) { + logger.trace("handleCommand() nothing yet to do as thing is not initialized."); + } else { + handler.handleCommand(channelUID, command); + } + } + logger.trace("handleCommand() done."); + } + + @Override + public void handleConfigurationUpdate(Map configurationParameters) { + if (isInitialized()) { // prevents change of address + validateConfigurationParameters(configurationParameters); + Configuration configuration = editConfiguration(); + for (Entry configurationParameter : configurationParameters.entrySet()) { + logger.trace("handleConfigurationUpdate(): found modified config entry {}.", + configurationParameter.getKey()); + } + // persist new configuration and reinitialize handler + dispose(); + updateConfiguration(configuration); + initialize(); + } else { + super.handleConfigurationUpdate(configurationParameters); + } + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/package-info.java new file mode 100644 index 0000000000000..c154d020febc1 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/package-info.java @@ -0,0 +1,19 @@ +/** + * 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 + */ +/** + * Classes for handling any kind of openHAB events and requests, providing a synchronisation between openHAB and the + * real world. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.handler; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ExtendedBaseBridgeHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ExtendedBaseBridgeHandler.java new file mode 100644 index 0000000000000..01c0674097adf --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ExtendedBaseBridgeHandler.java @@ -0,0 +1,76 @@ +/** + * 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.velux.internal.handler.utils; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; + +/** + * The {@link ExtendedBaseBridgeHandler} extended the {@link BaseBridgeHandler} interface and adds publicly + * visible convenience methods for property handling. + * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class ExtendedBaseBridgeHandler extends BaseBridgeHandler { + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * @see BaseBridgeHandler + * @param bridge which will be created. + */ + protected ExtendedBaseBridgeHandler(Bridge bridge) { + super(bridge); + } + + /** + * Returns a copy of the properties map, that can be modified. The method {@link #updateProperties} must be called + * to persist the properties. + * + * @return copy of the thing properties (not null) + */ + @Override + public Map editProperties() { + return super.editProperties(); + } + + /** + * Informs the framework, that the given properties map of the thing was updated. This method performs a check, if + * the properties were updated. If the properties did not change, the framework is not informed about changes. + * + * @param properties properties map, that was updated and should be persisted + */ + @Override + public void updateProperties(Map properties) { + super.updateProperties(properties); + } + + /** + * Returns whether at least one item is linked for the given UID of the channel. + * + * @param channelUID UID of the channel (must not be null) + * @return true if at least one item is linked, false otherwise + */ + @Override + public boolean isLinked(ChannelUID channelUID) { + return super.isLinked(channelUID); + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ExtendedBaseThingHandler.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ExtendedBaseThingHandler.java new file mode 100644 index 0000000000000..db0d248b8ff55 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ExtendedBaseThingHandler.java @@ -0,0 +1,68 @@ +/** + * 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.velux.internal.handler.utils; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; + +/** + * The {@link ExtendedBaseThingHandler} extended the {@link BaseThingHandler} interface and adds publicly + * visible convenience methods for property handling. + *

+ * It is recommended to extend this abstract base class. + *

+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public abstract class ExtendedBaseThingHandler extends BaseThingHandler { + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * @see BaseThingHandler + * @param thing which will be created. + */ + protected ExtendedBaseThingHandler(Thing thing) { + super(thing); + } + + /** + * Returns a copy of the properties map, that can be modified. The method {@link #updateProperties} must be called + * to persist the properties. + * + * @return copy of the thing properties (not null) + */ + @Override + public Map editProperties() { + return super.editProperties(); + } + + /** + * Informs the framework, that the given properties map of the thing was updated. This method performs a check, if + * the properties were updated. If the properties did not change, the framework is not informed about changes. + * + * @param properties properties map, that was updated and should be persisted + */ + @Override + public void updateProperties(Map properties) { + super.updateProperties(properties); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/StateUtils.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/StateUtils.java new file mode 100644 index 0000000000000..cbc0d1176c4cb --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/StateUtils.java @@ -0,0 +1,87 @@ +/** + * 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.velux.internal.handler.utils; + +import java.math.BigDecimal; + +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.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; + +/** + * This class support handling of openHAB type {@link State}. Therefore, it provides the methods: + *

    + *
  • {@link #createState} for creating an openHAB {@link State}.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class StateUtils { + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-instantiability + + private StateUtils() { + throw new AssertionError(); + } + + /** + * Creates an openHAB {@link State} in accordance to the class of the given {@code propertyValue}. Currently + * {@link PercentType}, {@link DecimalType}, and {@link Boolean} are handled explicitly. All other + * {@code dataTypes} are mapped to {@link StringType}. + *

+ * If {@code propertyValue} is {@code null}, {@link UnDefType#NULL} will be returned. + *

+ * Copied/adapted from the org.openhab.binding.koubachi binding. + *

+ * + * @param propertyValue which should be converted, + * @return state of type {@link State} in accordance with {@code dataType}. Will never be {@code null}. + */ + public static State createState(@Nullable Object propertyValue) { + if (propertyValue == null) { + return UnDefType.NULL; + } + + Class dataType = propertyValue.getClass(); + + if (PercentType.class.isAssignableFrom(dataType)) { + return new PercentType((Integer) propertyValue); + } else if (Integer.class.isAssignableFrom(dataType)) { + return new DecimalType((Integer) propertyValue); + } else if (BigDecimal.class.isAssignableFrom(dataType)) { + return new DecimalType((BigDecimal) propertyValue); + } else if (Boolean.class.isAssignableFrom(dataType)) { + if ((Boolean) propertyValue) { + return OnOffType.ON; + } else { + return OnOffType.OFF; + } + } else if (State.class.isAssignableFrom(dataType)) { + return (State) propertyValue; + } else { + return new StringType(propertyValue.toString()); + } + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/Thing2VeluxActuator.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/Thing2VeluxActuator.java new file mode 100644 index 0000000000000..ba2db6eb5e408 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/Thing2VeluxActuator.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.velux.internal.handler.utils; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.openhab.binding.velux.internal.VeluxBindingProperties; +import org.openhab.binding.velux.internal.handler.VeluxBridgeHandler; +import org.openhab.binding.velux.internal.things.VeluxProduct; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; +import org.openhab.binding.velux.internal.things.VeluxProductSerialNo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * The class {@link Thing2VeluxActuator} provides simplified access to Velux device behind the Velux bridge by + * evaluating the + * Thing property belonging to a channel and comparing them with the bridge registered objects. To put it in a nutshell, + * the methods provide a cache for faster access, + *
    + *
  • {@link #Thing2VeluxActuator} Constructor,
  • + *
  • {@link #getProductBridgeIndex} returns the Velux bridge index for access,
  • + *
  • {@link #isInverted} returns a flag about value inversion.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class Thing2VeluxActuator { + private final Logger logger = LoggerFactory.getLogger(Thing2VeluxActuator.class); + + // Class internal + + private VeluxBridgeHandler bridgeHandler; + private ChannelUID channelUID; + private boolean isInverted = false; + private VeluxProduct thisProduct = VeluxProduct.UNKNOWN; + + // Private + + private void mapThing2Velux() { + if (!ThingConfiguration.exists(bridgeHandler, channelUID, + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)) { + logger.warn("mapThing2Velux(): aborting processing as {} is not set.", + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER); + return; + } + String actuatorSerial = (String) ThingConfiguration.getValue(bridgeHandler, channelUID, + VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER); + logger.trace("mapThing2Velux(): found actuatorSerial={}.", actuatorSerial); + + // Handle value inversion + boolean propertyInverted = false; + if (ThingConfiguration.exists(bridgeHandler, channelUID, VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED)) { + propertyInverted = (boolean) ThingConfiguration.getValue(bridgeHandler, channelUID, + VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED); + } + isInverted = propertyInverted || VeluxProductSerialNo.indicatesRevertedValues(actuatorSerial); + logger.trace("mapThing2Velux(): found isInverted={}.", isInverted); + actuatorSerial = VeluxProductSerialNo.cleaned(actuatorSerial); + + if (!bridgeHandler.bridgeParameters.actuators.getChannel().existingProducts.isRegistered(actuatorSerial)) { + logger.warn("mapThing2Velux(): cannot work on unknown actuator with serial {}.", actuatorSerial); + return; + } + logger.trace("mapThing2Velux(): fetching actuator for {}.", actuatorSerial); + thisProduct = bridgeHandler.bridgeParameters.actuators.getChannel().existingProducts.get(actuatorSerial); + logger.debug("mapThing2Velux(): found actuator {}.", thisProduct); + return; + } + + // Constructor + + /** + * Constructor. + *

+ * + * @param thisBridgeHandler The Velux bridge handler with a specific communication protocol which provides + * information for this channel. + * @param thisChannelUID The item passed as type {@link ChannelUID} for which a refresh is intended. + */ + public Thing2VeluxActuator(VeluxBridgeHandler thisBridgeHandler, ChannelUID thisChannelUID) { + bridgeHandler = thisBridgeHandler; + channelUID = thisChannelUID; + } + + // Public methods + + /** + * Returns the Velux gateway index for accessing a Velux device based on the Thing configuration which belongs to + * the channel passed during constructor. + *

+ * + * @return bridgeProductIndex for accessing the Velux device (or ProductBridgeIndex.UNKNOWN if not found). + */ + public ProductBridgeIndex getProductBridgeIndex() { + if (thisProduct == VeluxProduct.UNKNOWN) { + mapThing2Velux(); + } + if (thisProduct == VeluxProduct.UNKNOWN) { + return ProductBridgeIndex.UNKNOWN; + } + return thisProduct.getBridgeProductIndex(); + } + + /** + * Returns the flag whether a value inversion in intended for the Velux device based on the Thing configuration + * which belongs to the channel passed during constructor. + *

+ * + * @return isInverted for handling of values of the Velux device (or false if not found).. + */ + public boolean isInverted() { + if (thisProduct == VeluxProduct.UNKNOWN) { + mapThing2Velux(); + } + if (thisProduct == VeluxProduct.UNKNOWN) { + logger.warn("isInverted(): Thing not found in Velux Bridge."); + } + return isInverted; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingConfiguration.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingConfiguration.java new file mode 100644 index 0000000000000..642543c0e61c7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingConfiguration.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.velux.internal.handler.utils; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * The class {@link ThingConfiguration} provides methods for dealing with + * properties. + *

    + *
  • {@link #exists} Check existence of a property,
  • + *
  • {@link #getValue} Returns a property value.
  • + *
+ *

+ * Noninstantiable utility class + *

+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class ThingConfiguration { + private static final Logger LOGGER = LoggerFactory.getLogger(ThingConfiguration.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-Instantiability + + private ThingConfiguration() { + throw new AssertionError(); + } + + /* + * ************************** + * ***** Public Methods ***** + */ + + /** + * Check existence of the configuration value for the given channel and + * desired configName which are defined within VeluxBindingProperties. + *

+ * + * @param bridge which handles the mentioned Things, + * @param channelUID describes the channel to by scrutinized, + * @param configName defines the configuration entry which is to be evaluated. + * @return exists of type boolean. + */ + public static boolean exists(BaseBridgeHandler bridge, ChannelUID channelUID, String configName) { + ThingUID channelTUID = channelUID.getThingUID(); + Thing thingOfChannel = bridge.getThingByUID(channelTUID); + boolean exists = false; + if (thingOfChannel == null) { + LOGGER.warn("exists(): Channel {} does not belong to a thing.", channelUID); + } else { + if (thingOfChannel.getConfiguration().get(configName) != null) { + exists = true; + } + } + LOGGER.trace("exists({},{}) returns {}.", channelUID, configName, exists); + return exists; + } + + /** + * Return the property value of type Object for the given channel and + * desired propertyName which are defined within VeluxBindingProperties. + *

+ * + * @param bridge which handles the mentioned Things, + * @param channelUID describes the channel to by scrutinized, + * @param configName defines the configuration entry which is to be evaluated. + * @return configurationValue of type {@link Object}. Will return {@code null}, if not found, or if value + * itself + * is {@code null}. + */ + public static Object getValue(BaseBridgeHandler bridge, ChannelUID channelUID, String configName) { + ThingUID channelTUID = channelUID.getThingUID(); + Thing thingOfChannel = bridge.getThingByUID(channelTUID); + if (thingOfChannel == null) { + LOGGER.warn("getValue(): Channel {} does not belong to a thing.", channelUID); + return true; + } + Object configurationValue = thingOfChannel.getConfiguration().get(configName); + LOGGER.trace("getValue({},{}) returns {}.", channelUID, configName, configurationValue); + return configurationValue; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingProperty.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingProperty.java new file mode 100644 index 0000000000000..0b27857d2e1af --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/ThingProperty.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.velux.internal.handler.utils; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * The class {@link ThingProperty} provides methods for dealing with + * properties. + *

    + *
  • {@link ThingConfiguration#exists} Check existence of a property,
  • + *
  • {@link ThingConfiguration#getValue} Returns a property value,
  • + *
  • {@link #setValue} Modifies a property value.
  • + *
+ *

+ * Noninstantiable utility class + *

+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class ThingProperty { + private static final Logger LOGGER = LoggerFactory.getLogger(ThingProperty.class); + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for non-Instantiability + + private ThingProperty() { + throw new AssertionError(); + } + + /* + * ************************** + * ***** Public Methods ***** + */ + + /** + * Modifies the property value of the given thing and the named property. + *

+ * + * @param thing which property will be modified, + * @param propertyName defines the property which is to be modified, + * @param propertyValue defines the new property value. + */ + public static void setValue(Thing thing, String propertyName, String propertyValue) { + thing.setProperty(propertyName, propertyValue); + LOGGER.trace("setValue() {} set to {}.", propertyName, propertyValue); + return; + } + + /** + * Modifies the property value for the given bridge, which is a dedicated thing, and the named property. + *

+ * + * @param bridgeHandler which contains the properties, + * @param propertyName defines the property which is to be modified. + * @param propertyValue defines the new property value. + */ + public static void setValue(ExtendedBaseBridgeHandler bridgeHandler, String propertyName, String propertyValue) { + setValue(bridgeHandler.getThing(), propertyName, propertyValue); + } + + /** + * Modifies the property value for the given propertyName, identified by the given bridge and channel.desired + * propertyName which are defined within + * VeluxBindingProperties. + *

+ * + * @param bridgeHandler which contains the properties, + * @param channelUID describes the channel to by scrutinized, + * @param propertyName defines the property which is to be modified. + * @param propertyValue defines the new property value. + */ + public static void setValue(ExtendedBaseBridgeHandler bridgeHandler, ChannelUID channelUID, String propertyName, + String propertyValue) { + ThingUID channelTUID = channelUID.getThingUID(); + Thing thingOfChannel = bridgeHandler.getThingByUID(channelTUID); + if (thingOfChannel == null) { + LOGGER.warn("setValue(): Channel {} does not belong to a thing.", channelUID); + return; + } + setValue(thingOfChannel, propertyName, propertyValue); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/package-info.java new file mode 100644 index 0000000000000..8d6e045afc4cc --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/handler/utils/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * Helper classes for dealing with configurations, properties, things and channels. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.handler.utils; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/package-info.java new file mode 100644 index 0000000000000..2bf7252673457 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * Classes for handling of openHAB bindings and configurations. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingProducts.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingProducts.java new file mode 100644 index 0000000000000..cbced7517a9f8 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingProducts.java @@ -0,0 +1,205 @@ +/** + * 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.velux.internal.things; + +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.things.VeluxProduct.ProductBridgeIndex; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Combined set of product informations provided by the Velux bridge, + * which can be used for later interactions. + *

+ * The following class access methods exist: + *

    + *
  • {@link VeluxExistingProducts#isRegistered} for querying existence of a {@link VeluxProduct},
  • + *
  • {@link VeluxExistingProducts#register} for storing a {@link VeluxProduct},
  • + *
  • {@link VeluxExistingProducts#update} for updating/storing of a {@link VeluxProduct},
  • + *
  • {@link VeluxExistingProducts#get} for retrieval of a {@link VeluxProduct},
  • + *
  • {@link VeluxExistingProducts#values} for retrieval of all {@link VeluxProduct}s,
  • + *
  • {@link VeluxExistingProducts#getNoMembers} for retrieval of the number of all {@link VeluxProduct}s,
  • + *
  • {@link VeluxExistingProducts#toString} for a descriptive string representation.
  • + *
+ * + * @see VeluxProduct + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxExistingProducts { + private final Logger logger = LoggerFactory.getLogger(VeluxExistingProducts.class); + + // Type definitions, class-internal variables + + private ConcurrentHashMap existingProductsByUniqueIndex; + private ConcurrentHashMap bridgeIndexToSerialNumber; + private ConcurrentHashMap modifiedProductsByUniqueIndex; + private int memberCount; + + /* + * Value to flag any changes towards the getter. + */ + private boolean dirty; + + // Constructor methods + + public VeluxExistingProducts() { + logger.trace("VeluxExistingProducts(constructor) called."); + existingProductsByUniqueIndex = new ConcurrentHashMap(); + bridgeIndexToSerialNumber = new ConcurrentHashMap(); + modifiedProductsByUniqueIndex = new ConcurrentHashMap(); + memberCount = 0; + dirty = true; + logger.trace("VeluxExistingProducts(constructor) done."); + } + + // Class access methods + + public boolean isRegistered(String productUniqueIndexOrSerialNumber) { + logger.trace("isRegistered(String {}) returns {}.", productUniqueIndexOrSerialNumber, + existingProductsByUniqueIndex.containsKey(productUniqueIndexOrSerialNumber) ? "true" : "false"); + return existingProductsByUniqueIndex.containsKey(productUniqueIndexOrSerialNumber); + } + + public boolean isRegistered(VeluxProduct product) { + logger.trace("isRegistered(VeluxProduct {}) called.", product.toString()); + if (product.isV2()) { + return isRegistered(product.getSerialNumber()); + } + return isRegistered(product.getProductUniqueIndex()); + } + + public boolean isRegistered(ProductBridgeIndex bridgeProductIndex) { + logger.trace("isRegisteredProductBridgeIndex {}) called.", bridgeProductIndex.toString()); + if (!bridgeIndexToSerialNumber.containsKey(bridgeProductIndex.toInt())) { + return false; + } + return isRegistered(bridgeIndexToSerialNumber.get(bridgeProductIndex.toInt())); + } + + public boolean register(VeluxProduct newProduct) { + logger.trace("register({}) called.", newProduct); + if (isRegistered(newProduct)) { + return false; + } + logger.trace("register() registering new product {}.", newProduct); + + String uniqueIndex = newProduct.isV2() ? newProduct.getSerialNumber() : newProduct.getProductUniqueIndex(); + logger.trace("register() registering by UniqueIndex {}", uniqueIndex); + existingProductsByUniqueIndex.put(uniqueIndex, newProduct); + + logger.trace("register() registering by ProductBridgeIndex {}", newProduct.getBridgeProductIndex().toInt()); + bridgeIndexToSerialNumber.put(newProduct.getBridgeProductIndex().toInt(), newProduct.getSerialNumber()); + + logger.trace("register() registering set of modifications by UniqueIndex {}", uniqueIndex); + modifiedProductsByUniqueIndex.put(uniqueIndex, newProduct); + + memberCount++; + dirty = true; + return true; + } + + public boolean update(ProductBridgeIndex bridgeProductIndex, int productState, int productPosition, + int productTarget) { + logger.debug("update(bridgeProductIndex={},productState={},productPosition={},productTarget={}) called.", + bridgeProductIndex.toInt(), productState, productPosition, productTarget); + if (!isRegistered(bridgeProductIndex)) { + logger.warn("update() failed as actuator (with index {}) is not registered.", bridgeProductIndex.toInt()); + return false; + } + VeluxProduct thisProduct = this.get(bridgeProductIndex); + if (thisProduct.setState(productState) || thisProduct.setCurrentPosition(productPosition) + || thisProduct.setTarget(productTarget)) { + dirty = true; + + String uniqueIndex = thisProduct.isV2() ? thisProduct.getSerialNumber() + : thisProduct.getProductUniqueIndex(); + logger.trace("update(): updating by UniqueIndex {}.", uniqueIndex); + existingProductsByUniqueIndex.replace(uniqueIndex, thisProduct); + modifiedProductsByUniqueIndex.put(uniqueIndex, thisProduct); + } + logger.trace("update() successfully finished (dirty={}).", dirty); + return true; + } + + public boolean update(VeluxProduct currentProduct) { + logger.trace("update(currentProduct={}) called.", currentProduct); + return update(currentProduct.getBridgeProductIndex(), currentProduct.getState(), + currentProduct.getCurrentPosition(), currentProduct.getTarget()); + } + + public VeluxProduct get(String productUniqueIndexOrSerialNumber) { + logger.trace("get({}) called.", productUniqueIndexOrSerialNumber); + if (!isRegistered(productUniqueIndexOrSerialNumber)) { + return VeluxProduct.UNKNOWN; + } + return existingProductsByUniqueIndex.get(productUniqueIndexOrSerialNumber); + } + + public VeluxProduct get(ProductBridgeIndex bridgeProductIndex) { + logger.trace("get({}) called.", bridgeProductIndex); + if (!isRegistered(bridgeProductIndex)) { + return VeluxProduct.UNKNOWN; + } + return existingProductsByUniqueIndex.get(bridgeIndexToSerialNumber.get(bridgeProductIndex.toInt())); + } + + public VeluxProduct[] values() { + return existingProductsByUniqueIndex.values().toArray(new VeluxProduct[0]); + } + + public VeluxProduct[] valuesOfModified() { + return modifiedProductsByUniqueIndex.values().toArray(new VeluxProduct[0]); + } + + public int getNoMembers() { + logger.trace("getNoMembers() returns {}.", memberCount); + return memberCount; + } + + public boolean isDirty() { + logger.trace("isDirty() returns {}.", dirty); + return dirty; + } + + public void resetDirtyFlag() { + logger.trace("resetDirtyFlag() called."); + modifiedProductsByUniqueIndex = new ConcurrentHashMap(); + dirty = false; + } + + public String toString(boolean showSummary, String delimiter) { + StringBuilder sb = new StringBuilder(); + + if (showSummary) { + sb.append(memberCount).append(" members: "); + } + for (VeluxProduct product : this.values()) { + sb.append(product).append(delimiter); + } + if (sb.lastIndexOf(delimiter) > 0) { + sb.deleteCharAt(sb.lastIndexOf(delimiter)); + } + return sb.toString(); + } + + @Override + public String toString() { + return toString(true, VeluxBindingConstants.OUTPUT_VALUE_SEPARATOR); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java new file mode 100644 index 0000000000000..040c0b0295e02 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxExistingScenes.java @@ -0,0 +1,117 @@ +/** + * 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.velux.internal.things; + +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.openhab.binding.velux.internal.things.VeluxScene.SceneName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Combined set of scene informations provided by the Velux bridge, + * which can be used for later interactions. + *

+ * The following class access methods exist: + *

    + *
  • {@link VeluxExistingScenes#isRegistered} for querying existence of a {@link VeluxScene},
  • + *
  • {@link VeluxExistingScenes#register} for storing a {@link VeluxScene},
  • + *
  • {@link VeluxExistingScenes#get} for retrieval of a {@link VeluxScene},
  • + *
  • {@link VeluxExistingScenes#values} for retrieval of all {@link VeluxScene}s,
  • + *
  • {@link VeluxExistingScenes#getNoMembers} for retrieval of the number of all {@link VeluxScene}s,
  • + *
  • {@link VeluxExistingScenes#toString} for a descriptive string representation.
  • + *
+ * + * @see VeluxScene + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxExistingScenes { + private final Logger logger = LoggerFactory.getLogger(VeluxExistingScenes.class); + + // Type definitions, class-internal variables + + private ConcurrentHashMap existingScenesBySceneName; + private int memberCount; + + // Constructor methods + + public VeluxExistingScenes() { + existingScenesBySceneName = new ConcurrentHashMap(); + memberCount = 0; + logger.trace("VeluxExistingScenes(constructor) done."); + } + + // Class access methods + + public boolean isRegistered(SceneName sceneName) { + logger.trace("isRegistered({}) returns {}.", sceneName, + existingScenesBySceneName.containsKey(sceneName.toString()) ? "true" : "false"); + return existingScenesBySceneName.containsKey(sceneName.toString()); + } + + public boolean isRegistered(VeluxScene scene) { + return isRegistered(scene.getName()); + } + + public boolean register(VeluxScene newScene) { + if (isRegistered(newScene)) { + logger.trace("register() ignoring scene {} as already known.", newScene); + return false; + } + logger.trace("register() registering new scene {}.", newScene); + existingScenesBySceneName.put(newScene.getName().toString(), newScene); + memberCount++; + return true; + } + + public VeluxScene get(SceneName sceneName) { + logger.trace("get({}) called.", sceneName); + if (!isRegistered(sceneName)) { + return VeluxScene.UNKNOWN; + } + return existingScenesBySceneName.get(sceneName.toString()); + } + + public VeluxScene[] values() { + return existingScenesBySceneName.values().toArray(new VeluxScene[0]); + } + + public int getNoMembers() { + logger.trace("getNoMembers() returns {}.", memberCount); + return memberCount; + } + + public String toString(boolean showSummary, String delimiter) { + StringBuilder sb = new StringBuilder(); + + if (showSummary) { + sb.append(memberCount).append(" members: "); + } + for (VeluxScene scene : this.values()) { + sb.append(scene.toString()).append(delimiter); + } + if (sb.lastIndexOf(delimiter) > 0) { + sb.deleteCharAt(sb.lastIndexOf(delimiter)); + } + return sb.toString(); + } + + @Override + public String toString() { + return toString(true, VeluxBindingConstants.OUTPUT_VALUE_SEPARATOR); + } +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwFirmware.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwFirmware.java new file mode 100644 index 0000000000000..9b1ffe07f5641 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwFirmware.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.velux.internal.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product representation. + *

+ * Combined set of information describing a single Velux product. + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxGwFirmware { + private final Logger logger = LoggerFactory.getLogger(VeluxGwFirmware.class); + + // Class internal + + private String firmwareVersion = VeluxBindingConstants.UNKNOWN; + + // Constructor + + public VeluxGwFirmware(String firmwareVersion) { + logger.trace("VeluxGwFirmware() created."); + + this.firmwareVersion = firmwareVersion; + } + + public VeluxGwFirmware() { + logger.trace("VeluxGwFirmware(dummy) created."); + } + + // Class access methods + + public String getfirmwareVersion() { + return this.firmwareVersion; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwLAN.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwLAN.java new file mode 100644 index 0000000000000..1a34111901067 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwLAN.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.velux.internal.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product representation. + *

+ * Combined set of information describing a single Velux product. + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxGwLAN { + private final Logger logger = LoggerFactory.getLogger(VeluxGwLAN.class); + + // Class internal + + private String ipAddress = VeluxBindingConstants.UNKNOWN; + private String subnetMask = VeluxBindingConstants.UNKNOWN; + private String defaultGW = VeluxBindingConstants.UNKNOWN; + private boolean enabledDHCP = false; + + // Constructor + + public VeluxGwLAN(String ipAddress, String subnetMask, String defaultGW, boolean enabledDHCP) { + logger.trace("VeluxGwLAN() created."); + + this.ipAddress = ipAddress; + this.subnetMask = subnetMask; + this.defaultGW = defaultGW; + this.enabledDHCP = enabledDHCP; + } + + // Class access methods + + public String getIpAddress() { + logger.trace("getIpAddress() returns {}.", this.ipAddress); + return this.ipAddress; + } + + public String getSubnetMask() { + logger.trace("getSubnetMask() returns {}.", this.subnetMask); + return this.subnetMask; + } + + public String getDefaultGW() { + logger.trace("getDefaultGW() returns {}.", this.defaultGW); + return this.defaultGW; + } + + public boolean getDHCP() { + logger.trace("getDHCP() returns {}.", this.enabledDHCP ? "enabled" : "disabled"); + return this.enabledDHCP; + } + + @Override + public String toString() { + return String.format("ip %s, nm %s, gw %s, DHCP %s", this.ipAddress, this.subnetMask, this.defaultGW, + this.enabledDHCP ? "enabled" : "disabled"); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwState.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwState.java new file mode 100644 index 0000000000000..fc7a7455dc887 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwState.java @@ -0,0 +1,175 @@ +/** + * 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.velux.internal.things; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product characteristics: GatewayState Description. + *

+ * See KLF200 + * GatewayState value Description + *

+ * Methods in handle this type of information: + * * + *

    + *
  • {@link #VeluxGwState(byte, byte)} to convert a value into the complete characteristic.
  • + *
  • {@link #toString()} to retrieve the human-readable description of the complete characteristic.
  • + *
  • {@link #toDescription()} to retrieve the verbose human-readable description of the complete characteristic..
  • + *
  • {@link #getSubState()} to retrieve a Velux value as part of the complete characteristic.
  • + *
+ * In addition, the subtype are accessible via: + *
    + *
  • {@link VeluxGatewayState#getStateValue()} to retrieve the Velux value of the characteristic.
  • + *
  • {@link VeluxGatewayState#getStateDescription()} to retrieve the human-readable description of the + * characteristic.
  • + *
  • {@link VeluxGatewayState#get(int)} to convert a value into the characteristic.
  • + *
  • {@link VeluxGatewaySubState#getStateValue()} to retrieve the Velux value of the characteristic.
  • + *
  • {@link VeluxGatewaySubState#getStateDescription()} to retrieve the human-readable description of the + * characteristic.
  • + *
  • {@link VeluxGatewaySubState#get(int)} to convert a value into the characteristic.
  • + *
+ * + * @see VeluxKLFAPI + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxGwState { + private final Logger logger = LoggerFactory.getLogger(VeluxGwState.class); + + // Type definition + + public enum VeluxGatewayState { + UNDEFTYPE(-1, "Unkwown state."), + GW_S_TEST(0, "Test mode."), + GW_S_GWM_EMPTY(1, "Gateway mode, no actuator nodes in the system table."), + GW_S_GWM(2, "Gateway mode, with one or more actuator nodes in the system table."), + GW_S_BM_UNCONFIG(3, "Beacon mode, not configured by a remote controller."), + GW_S_BM(4, "Beacon mode, has been configured by a remote controller."), + GW_STATE_RESERVED(255, "Reserved"); + + // Class internal + + private int stateValue; + private String stateDescription; + + // Reverse-lookup map for getting a VeluxProductVelocity from a value. + private static final Map LOOKUPTYPEID2ENUM = Stream.of(VeluxGatewayState.values()) + .collect(Collectors.toMap(VeluxGatewayState::getStateValue, Function.identity())); + + // Constructor + + private VeluxGatewayState(int stateValue, String stateDescription) { + this.stateValue = stateValue; + this.stateDescription = stateDescription; + } + + // Class access methods + + public int getStateValue() { + return stateValue; + } + + public String getStateDescription() { + return stateDescription; + } + + public static VeluxGatewayState get(int stateValue) { + return LOOKUPTYPEID2ENUM.getOrDefault(stateValue, VeluxGatewayState.UNDEFTYPE); + } + } + + public enum VeluxGatewaySubState { + UNDEFTYPE(-1, "Unknown state."), + GW_SS_IDLE(0, "Idle state."), + GW_SS_P1(1, "Performing task in Configuration Service handler."), + GW_SS_P2(2, "Performing Scene Configuration."), + GW_SS_P3(3, "Performing Information Service Configuration."), + GW_SS_P4(4, "Performing Contact input Configuration."), + GW_SS_PFF(88, "Reserved"); + + // Class internal + + private int stateValue; + private String stateDescription; + + // Reverse-lookup map for getting a VeluxGatewayState from an TypeId + private static final Map LOOKUPTYPEID2ENUM = Stream + .of(VeluxGatewaySubState.values()) + .collect(Collectors.toMap(VeluxGatewaySubState::getStateValue, Function.identity())); + + // Constructor + + private VeluxGatewaySubState(int stateValue, String stateDescription) { + this.stateValue = stateValue; + this.stateDescription = stateDescription; + } + + // Class access methods + + public int getStateValue() { + return stateValue; + } + + public String getStateDescription() { + return stateDescription; + } + + public static VeluxGatewaySubState get(int stateValue) { + if (LOOKUPTYPEID2ENUM.containsKey(stateValue)) { + return LOOKUPTYPEID2ENUM.get(stateValue); + } else { + return VeluxGatewaySubState.UNDEFTYPE; + } + } + } + + // Class internal + + private VeluxGatewayState gwState; + private VeluxGatewaySubState gwSubState; + + // Constructor + + public VeluxGwState(byte stateValue, byte subStateValue) { + logger.trace("VeluxGwState() created."); + + this.gwState = VeluxGatewayState.get(stateValue); + this.gwSubState = VeluxGatewaySubState.get(subStateValue); + } + + // Class access methods + + @Override + public String toString() { + return this.gwState.name().concat("/").concat(this.gwSubState.name()); + } + + public String toDescription() { + return this.gwState.getStateDescription().concat(", ").concat(this.gwSubState.getStateDescription()); + } + + public byte getSubState() { + return (byte) this.gwSubState.getStateValue(); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwWLAN.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwWLAN.java new file mode 100644 index 0000000000000..3effaa76bf8ca --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxGwWLAN.java @@ -0,0 +1,59 @@ +/** + * 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.velux.internal.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product representation. + *

+ * Combined set of information describing a single Velux product. + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxGwWLAN { + private final Logger logger = LoggerFactory.getLogger(VeluxGwWLAN.class); + + // Class internal + + private String serviceSetID = VeluxBindingConstants.UNKNOWN; + private String password = VeluxBindingConstants.UNKNOWN; + + // Constructor + + public VeluxGwWLAN(String serviceSetID, String password) { + logger.trace("VeluxGwWLAN() created."); + + this.serviceSetID = serviceSetID; + this.password = password; + } + + public VeluxGwWLAN() { + logger.trace("VeluxGwWLAN(dummy) created."); + } + + // Class access methods + + public String getSSID() { + return this.serviceSetID; + } + + public String getPassword() { + return this.password; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxKLFAPI.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxKLFAPI.java new file mode 100644 index 0000000000000..7662a4e783320 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxKLFAPI.java @@ -0,0 +1,358 @@ +/** + * 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.velux.internal.things; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * + * The {@link VeluxKLFAPI} class defines common KLF200 API constants, which are + * used across the whole binding. + *

+ * It provides the Enumeration of available API message identifiers as well as + * constants which describe the KLF200 API restrictions. + *

+ * Classes/Enumeration available: + *

    + *
  • Enumeration {@link Command} provides command name, coding and description.
  • + *
  • Class {@link CommandName} to handle symbolic API names.
  • + *
  • Class {@link CommandNumber} to handle API code.
  • + *
+ * Constants available: + *
    + *
  • {@link #KLF_SYSTEMTABLE_MAX} provides limits of the System table.
  • + *
+ *

+ * + * @author Guenther Schreiner - Initial contribution. + */ +@NonNullByDefault +public class VeluxKLFAPI { + + // Constants + + /** + * System table index parameter - an be a number from 0 to 203. + * + * See KLF200 + * System table + */ + public static final int KLF_SYSTEMTABLE_MAX = 203; + + // Type definitions + + /** + * Handle symbolic names of the {@link VeluxKLFAPI}. + *

+ * Methods available: + *

    + *
  • Constructor {@link CommandName} by String.
  • + *
  • Method {@link toString} to return a String.
  • + *
+ */ + @NonNullByDefault + public static class CommandName { + private String name; + + CommandName(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + + } + + /** + * Handle API codings of the {@link VeluxKLFAPI}. + *

+ * Methods available: + *

    + *
  • CommandNumber {@link CommandName} by short.
  • + *
  • Method {@link toShort} to return a short.
  • + *
  • Method {@link toString} to return a well-formatted String.
  • + *
+ */ + @NonNullByDefault + public static class CommandNumber { + private short commandNumber; + + public CommandNumber(short commandNumber) { + this.commandNumber = commandNumber; + } + + public short toShort() { + return commandNumber; + } + + @Override + public String toString() { + return "0x" + Integer.toHexString(new Short(commandNumber).intValue()); + } + } + + /** + * Enumeration of complete API as definition of a + * List of Gateway commands. + *

+ * See Appendix + * 3: List of Gateway commands + *

+ * Methods available: + *

    + *
  • Constructor {@link Command} by String.
  • + *
  • Method {@link getCommand} to return a {@link CommandNumber}.
  • + *
  • Method {@link getDescription} to return a description as String.
  • + *
  • Method {@link get} to return a {@link Command} based on the given int.
  • + *
+ */ + public enum Command { + // Special item: unrecognized command + UNDEFTYPE((short) -1, "Unknown command."), + // Special item: Shutdown of the connection + GW_OPENHAB_CLOSE((short) -2, "openHAB connection shutdown command."), + // Special item: Shutdown of the connection + GW_OPENHAB_RECEIVEONLY((short) -3, "openHAB receive command."), + // Velux specific commands + GW_ERROR_NTF((short) 0x0000, "Provides information on what triggered the error."), + GW_REBOOT_REQ((short) 0x0001, "Request gateway to reboot."), + GW_REBOOT_CFM((short) 0x0002, "Acknowledge to GW_REBOOT_REQ command."), + GW_SET_FACTORY_DEFAULT_REQ((short) 0x0003, + "Request gateway to clear system table, scene table and set Ethernet settings to factory default. Gateway will reboot."), + GW_SET_FACTORY_DEFAULT_CFM((short) 0x0004, "Acknowledge to GW_SET_FACTORY_DEFAULT_REQ command."), + GW_GET_VERSION_REQ((short) 0x0008, "Request version information."), + GW_GET_VERSION_CFM((short) 0x0009, "Acknowledge to GW_GET_VERSION_REQ command."), + GW_GET_PROTOCOL_VERSION_REQ((short) 0x000A, "Request KLF 200 API protocol version."), + GW_GET_PROTOCOL_VERSION_CFM((short) 0x000B, "Acknowledge to GW_GET_PROTOCOL_VERSION_REQ command."), + GW_GET_STATE_REQ((short) 0x000C, "Request the state of the gateway"), + GW_GET_STATE_CFM((short) 0x000D, "Acknowledge to GW_GET_STATE_REQ command."), + + GW_LEAVE_LEARN_STATE_REQ((short) 0x000E, "Request gateway to leave learn state."), + GW_LEAVE_LEARN_STATE_CFM((short) 0x000F, "Acknowledge to GW_LEAVE_LEARN_STATE_REQ command."), + GW_GET_NETWORK_SETUP_REQ((short) 0x00E0, "Request network parameters."), + GW_GET_NETWORK_SETUP_CFM((short) 0x00E1, "Acknowledge to GW_GET_NETWORK_SETUP_REQ."), + GW_SET_NETWORK_SETUP_REQ((short) 0x00E2, "Set network parameters."), + GW_SET_NETWORK_SETUP_CFM((short) 0x00E3, "Acknowledge to GW_SET_NETWORK_SETUP_REQ."), + + GW_CS_GET_SYSTEMTABLE_DATA_REQ((short) 0x0100, "Request a list of nodes in the gateways system table."), + GW_CS_GET_SYSTEMTABLE_DATA_CFM((short) 0x0101, "Acknowledge to GW_CS_GET_SYSTEMTABLE_DATA_REQ"), + GW_CS_GET_SYSTEMTABLE_DATA_NTF((short) 0x0102, + "Acknowledge to GW_CS_GET_SYSTEM_TABLE_DATA_REQList of nodes in the gateways systemtable."), + GW_CS_DISCOVER_NODES_REQ((short) 0x0103, "Start CS DiscoverNodes macro in KLF200."), + GW_CS_DISCOVER_NODES_CFM((short) 0x0104, "Acknowledge to GW_CS_DISCOVER_NODES_REQ command."), + GW_CS_DISCOVER_NODES_NTF((short) 0x0105, "Acknowledge to GW_CS_DISCOVER_NODES_REQ command."), + GW_CS_REMOVE_NODES_REQ((short) 0x0106, "Remove one or more nodes in the systemtable."), + GW_CS_REMOVE_NODES_CFM((short) 0x0107, "Acknowledge to GW_CS_REMOVE_NODES_REQ."), + GW_CS_VIRGIN_STATE_REQ((short) 0x0108, "Clear systemtable and delete system key."), + GW_CS_VIRGIN_STATE_CFM((short) 0x0109, "Acknowledge to GW_CS_VIRGIN_STATE_REQ."), + GW_CS_CONTROLLER_COPY_REQ((short) 0x010A, + "Setup KLF200 to get or give a system to or from another io-homecontrol® remote control. By a system means all nodes in the systemtable and the system key."), + GW_CS_CONTROLLER_COPY_CFM((short) 0x010B, "Acknowledge to GW_CS_CONTROLLER_COPY_REQ."), + GW_CS_CONTROLLER_COPY_NTF((short) 0x010C, "Acknowledge to GW_CS_CONTROLLER_COPY_REQ."), + GW_CS_CONTROLLER_COPY_CANCEL_NTF((short) 0x010D, "Cancellation of system copy to other controllers."), + GW_CS_RECEIVE_KEY_REQ((short) 0x010E, "Receive system key from another controller."), + GW_CS_RECEIVE_KEY_CFM((short) 0x010F, "Acknowledge to GW_CS_RECEIVE_KEY_REQ."), + GW_CS_RECEIVE_KEY_NTF((short) 0x0110, "Acknowledge to GW_CS_RECEIVE_KEY_REQ with status."), + GW_CS_PGC_JOB_NTF((short) 0x0111, + "Information on Product Generic Configuration job initiated by press on PGC button."), + GW_CS_SYSTEM_TABLE_UPDATE_NTF((short) 0x0112, + "Broadcasted to all clients and gives information about added and removed actuator nodes in system table."), + GW_CS_GENERATE_NEW_KEY_REQ((short) 0x0113, "Generate new system key and update actuators in systemtable."), + GW_CS_GENERATE_NEW_KEY_CFM((short) 0x0114, "Acknowledge to GW_CS_GENERATE_NEW_KEY_REQ."), + GW_CS_GENERATE_NEW_KEY_NTF((short) 0x0115, "Acknowledge to GW_CS_GENERATE_NEW_KEY_REQ with status."), + GW_CS_REPAIR_KEY_REQ((short) 0x0116, "Update key in actuators holding an old key."), + GW_CS_REPAIR_KEY_CFM((short) 0x0117, "Acknowledge to GW_CS_REPAIR_KEY_REQ."), + GW_CS_REPAIR_KEY_NTF((short) 0x0118, "Acknowledge to GW_CS_REPAIR_KEY_REQ with status."), + GW_CS_ACTIVATE_CONFIGURATION_MODE_REQ((short) 0x0119, + "Request one or more actuator to open for configuration."), + GW_CS_ACTIVATE_CONFIGURATION_MODE_CFM((short) 0x011A, "Acknowledge to GW_CS_ACTIVATE_CONFIGURATION_MODE_REQ."), + + GW_GET_NODE_INFORMATION_REQ((short) 0x0200, "Request extended information of one specific actuator node."), + GW_GET_NODE_INFORMATION_CFM((short) 0x0201, "Acknowledge to GW_GET_NODE_INFORMATION_REQ."), + GW_GET_NODE_INFORMATION_NTF((short) 0x0210, "Acknowledge to GW_GET_NODE_INFORMATION_REQ."), + GW_GET_ALL_NODES_INFORMATION_REQ((short) 0x0202, "Request extended information of all nodes."), + GW_GET_ALL_NODES_INFORMATION_CFM((short) 0x0203, "Acknowledge to GW_GET_ALL_NODES_INFORMATION_REQ"), + GW_GET_ALL_NODES_INFORMATION_NTF((short) 0x0204, + "Acknowledge to GW_GET_ALL_NODES_INFORMATION_REQ. Holds node information"), + GW_GET_ALL_NODES_INFORMATION_FINISHED_NTF((short) 0x0205, + "Acknowledge to GW_GET_ALL_NODES_INFORMATION_REQ. No more nodes."), + GW_SET_NODE_VARIATION_REQ((short) 0x0206, "Set node variation."), + GW_SET_NODE_VARIATION_CFM((short) 0x0207, "Acknowledge to GW_SET_NODE_VARIATION_REQ."), + GW_SET_NODE_NAME_REQ((short) 0x0208, "Set node name."), + GW_SET_NODE_NAME_CFM((short) 0x0209, "Acknowledge to GW_SET_NODE_NAME_REQ."), + GW_SET_NODE_VELOCITY_REQ((short) 0x020A, "Set node velocity."), + GW_SET_NODE_VELOCITY_CFM((short) 0x020B, "Acknowledge to GW_SET_NODE_VELOCITY_REQ."), + GW_NODE_INFORMATION_CHANGED_NTF((short) 0x020C, "Information has been updated."), + GW_NODE_STATE_POSITION_CHANGED_NTF((short) 0x0211, "Information has been updated."), + GW_SET_NODE_ORDER_AND_PLACEMENT_REQ((short) 0x020D, "Set search order and room placement."), + GW_SET_NODE_ORDER_AND_PLACEMENT_CFM((short) 0x020E, "Acknowledge to GW_SET_NODE_ORDER_AND_PLACEMENT_REQ."), + + GW_GET_GROUP_INFORMATION_REQ((short) 0x0220, "Request information about all defined groups."), + GW_GET_GROUP_INFORMATION_CFM((short) 0x0221, "Acknowledge to GW_GET_GROUP_INFORMATION_REQ."), + GW_GET_GROUP_INFORMATION_NTF((short) 0x0230, "Acknowledge to GW_GET_NODE_INFORMATION_REQ."), + GW_SET_GROUP_INFORMATION_REQ((short) 0x0222, "Change an existing group."), + GW_SET_GROUP_INFORMATION_CFM((short) 0x0223, "Acknowledge to GW_SET_GROUP_INFORMATION_REQ."), + GW_GROUP_INFORMATION_CHANGED_NTF((short) 0x0224, + "Broadcast to all, about group information of a group has been changed."), + GW_DELETE_GROUP_REQ((short) 0x0225, "Delete a group."), + GW_DELETE_GROUP_CFM((short) 0x0226, "Acknowledge to GW_DELETE_GROUP_INFORMATION_REQ."), + GW_NEW_GROUP_REQ((short) 0x0227, "Request new group to be created."), + GW_NEW_GROUP_CFM((short) 0x0228, ""), + GW_GET_ALL_GROUPS_INFORMATION_REQ((short) 0x0229, "Request information about all defined groups."), + GW_GET_ALL_GROUPS_INFORMATION_CFM((short) 0x022A, "Acknowledge to GW_GET_ALL_GROUPS_INFORMATION_REQ."), + GW_GET_ALL_GROUPS_INFORMATION_NTF((short) 0x022B, "Acknowledge to GW_GET_ALL_GROUPS_INFORMATION_REQ."), + GW_GET_ALL_GROUPS_INFORMATION_FINISHED_NTF((short) 0x022C, "Acknowledge to GW_GET_ALL_GROUPS_INFORMATION_REQ."), + GW_GROUP_DELETED_NTF((short) 0x022D, + "GW_GROUP_DELETED_NTF is broadcasted to all, when a group has been removed."), + GW_HOUSE_STATUS_MONITOR_ENABLE_REQ((short) 0x0240, "Enable house status monitor."), + GW_HOUSE_STATUS_MONITOR_ENABLE_CFM((short) 0x0241, "Acknowledge to GW_HOUSE_STATUS_MONITOR_ENABLE_REQ."), + GW_HOUSE_STATUS_MONITOR_DISABLE_REQ((short) 0x0242, "Disable house status monitor."), + GW_HOUSE_STATUS_MONITOR_DISABLE_CFM((short) 0x0243, "Acknowledge to GW_HOUSE_STATUS_MONITOR_DISABLE_REQ."), + + GW_COMMAND_SEND_REQ((short) 0x0300, "Send activating command direct to one or more io-homecontrol® nodes."), + GW_COMMAND_SEND_CFM((short) 0x0301, "Acknowledge to GW_COMMAND_SEND_REQ."), + GW_COMMAND_RUN_STATUS_NTF((short) 0x0302, "Gives run status for io-homecontrol® node."), + GW_COMMAND_REMAINING_TIME_NTF((short) 0x0303, + "Gives remaining time before io-homecontrol® node enter target position."), + GW_SESSION_FINISHED_NTF((short) 0x0304, + "Command send, Status request, Wink, Mode or Stop session is finished."), + GW_STATUS_REQUEST_REQ((short) 0x0305, "Get status request from one or more io-homecontrol® nodes."), + GW_STATUS_REQUEST_CFM((short) 0x0306, "Acknowledge to GW_STATUS_REQUEST_REQ."), + GW_STATUS_REQUEST_NTF((short) 0x0307, + "Acknowledge to GW_STATUS_REQUEST_REQ. Status request from one or more io-homecontrol® nodes."), + GW_WINK_SEND_REQ((short) 0x0308, "Request from one or more io-homecontrol® nodes to Wink."), + GW_WINK_SEND_CFM((short) 0x0309, "Acknowledge to GW_WINK_SEND_REQ"), + GW_WINK_SEND_NTF((short) 0x030A, "Status info for performed wink request."), + + GW_SET_LIMITATION_REQ((short) 0x0310, "Set a parameter limitation in an actuator."), + GW_SET_LIMITATION_CFM((short) 0x0311, "Acknowledge to GW_SET_LIMITATION_REQ."), + GW_GET_LIMITATION_STATUS_REQ((short) 0x0312, "Get parameter limitation in an actuator."), + GW_GET_LIMITATION_STATUS_CFM((short) 0x0313, "Acknowledge to GW_GET_LIMITATION_STATUS_REQ."), + GW_LIMITATION_STATUS_NTF((short) 0x0314, "Hold information about limitation."), + GW_MODE_SEND_REQ((short) 0x0320, "Send Activate Mode to one or more io-homecontrol® nodes."), + GW_MODE_SEND_CFM((short) 0x0321, "Acknowledge to GW_MODE_SEND_REQ"), + GW_MODE_SEND_NTF((short) 0x0322, "Notify with Mode activation info."), + + GW_INITIALIZE_SCENE_REQ((short) 0x0400, "Prepare gateway to record a scene."), + GW_INITIALIZE_SCENE_CFM((short) 0x0401, "Acknowledge to GW_INITIALIZE_SCENE_REQ."), + GW_INITIALIZE_SCENE_NTF((short) 0x0402, "Acknowledge to GW_INITIALIZE_SCENE_REQ."), + GW_INITIALIZE_SCENE_CANCEL_REQ((short) 0x0403, "Cancel record scene process."), + GW_INITIALIZE_SCENE_CANCEL_CFM((short) 0x0404, "Acknowledge to GW_INITIALIZE_SCENE_CANCEL_REQ command."), + GW_RECORD_SCENE_REQ((short) 0x0405, "Store actuator positions changes since GW_INITIALIZE_SCENE, as a scene."), + GW_RECORD_SCENE_CFM((short) 0x0406, "Acknowledge to GW_RECORD_SCENE_REQ."), + GW_RECORD_SCENE_NTF((short) 0x0407, "Acknowledge to GW_RECORD_SCENE_REQ."), + GW_DELETE_SCENE_REQ((short) 0x0408, "Delete a recorded scene."), + GW_DELETE_SCENE_CFM((short) 0x0409, "Acknowledge to GW_DELETE_SCENE_REQ."), + GW_RENAME_SCENE_REQ((short) 0x040A, "Request a scene to be renamed."), + GW_RENAME_SCENE_CFM((short) 0x040B, "Acknowledge to GW_RENAME_SCENE_REQ."), + GW_GET_SCENE_LIST_REQ((short) 0x040C, "Request a list of scenes."), + GW_GET_SCENE_LIST_CFM((short) 0x040D, "Acknowledge to GW_GET_SCENE_LIST."), + GW_GET_SCENE_LIST_NTF((short) 0x040E, "Acknowledge to GW_GET_SCENE_LIST."), + GW_GET_SCENE_INFOAMATION_REQ((short) 0x040F, "Request extended information for one given scene."), + GW_GET_SCENE_INFOAMATION_CFM((short) 0x0410, "Acknowledge to GW_GET_SCENE_INFOAMATION_REQ."), + GW_GET_SCENE_INFOAMATION_NTF((short) 0x0411, "Acknowledge to GW_GET_SCENE_INFOAMATION_REQ."), + GW_ACTIVATE_SCENE_REQ((short) 0x0412, "Request gateway to enter a scene."), + GW_ACTIVATE_SCENE_CFM((short) 0x0413, "Acknowledge to GW_ACTIVATE_SCENE_REQ."), + GW_STOP_SCENE_REQ((short) 0x0415, "Request all nodes in a given scene to stop at their current position."), + GW_STOP_SCENE_CFM((short) 0x0416, "Acknowledge to GW_STOP_SCENE_REQ."), + GW_SCENE_INFORMATION_CHANGED_NTF((short) 0x0419, "A scene has either been changed or removed."), + + GW_ACTIVATE_PRODUCTGROUP_REQ((short) 0x0447, "Activate a product group in a given direction."), + GW_ACTIVATE_PRODUCTGROUP_CFM((short) 0x0448, "Acknowledge to GW_ACTIVATE_PRODUCTGROUP_REQ."), + GW_ACTIVATE_PRODUCTGROUP_NTF((short) 0x0449, "Acknowledge to GW_ACTIVATE_PRODUCTGROUP_REQ."), + + GW_GET_CONTACT_INPUT_LINK_LIST_REQ((short) 0x0460, + "Get list of assignments to all Contact Input to scene or product group."), + GW_GET_CONTACT_INPUT_LINK_LIST_CFM((short) 0x0461, "Acknowledge to GW_GET_CONTACT_INPUT_LINK_LIST_REQ."), + GW_SET_CONTACT_INPUT_LINK_REQ((short) 0x0462, "Set a link from a Contact Input to a scene or product group."), + GW_SET_CONTACT_INPUT_LINK_CFM((short) 0x0463, "Acknowledge to GW_SET_CONTACT_INPUT_LINK_REQ."), + GW_REMOVE_CONTACT_INPUT_LINK_REQ((short) 0x0464, "Remove a link from a Contact Input to a scene."), + GW_REMOVE_CONTACT_INPUT_LINK_CFM((short) 0x0465, "Acknowledge to GW_REMOVE_CONTACT_INPUT_LINK_REQ."), + + GW_GET_ACTIVATION_LOG_HEADER_REQ((short) 0x0500, "Request header from activation log."), + GW_GET_ACTIVATION_LOG_HEADER_CFM((short) 0x0501, "Confirm header from activation log."), + GW_CLEAR_ACTIVATION_LOG_REQ((short) 0x0502, "Request clear all data in activation log."), + GW_CLEAR_ACTIVATION_LOG_CFM((short) 0x0503, "Confirm clear all data in activation log."), + GW_GET_ACTIVATION_LOG_LINE_REQ((short) 0x0504, "Request line from activation log."), + GW_GET_ACTIVATION_LOG_LINE_CFM((short) 0x0505, "Confirm line from activation log."), + GW_ACTIVATION_LOG_UPDATED_NTF((short) 0x0506, "Confirm line from activation log."), + GW_GET_MULTIPLE_ACTIVATION_LOG_LINES_REQ((short) 0x0507, "Request lines from activation log."), + GW_GET_MULTIPLE_ACTIVATION_LOG_LINES_NTF((short) 0x0508, "Error log data from activation log."), + GW_GET_MULTIPLE_ACTIVATION_LOG_LINES_CFM((short) 0x0509, "Confirm lines from activation log."), + + GW_SET_UTC_REQ((short) 0x2000, "Request to set UTC time."), + GW_SET_UTC_CFM((short) 0x2001, "Acknowledge to GW_SET_UTC_REQ."), + GW_RTC_SET_TIME_ZONE_REQ((short) 0x2002, "Set time zone and daylight savings rules."), + GW_RTC_SET_TIME_ZONE_CFM((short) 0x2003, "Acknowledge to GW_RTC_SET_TIME_ZONE_REQ."), + GW_GET_LOCAL_TIME_REQ((short) 0x2004, + "Request the local time based on current time zone and daylight savings rules."), + GW_GET_LOCAL_TIME_CFM((short) 0x2005, "Acknowledge to GW_RTC_SET_TIME_ZONE_REQ."), + GW_PASSWORD_ENTER_REQ((short) 0x3000, "Enter password to authenticate request"), + GW_PASSWORD_ENTER_CFM((short) 0x3001, "Acknowledge to GW_PASSWORD_ENTER_REQ"), + GW_PASSWORD_CHANGE_REQ((short) 0x3002, "Request password change."), + GW_PASSWORD_CHANGE_CFM((short) 0x3003, "Acknowledge to GW_PASSWORD_CHANGE_REQ."), + GW_PASSWORD_CHANGE_NTF((short) 0x3004, + "Acknowledge to GW_PASSWORD_CHANGE_REQ. Broadcasted to all connected clients."), + + ; + + // Class internal + + private CommandNumber command; + private String description; + + // Reverse-lookup map for getting a Command from an TypeId + private static final Map LOOKUPTYPEID2ENUM = Stream.of(Command.values()) + .collect(Collectors.toMap(Command::getShort, Function.identity())); + + // Constructor + + private Command(short typeId, String description) { + this.command = new CommandNumber(typeId); + this.description = description; + } + + // Class access methods + + public CommandNumber getCommand() { + return command; + } + + public short getShort() { + return command.toShort(); + } + + public String getDescription() { + return description; + } + + public static Command get(short thisTypeId) { + if (LOOKUPTYPEID2ENUM.containsKey(thisTypeId)) { + return LOOKUPTYPEID2ENUM.get(thisTypeId); + } else { + return Command.UNDEFTYPE; + } + } + + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProduct.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProduct.java new file mode 100644 index 0000000000000..41943610441a5 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProduct.java @@ -0,0 +1,338 @@ +/** + * 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.velux.internal.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product representation. + *

+ * Combined set of information describing a single Velux product. + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxProduct { + private final Logger logger = LoggerFactory.getLogger(VeluxProduct.class); + + // Public definition + + public static final VeluxProduct UNKNOWN = new VeluxProduct(); + + // Type definitions + + @NonNullByDefault + public static class ProductBridgeIndex { + + // Public definition + public static final ProductBridgeIndex UNKNOWN = new ProductBridgeIndex(0); + + // Class internal + private int id; + + // Constructor + public ProductBridgeIndex(int id) { + this.id = id; + } + + // Class access methods + public int toInt() { + return id; + } + + @Override + public String toString() { + return Integer.toString(id); + } + } + + // Class internal + + private VeluxProductName name; + private VeluxProductType typeId; + private ProductBridgeIndex bridgeProductIndex; + + private boolean v2 = false; + private int order = 0; + private int placement = 0; + private int velocity = 0; + private int variation = 0; + private int powerMode = 0; + private String serialNumber = VeluxProductSerialNo.UNKNOWN; + private int state = 0; + private int currentPosition = 0; + private int target = 0; + private int remainingTime = 0; + private int timeStamp = 0; + + // Constructor + + /** + * Constructor + * + * just for the dummy VeluxProduct. + */ + public VeluxProduct() { + logger.trace("VeluxProduct() created."); + this.name = VeluxProductName.UNKNOWN; + this.typeId = VeluxProductType.UNDEFTYPE; + this.bridgeProductIndex = ProductBridgeIndex.UNKNOWN; + } + + /** + * Constructor + * + * @param name This field Name holds the name of the actuator, ex. “Window 1”. This field is 64 bytes + * long, formatted as UTF-8 characters. + * @param typeId This field indicates the node type, ex. Window, Roller shutter, Light etc. + * @param bridgeProductIndex NodeID is an Actuator index in the system table, to get information from. It must be a + * value from 0 to 199. + */ + public VeluxProduct(VeluxProductName name, VeluxProductType typeId, ProductBridgeIndex bridgeProductIndex) { + logger.trace("VeluxProduct(v1,name={}) created.", name.toString()); + this.name = name; + this.typeId = typeId; + this.bridgeProductIndex = bridgeProductIndex; + } + + /** + * Constructor + * + * @param name This field Name holds the name of the actuator, ex. “Window 1”. This field is 64 bytes + * long, formatted as UTF-8 characters. + * @param typeId This field indicates the node type, ex. Window, Roller shutter, Light etc. + * @param bridgeProductIndex NodeID is an Actuator index in the system table, to get information from. It must be a + * value from 0 to 199. + * @param order Order can be used to store a sort order. The sort order is used in client end, when + * presenting a list of nodes for the user. + * @param placement Placement can be used to store a room group index or house group index number. + * @param velocity This field indicates what velocity the node is operation with. + * @param variation More detail information like top hung, kip, flat roof or sky light window. + * @param powerMode This field indicates the power mode of the node (ALWAYS_ALIVE/LOW_POWER_MODE). + * @param serialNumber This field tells the serial number of the node. This field is 8 bytes. + * @param state This field indicates the operating state of the node. + * @param currentPosition This field indicates the current position of the node. + * @param target This field indicates the target position of the current operation. + * @param remainingTime This field indicates the remaining time for a node activation in seconds. + * @param timeStamp UTC time stamp for last known position. + */ + public VeluxProduct(VeluxProductName name, VeluxProductType typeId, ProductBridgeIndex bridgeProductIndex, + int order, int placement, int velocity, int variation, int powerMode, String serialNumber, int state, + int currentPosition, int target, int remainingTime, int timeStamp) { + logger.trace("VeluxProduct(v2,name={}) created.", name.toString()); + this.name = name; + this.typeId = typeId; + this.bridgeProductIndex = bridgeProductIndex; + this.v2 = true; + this.order = order; + this.placement = placement; + this.velocity = velocity; + this.variation = variation; + this.powerMode = powerMode; + this.serialNumber = serialNumber; + this.state = state; + this.currentPosition = currentPosition; + this.target = target; + this.remainingTime = remainingTime; + this.timeStamp = timeStamp; + } + + // Utility methods + + @Override + public VeluxProduct clone() { + if (this.v2) { + return new VeluxProduct(this.name, this.typeId, this.bridgeProductIndex, this.order, this.placement, + this.velocity, this.variation, this.powerMode, this.serialNumber, this.state, this.currentPosition, + this.target, this.remainingTime, this.timeStamp); + } else { + return new VeluxProduct(this.name, this.typeId, this.bridgeProductIndex); + } + } + + // Class access methods + + /** + * Returns the name of the current product (aka actuator) for convenience as type-specific class. + * + * @return nameOfThisProduct as type {@link VeluxProductName}. + */ + public VeluxProductName getProductName() { + return this.name; + } + + /** + * Returns the type of the current product (aka actuator) for convenience as type-specific class. + * + * @return typeOfThisProduct as type {@link VeluxProductType}. + */ + public VeluxProductType getProductType() { + return this.typeId; + } + + public ProductBridgeIndex getBridgeProductIndex() { + return this.bridgeProductIndex; + } + + @Override + public String toString() { + if (this.v2) { + return String.format("Product \"%s\" / %s (bridgeIndex=%d,serial=%s,position=%04X)", this.name, this.typeId, + this.bridgeProductIndex.toInt(), this.serialNumber, this.currentPosition); + } else { + return String.format("Product \"%s\" / %s (bridgeIndex %d)", this.name, this.typeId, + this.bridgeProductIndex.toInt()); + } + } + + // Class helper methods + + public String getProductUniqueIndex() { + return this.name.toString().concat("#").concat(this.typeId.toString()); + } + + // Getter and Setter methods + + /** + * @return v2 as type boolean signals the availability of firmware version two (product) details. + */ + public boolean isV2() { + return v2; + } + + /** + * @return order as type int describes the user-oriented sort-order. + */ + public int getOrder() { + return order; + } + + /** + * @return placement as type int is used to describe a group index or house group index number. + */ + public int getPlacement() { + return placement; + } + + /** + * @return velocity as type int describes what velocity the node is operation with + */ + public int getVelocity() { + return velocity; + } + + /** + * @return variation as type int describes detail information like top hung, kip, flat roof or sky light + * window. + */ + public int getVariation() { + return variation; + } + + /** + * @return powerMode as type int is used to show the power mode of the node (ALWAYS_ALIVE/LOW_POWER_MODE). + */ + public int getPowerMode() { + return powerMode; + } + + /** + * @return serialNumber as type String is the serial number of 8 bytes length of the node. + */ + public String getSerialNumber() { + return serialNumber; + } + + /** + * @return state as type int is used to operating state of the node. + */ + public int getState() { + return state; + } + + /** + * @param newState Update the operating state of the node. + * @return modified as type boolean to signal a real modification. + */ + public boolean setState(int newState) { + if (this.state == newState) { + return false; + } else { + logger.trace("setState(name={},index={}) state {} replaced by {}.", name.toString(), + bridgeProductIndex.toInt(), this.state, newState); + this.state = newState; + return true; + } + } + + /** + * @return currentPosition as type int signals the current position of the node. + */ + public int getCurrentPosition() { + return currentPosition; + } + + /** + * @param newCurrentPosition Update the current position of the node. + * @return modified as boolean to signal a real modification. + */ + public boolean setCurrentPosition(int newCurrentPosition) { + if (this.currentPosition == newCurrentPosition) { + return false; + } else { + logger.trace("setCurrentPosition(name={},index={}) currentPosition {} replaced by {}.", name.toString(), + bridgeProductIndex.toInt(), this.currentPosition, newCurrentPosition); + this.currentPosition = newCurrentPosition; + return true; + } + } + + /** + * @return target as type int shows the target position of the current operation. + */ + public int getTarget() { + return target; + } + + /** + * @param newTarget Update the target position of the current operation. + * @return modified as boolean to signal a real modification. + */ + public boolean setTarget(int newTarget) { + if (this.target == newTarget) { + return false; + } else { + logger.trace("setCurrentPosition(name={},index={}) target {} replaced by {}.", name.toString(), + bridgeProductIndex.toInt(), this.target, newTarget); + this.target = newTarget; + return true; + } + } + + /** + * @return remainingTime as type int describes the intended remaining time of current operation. + */ + public int getRemainingTime() { + return remainingTime; + } + + /** + * @return timeStamp as type int describes the current time. + */ + public int getTimeStamp() { + return timeStamp; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductName.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductName.java new file mode 100644 index 0000000000000..9f0cbfd64389c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductName.java @@ -0,0 +1,49 @@ +/** + * 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.velux.internal.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; + +/** + * Velux product representation. + *

+ * Combined set of information describing a single Velux product. + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxProductName { + + // Public definition + + public static final VeluxProductName UNKNOWN = new VeluxProductName(VeluxBindingConstants.UNKNOWN); + + // Class internal + + private String name; + + // Constructor + + public VeluxProductName(String name) { + this.name = name; + } + + // Class access methods + + @Override + public String toString() { + return name; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductPosition.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductPosition.java new file mode 100644 index 0000000000000..8b0fda88963c4 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductPosition.java @@ -0,0 +1,174 @@ +/** + * 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.velux.internal.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product characteristics: Product Position. + *

+ * See KLF200 + * Standard Parameter definition + *

+ * Methods in handle this type of information: + *

    + *
  • {@link #VeluxProductPosition(int)} to convert a Velux value into the characteristic.
  • + *
  • {@link #VeluxProductPosition(PercentType)} to convert an openHAB value into the characteristic.
  • + *
  • {@link #VeluxProductPosition()} to convert an openHAB STOP value into the characteristic.
  • + *
  • {@link #isValid} to determine whether the characteristic has got a valid value.
  • + *
  • {@link #getPositionAsPercentType()} to convert the characteristic into an openHAB value.
  • + *
  • {@link #getPositionAsVeluxType()} to convert the characteristic into a Velux value.
  • + *
  • {@link #toString} to retrieve a human-readable description of this characteristic.
  • + *
+ * + * @see VeluxKLFAPI + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxProductPosition { + private final Logger logger = LoggerFactory.getLogger(getClass()); + + // Public definition + + public static final VeluxProductPosition UNKNOWN = new VeluxProductPosition(); + public static final int VPP_VELUX_STOP = 0xD200; + public static final int VPP_VELUX_DEFAULT = 0xD300; + public static final int VPP_VELUX_IGNORE = 0xD400; + + // Make sure that the calculation are done as non-integer + private static final float ONE = 1; + + private static final int VPP_UNKNOWN = 0; + + private static final int VPP_OPENHAB_MIN = 0; + private static final int VPP_OPENHAB_MAX = 100; + private static final int VPP_VELUX_MIN = 0x0000; + private static final int VPP_VELUX_MAX = 0xc800; + private static final int VPP_VELUX_UNKNOWN = 0xF7FF; + + private static final int VPP_VELUX_PERCENTAGE_MIN = 0xc900; + private static final int VPP_VELUX_PERCENTAGE_MAX = 0xd0d0; + + // Class internal + + private PercentType position; + private boolean isValid = false; + + // Constructor + + /** + * Creation of a Position object to specify a distinct actuator setting. + * + * @param position A position as type {@link PercentType} (between 0 and 100). + */ + public VeluxProductPosition(PercentType position) { + logger.trace("VeluxProductPosition({} as PercentType) created.", position.intValue()); + this.position = position; + this.isValid = true; + } + + /** + * Creation of a Position object to specify a distinct actuator setting. + * + * @param position A position as type {@link PercentType} (between 0 and 100). + * @param toBeInverted Flag whether the value should be handled as inverted. + */ + public VeluxProductPosition(PercentType position, boolean toBeInverted) { + this(toBeInverted ? new PercentType(PercentType.HUNDRED.intValue() - position.intValue()) : position); + } + + /** + * Creation of a Position object to specify a distinct actuator setting. + * + * @param veluxPosition A position as type {@link int} based on the Velux-specific value ranges (between 0x0000 and + * 0xc800, or 0xD200 for stop). + */ + public VeluxProductPosition(int veluxPosition) { + logger.trace("VeluxProductPosition(constructur with {} as veluxPosition) called.", veluxPosition); + if ((veluxPosition == VPP_VELUX_UNKNOWN) || (veluxPosition == VPP_VELUX_STOP) || (veluxPosition < VPP_VELUX_MIN) + || (veluxPosition > VPP_VELUX_MAX)) { + logger.trace("VeluxProductPosition() gives up."); + this.position = new PercentType(VPP_UNKNOWN); + this.isValid = false; + } else { + float result = (ONE * veluxPosition - VPP_VELUX_MIN) / (VPP_VELUX_MAX - VPP_VELUX_MIN); + result = Math.round(VPP_OPENHAB_MIN + result * (VPP_OPENHAB_MAX - VPP_OPENHAB_MIN)); + logger.trace("VeluxProductPosition() created with percent-type {}.", (int) result); + this.position = new PercentType((int) result); + this.isValid = true; + } + } + + /** + * Creation of a Position object to specify a STOP. + */ + public VeluxProductPosition() { + logger.trace("VeluxProductPosition() as STOP position created."); + this.position = new PercentType(VPP_UNKNOWN); + this.isValid = false; + } + + // Class access methods + + public boolean isValid() { + return this.isValid; + } + + public PercentType getPositionAsPercentType() { + return position; + } + + public PercentType getPositionAsPercentType(boolean toBeInverted) { + return toBeInverted ? new PercentType(PercentType.HUNDRED.intValue() - position.intValue()) : position; + } + + public int getPositionAsVeluxType() { + if (this.isValid) { + float result = (ONE * position.intValue() - VPP_OPENHAB_MIN) / (VPP_OPENHAB_MAX - VPP_OPENHAB_MIN); + result = VPP_VELUX_MIN + result * (VPP_VELUX_MAX - VPP_VELUX_MIN); + return (int) result; + } else { + return VPP_VELUX_STOP; + } + } + + @Override + public String toString() { + if (this.isValid) { + return String.format("%d", position.intValue()); + } else { + return new String(VeluxBindingConstants.UNKNOWN); + } + } + + // Helper methods + + public static int getRelativePositionAsVeluxType(boolean upwards, PercentType position) { + float result = (VPP_VELUX_PERCENTAGE_MAX + VPP_VELUX_PERCENTAGE_MIN) / 2; + if (upwards) { + result = result + (ONE * position.intValue() - VPP_OPENHAB_MIN) / (VPP_OPENHAB_MAX - VPP_OPENHAB_MIN) + * ((VPP_VELUX_PERCENTAGE_MAX - VPP_VELUX_PERCENTAGE_MIN) / 2); + } else { + result = result - (ONE * position.intValue() - VPP_OPENHAB_MIN) / (VPP_OPENHAB_MAX - VPP_OPENHAB_MIN) + * ((VPP_VELUX_PERCENTAGE_MAX - VPP_VELUX_PERCENTAGE_MIN) / 2); + } + return (int) result; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductReference.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductReference.java new file mode 100644 index 0000000000000..ea30b7774261b --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductReference.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.velux.internal.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux product characteristics: Product Reference. + *

+ * Combined set of information which describes a current state of a single Velux product. + *

+ * Methods in handle this type of information: + *
    + *
  • {@link #getProductName()} to retrieve the name representing this actuator/product.
  • + *
  • {@link #getProductType()} to retrieve the type of the product.
  • + *
  • {@link #toString} to retrieve a human-readable description of the product state.
  • + *
+ * + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxProductReference { + private final Logger logger = LoggerFactory.getLogger(VeluxProductReference.class); + + // Class internal + + private final VeluxProductName name; + private final VeluxProductType typeId; + + // Constructor + + /** + * Initializes the {@link VeluxProductReference} based on a given {@link VeluxProduct} and its associated type. + *

+ * + * @param name as {@link VeluxProductName} referencing to a specific actuator/product. + * @param type as int as handled by {@link VeluxProductType#get(int)}. + */ + public VeluxProductReference(VeluxProductName name, int type) { + this.name = name; + this.typeId = VeluxProductType.get(type); + if (this.typeId == VeluxProductType.UNDEFTYPE) { + logger.warn( + "Please report this to maintainer of the {} binding: VeluxProductReference({}) has found an unregistered ProductTypeId.", + VeluxBindingConstants.BINDING_ID, type); + } + } + + // Class access methods + + public VeluxProductName getProductName() { + return this.name; + } + + public VeluxProductType getProductType() { + return this.typeId; + } + + @Override + public String toString() { + return String.format("Prod.ref. \"%s\"/%s", this.name, this.typeId); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductSerialNo.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductSerialNo.java new file mode 100644 index 0000000000000..4bf12768b5b0e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductSerialNo.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.velux.internal.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * This class support handling of Serial Number used with Velux. + *

    + *
  • {@link #UNKNOWN} defines an unknown serial number as constant,
  • + *
+ *
    + *
  • {@link #toString} converts the serial number as array of bytes into a human-readable String,
  • + *
  • {@link #isInvalid} evaluates whether the given serial number is valid,
  • + *
  • {@link #indicatesRevertedValues} returns a flag whether the serial number indicates inversion,
  • + *
  • {@link #cleaned} returns a plain serial number without any inverse indicators.
  • + *
+ *

+ * Example: + *

    + *
  • 12:23:34:45:56:67:78:89 represents a normal serial number,
  • + *
  • 12:23:34:45:56:67:78:89* represents a serial number which leads to an inverted value handling.
  • + *
+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public final class VeluxProductSerialNo { + + /* + * *************************** + * ***** Private Objects ***** + */ + + private static final String HEXBYTE_SEPARATOR = ":"; + private static final char SUFFIX_MARKER = '*'; + + /* + * ************************** + * ***** Public Objects ***** + */ + + public static final String UNKNOWN = "00:00:00:00:00:00:00:00"; + + /* + * ************************ + * ***** Constructors ***** + */ + + // Suppress default constructor for creating a non-instantiable class. + + private VeluxProductSerialNo() { + throw new AssertionError(); + } + + /* + * *************************** + * ***** Utility Methods ***** + */ + + /** + * Returns the complete serial number as human-readable sequence of hex bytes each separated by the given separator. + * + * @param serialNumber as array of Type byte. + * @param separator as of Type String. + * @return serialNumberString of type String. + */ + public static String toString(byte[] serialNumber, String separator) { + StringBuilder sb = new StringBuilder(); + for (byte b : serialNumber) { + sb.append(String.format("%02X", b)); + sb.append(separator); + } + if (sb.lastIndexOf(separator) > 0) { + sb.deleteCharAt(sb.lastIndexOf(separator)); + } + return (sb.toString()); + } + + /** + * Returns the complete serial number as human-readable sequence of hex bytes each separated by a colon. + * + * @param serialNumber as array of Type byte. + * @return serialNumberString of type String. + */ + public static String toString(byte[] serialNumber) { + return toString(serialNumber, HEXBYTE_SEPARATOR); + } + + /** + * Evaluates whether the given serial number is valid. + * + * @param serialNumber as array of type {@link byte}, + * @return invalid of type {@link boolean}. + */ + public static boolean isInvalid(byte[] serialNumber) { + if (serialNumber.length != 8) { + return true; + } + return ((serialNumber[0] == 0) && (serialNumber[1] == 0) && (serialNumber[2] == 0) && (serialNumber[3] == 0) + && (serialNumber[4] == 0) && (serialNumber[5] == 0) && (serialNumber[6] == 0) + && (serialNumber[7] == 0)); + } + + /** + * Evaluates a given serial number to determine whether any item value should be handled inverted. + * + * @param serialNumberString as type {@link String}, + * @return isInverted of type {@link boolean}. + */ + public static boolean indicatesRevertedValues(String serialNumberString) { + return (serialNumberString.length() == 0) ? false + : serialNumberString.charAt(serialNumberString.length() - 1) == SUFFIX_MARKER; + } + + /** + * Converts a given serial number into a plain serial number without any inversion markers being left. + * + * @param serialNumberString as type {@link String}, + * @return cleanedSerialNumberString of type {@link String}. + */ + public static String cleaned(String serialNumberString) { + return indicatesRevertedValues(serialNumberString) + ? serialNumberString.substring(0, serialNumberString.length() - 1) + : serialNumberString; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductState.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductState.java new file mode 100644 index 0000000000000..9c42ab8da774e --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductState.java @@ -0,0 +1,91 @@ +/** + * 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.velux.internal.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Velux product characteristics: Product State. + *

+ * Combined set of information which describes a current state of a single Velux product. + *

+ * Methods in handle this type of information: + *
    + *
  • {@link #VeluxProductState(VeluxProductReference, int, int)} to create a new product state.
  • + *
  • {@link #getActuator()} to retrieve the number representing this actuator/product.
  • + *
  • {@link #getProductReference()} to retrieve reference to a product.
  • + *
  • {@link #getState()} to retrieve the current {@link VeluxProductState.ProductState product state}.
  • + *
  • {@link #getStateAsInt()} to retrieve the current product state.
  • + *
  • {@link #toString} to retrieve a human-readable description of the product state.
  • + *
+ * + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxProductState { + + // Type definitions + + @NonNullByDefault + private class ProductState { + + private int state; + + public int getState() { + return state; + } + + private ProductState(int state) { + this.state = state; + } + } + + // Class internal + + private VeluxProductReference productReference; + private int actuator; + private ProductState state; + + // Constructor + + public VeluxProductState(VeluxProductReference productReference, int actuator, int state) { + this.productReference = productReference; + this.actuator = actuator; + this.state = new ProductState(state); + } + + // Class access methods + + public VeluxProductReference getProductReference() { + return this.productReference; + } + + public int getActuator() { + return this.actuator; + } + + public ProductState getState() { + return this.state; + } + + public int getStateAsInt() { + return this.state.getState(); + } + + @Override + public String toString() { + return String.format("State (%s, actuator %d, value %d)", this.productReference, this.actuator, this.state); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductType.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductType.java new file mode 100644 index 0000000000000..de056660b6370 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductType.java @@ -0,0 +1,127 @@ +/** + * 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.velux.internal.things; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; + +/** + * Velux product characteristics: Product Type. + *

+ * See KLF200 + * List of actuator types and their use of Main Parameter and Functional Parameters + *

+ * Methods in handle this type of information: + *

    + *
  • {@link #get(int)} to convert a value into the VeluxProductType.
  • + *
  • {@link #toString(int)} to convert a value into the description of the VeluxProductType.
  • + *
+ * + * @see VeluxKLFAPI + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public enum VeluxProductType { + SLIDER_SHUTTER, + SLIDER_WINDOW, + SWITCH, + UNDEFTYPE; + + private static enum ActuatorType { + UNDEFTYPE((short) 0xffff, VeluxBindingConstants.UNKNOWN, VeluxProductType.SWITCH), + BLIND_1_0((short) 0x0040, "Interior Venetian Blind", VeluxProductType.SLIDER_SHUTTER), + ROLLERSHUTTER_2_0((short) 0x0080, "Roller Shutter", VeluxProductType.SLIDER_SHUTTER), + ROLLERSHUTTER_2_1((short) 0x0081, "Roller Shutter", VeluxProductType.SLIDER_SHUTTER), + ROLLERSHUTTER_2_2((short) 0x0082, "Roller Shutter", VeluxProductType.SLIDER_SHUTTER), + AWNING_3_0((short) 0x00C0, "Vertical Exterior Awning", VeluxProductType.SLIDER_SHUTTER), + WINDOW_4_0((short) 0x0100, "Window opener", VeluxProductType.SLIDER_WINDOW), + WINDOW_4_1((short) 0x0101, "Window opener", VeluxProductType.SLIDER_WINDOW), + OPENER_5_0((short) 0x0140, "Garage door opener", VeluxProductType.SLIDER_SHUTTER), + OPENER_5_8((short) 0x017A, "Garage door opener", VeluxProductType.SLIDER_SHUTTER), + LIGHT_6_0((short) 0x0180, "Light", VeluxProductType.SLIDER_SHUTTER), + LIGHT_6_5((short) 0x01BA, "Light", VeluxProductType.SLIDER_SHUTTER), + OPENER_7_0((short) 0x01C0, "Gate opener", VeluxProductType.SLIDER_SHUTTER), + OPENER_7_5((short) 0x01FA, "Gate opener", VeluxProductType.SLIDER_SHUTTER), + LOCK_9_0((short) 0x0240, "Door lock", VeluxProductType.SLIDER_SHUTTER), + LOCK_9_1((short) 0x0241, "Window lock", VeluxProductType.SLIDER_SHUTTER), + BLIND_10((short) 0x0280, "Vertical Interior Blinds", VeluxProductType.SLIDER_SHUTTER), + SHUTTER_13((short) 0x0340, "Dual Roller Shutter", VeluxProductType.SLIDER_SHUTTER), + SWITCH_15((short) 0x03C0, "On/Off switch", VeluxProductType.SWITCH), + AWNING_16((short) 0x0400, "Horizontal awning", VeluxProductType.SLIDER_SHUTTER), + BLIND_17((short) 0x0440, "Exterior Venetian blind", VeluxProductType.SLIDER_SHUTTER), + BLIND_18((short) 0x0480, "Louver blind", VeluxProductType.SLIDER_SHUTTER), + TRACK_19((short) 0x04C0, "Curtain track", VeluxProductType.SLIDER_SHUTTER), + POINT_20((short) 0x0500, "Ventilation point", VeluxProductType.SLIDER_SHUTTER), + POINT_20_1((short) 0x0501, "Ventilation point", VeluxProductType.SLIDER_SHUTTER), + POINT_20_2((short) 0x0502, "Ventilation point", VeluxProductType.SLIDER_SHUTTER), + POINT_20_3((short) 0x0503, "Ventilation point", VeluxProductType.SLIDER_SHUTTER), + HEATING_21((short) 0x0540, "Exterior heating", VeluxProductType.SLIDER_SHUTTER), + HEATING_21_5((short) 0x57A, "Exterior heating", VeluxProductType.SLIDER_SHUTTER), + SHUTTER_24_0((short) 0x0600, "Swinging Shutters", VeluxProductType.SLIDER_SHUTTER), + SHUTTER_24_1((short) 0x0601, "Swinging Shutters", VeluxProductType.SLIDER_SHUTTER),; + + // Class internal + + private short nodeType; + private String description; + private VeluxProductType typeClass; + + // Reverse-lookup map for getting a ActuatorType from an TypeId + private static final Map LOOKUPTYPEID2ENUM = Stream.of(ActuatorType.values()) + .collect(Collectors.toMap(ActuatorType::getNodeType, Function.identity())); + + // Constructor + + private ActuatorType(short nodeType, String description, VeluxProductType typeClass) { + this.nodeType = nodeType; + this.description = description; + this.typeClass = typeClass; + } + + // Class access methods + + int getNodeType() { + return nodeType; + } + + String getDescription() { + return description; + } + + static ActuatorType get(int nodeType) { + return LOOKUPTYPEID2ENUM.getOrDefault(nodeType, ActuatorType.UNDEFTYPE); + } + } + + // Class access methods + + public static VeluxProductType get(int nodeType) { + if (ActuatorType.get(nodeType) != ActuatorType.UNDEFTYPE) { + return ActuatorType.get(nodeType).typeClass; + } else { + return VeluxProductType.UNDEFTYPE; + } + } + + public static String toString(int nodeType) { + return ActuatorType.get(nodeType).getDescription(); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductVelocity.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductVelocity.java new file mode 100644 index 0000000000000..343aecb458529 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxProductVelocity.java @@ -0,0 +1,96 @@ +/** + * 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.velux.internal.things; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; + +/** + * Velux product characteristics: Velocity. + *

+ * See KLF200 + * Velocity parameter + *

+ * Methods in handle this type of information: + *

    + *
  • {@link #getVelocity()} to retrieve the value of the characteristic.
  • + *
  • {@link #get(int)} to convert a value into the characteristic.
  • + *
  • {@link #getByName(String)} to convert a name into the characteristic.
  • + *
  • {@link #dump} to retrieve a human-readable description of all values.
  • + *
+ * + * @see VeluxKLFAPI + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public enum VeluxProductVelocity { + DEFAULT((short) 0, "default"), + SILENT((short) 1, "short"), + FAST((short) 2, "fast"), + VELOCITY_NOT_AVAILABLE((short) 255, ""), + UNDEFTYPE((short) 0xffff, VeluxBindingConstants.UNKNOWN); + + // Class internal + + private short velocity; + private String velocityName; + + // Reverse-lookup map for getting a VeluxProductVelocity from a value. + private static final Map LOOKUPTYPEID2ENUM = Stream.of(VeluxProductVelocity.values()) + .collect(Collectors.toMap(VeluxProductVelocity::getVelocity, Function.identity())); + + // Constructor + + private VeluxProductVelocity(short velocity, String velocityName) { + this.velocity = velocity; + this.velocityName = velocityName; + } + + // Class access methods + + public short getVelocity() { + return velocity; + } + + public static VeluxProductVelocity get(int velocity) { + return LOOKUPTYPEID2ENUM.getOrDefault(velocity, VeluxProductVelocity.UNDEFTYPE); + } + + public static VeluxProductVelocity getByName(String velocityName) { + for (VeluxProductVelocity enumItem : VeluxProductVelocity.values()) { + if (enumItem.velocityName.equals(velocityName)) { + return enumItem; + } + } + return VeluxProductVelocity.UNDEFTYPE; + } + + public static String dump() { + StringBuilder sb = new StringBuilder(); + for (VeluxProductVelocity typeId : VeluxProductVelocity.values()) { + sb.append(typeId).append(VeluxBindingConstants.OUTPUT_VALUE_SEPARATOR); + } + if (sb.lastIndexOf(VeluxBindingConstants.OUTPUT_VALUE_SEPARATOR) > 0) { + sb.deleteCharAt(sb.lastIndexOf(VeluxBindingConstants.OUTPUT_VALUE_SEPARATOR)); + } + return sb.toString(); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxScene.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxScene.java new file mode 100644 index 0000000000000..58731801002b8 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/VeluxScene.java @@ -0,0 +1,141 @@ +/** + * 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.velux.internal.things; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.velux.internal.VeluxBindingConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Velux scene representation. + *

+ * Combined set of information with references towards multiple Velux product states. + *

+ * Methods in handle this type of information: + *

    + *
  • {@link #VeluxScene(String, int, boolean, VeluxProductState[])} to create a new scene.
  • + *
  • {@link #VeluxScene(VeluxScene)} to duplicate a scene.
  • + *
  • {@link #getName} to retrieve the name of this scene.
  • + *
  • {@link #getBridgeSceneIndex()} to retrieve the index of this scene.
  • + *
  • {@link #toString()} to retrieve a human-readable description of this scene.
  • + *
+ * + * @see VeluxProductState + * + * @author Guenther Schreiner - initial contribution. + */ +@NonNullByDefault +public class VeluxScene { + private final Logger logger = LoggerFactory.getLogger(VeluxScene.class); + + // Public definition + + public static final VeluxScene UNKNOWN = new VeluxScene(); + + // Type definitions + + @NonNullByDefault + public static class SceneName { + + private static final SceneName UNKNOWN = new SceneName(VeluxBindingConstants.UNKNOWN); + + private String name; + + @Override + public String toString() { + return name; + } + + public SceneName(String name) { + this.name = name; + } + + public boolean equals(SceneName anotherName) { + return this.name.equals(anotherName.toString()); + } + } + + @NonNullByDefault + public static class SceneBridgeIndex { + + private static final SceneBridgeIndex UNKNOWN = new SceneBridgeIndex(0); + + private int id; + + @Override + public String toString() { + return String.valueOf(id); + } + + public int toInt() { + return id; + } + + private SceneBridgeIndex(int id) { + this.id = id; + } + } + + // Class internal + + private SceneName name; + private SceneBridgeIndex bridgeSceneIndex; + private boolean silent; + private VeluxProductState[] productStates; + + // Constructor + + /** + * Constructor + * + * just for the dummy VeluxProduct. + */ + private VeluxScene() { + logger.trace("VeluxScene() created."); + this.name = SceneName.UNKNOWN; + this.bridgeSceneIndex = SceneBridgeIndex.UNKNOWN; + this.silent = false; + this.productStates = new VeluxProductState[0]; + } + + public VeluxScene(String name, int sceneBridgeIndex, boolean silentOperation, VeluxProductState[] actions) { + this.name = new SceneName(name); + this.bridgeSceneIndex = new SceneBridgeIndex(sceneBridgeIndex); + this.silent = silentOperation; + this.productStates = actions; + } + + public VeluxScene(VeluxScene scene) { + this.name = new SceneName(scene.name.toString()); + this.bridgeSceneIndex = new SceneBridgeIndex(scene.bridgeSceneIndex.toInt()); + this.silent = scene.silent; + this.productStates = scene.productStates; + } + // Class access methods + + public SceneName getName() { + return this.name; + } + + public SceneBridgeIndex getBridgeSceneIndex() { + return this.bridgeSceneIndex; + } + + @Override + public String toString() { + return String.format("Scene \"%s\" (index %d) with %ssilent mode and %d actions", this.name, + this.bridgeSceneIndex.toInt(), this.silent ? "" : "non-", this.productStates.length); + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/package-info.java new file mode 100644 index 0000000000000..06702fbaac96a --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/things/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * Classes for modeling the Velux devices and their properties in a general openHAB (OH1/OH2/OH3)-independent way. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.things; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java new file mode 100644 index 0000000000000..76cc54d637850 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/Localization.java @@ -0,0 +1,117 @@ +/** + * 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.velux.internal.utils; + +import java.util.Locale; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.i18n.LocaleProvider; +import org.eclipse.smarthome.core.i18n.TranslationProvider; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is a utility class for dealing with localization. + * + * It provides the following methods: + *
    + *
  • {@link #getText} returns the localized message.
  • + *
+ *

+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class Localization { + private final Logger logger = LoggerFactory.getLogger(Localization.class); + + // Public definition + + public static final Localization UNKNOWN = new Localization(); + + /* + * *************************** + * ***** Private Objects ***** + */ + private static final String OPENBRACKET = "("; + private static final String CLOSEBRACKET = ")"; + private LocaleProvider localeProvider; + private @NonNullByDefault({}) TranslationProvider i18nProvider; + + /** + * Class, which is needed to maintain a @NonNullByDefault for class {@link Localization}. + */ + @NonNullByDefault + private class UnknownLocale implements LocaleProvider { + @Override + public Locale getLocale() { + return java.util.Locale.ROOT; + } + } + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * Constructor + *

+ * Initializes the {@link Localization} module without any framework informations. + */ + Localization() { + this.localeProvider = new UnknownLocale(); + } + + /** + * Constructor + *

+ * Initializes the {@link Localization} module with framework informations. + * + * @param localeProvider providing a locale, + * @param i18nProvider as service interface for internationalization. + */ + public Localization(final LocaleProvider localeProvider, final TranslationProvider i18nProvider) { + logger.trace("Localization(Constructor w/ {},{}) called.", localeProvider, i18nProvider); + this.localeProvider = localeProvider; + this.i18nProvider = i18nProvider; + } + + /** + * Converts a given message into an equivalent localized message. + * + * @param key the message of type {@link String} to be converted, + * @param arguments (optional) arguments being referenced within the messageString. + * @return localizedMessageString the resulted message of type {@link String}. + */ + public String getText(String key, Object... arguments) { + if (i18nProvider == null) { + logger.trace("getText() returns default as no i18nProvider existant."); + return key; + } + Bundle bundle = FrameworkUtil.getBundle(this.getClass()).getBundleContext().getBundle(); + Locale locale = localeProvider.getLocale(); + String defaultText = OPENBRACKET.concat(key).concat(CLOSEBRACKET); + + String text = i18nProvider.getText(bundle, key, defaultText, locale, arguments); + if (text == null) { + logger.warn("Internal error: localization for key {} is missing.", key); + text = defaultText; + } + logger.trace("getText() returns {}.", text); + return text; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.java new file mode 100644 index 0000000000000..627aba075890a --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/ManifestInformation.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.velux.internal.utils; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.osgi.framework.FrameworkUtil; + +/** + * This is a helper class for dealing with information from MANIFEST file. + * + * It provides the following methods: + *

    + *
  • {@link #getBundleVersion} returns the bundle version as specified within the MANIFEST.
  • + *
+ *

+ * + * @author Guenther Schreiner - Initial contribution + */ +@NonNullByDefault +public class ManifestInformation { + + /* + * ************************ + * ***** Constructors ***** + */ + + /** + * Suppress default constructor for creating a non-instantiable class. + */ + private ManifestInformation() { + throw new AssertionError(); + } + + // Class access methods + + /** + * Returns the bundle version as specified within the MANIFEST file. + * + * @return bundleVersion the resulted bundle version as {@link String}. + */ + public static String getBundleVersion() { + String osgiBundleVersion = FrameworkUtil.getBundle(ManifestInformation.class).getBundleContext().getBundle() + .toString(); + return osgiBundleVersion; + } + +} diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/package-info.java new file mode 100644 index 0000000000000..4a90117dec99b --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/internal/utils/package-info.java @@ -0,0 +1,18 @@ +/** + * 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 + */ +/** + * Generic utility classes (might not be limited to the Velux binding). + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux.internal.utils; diff --git a/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/package-info.java b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/package-info.java new file mode 100644 index 0000000000000..e58e8487cac24 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/java/org/openhab/binding/velux/package-info.java @@ -0,0 +1,19 @@ +/** + * 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 + */ +/** + * + * NOTE: All relevant classes of this binding are below the internal node. + * + * @author Guenther Schreiner - Initial contribution + */ +package org.openhab.binding.velux; diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..259c5ac623306 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,11 @@ + + + + @text/binding.velux.name + @text/binding.velux.description + + Guenther Schreiner + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml new file mode 100644 index 0000000000000..c30311f034f39 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/config/config.xml @@ -0,0 +1,197 @@ + + + + + + + + + + @text/config.velux.bridge.ipAddress.description + network-address + true + + + + @text/config.velux.bridge.protocol.description + slip + true + + + + + + false + true + + + + @text/config.velux.bridge.tcpPort.description + 51200 + false + true + + + + @text/config.velux.bridge.password.description + true + password + + velux123 + + + + @text/config.velux.bridge.timeoutMsecs.description + false + 500 + true + + + + @text/config.velux.bridge.retries.description + false + 5 + true + + + + @text/config.velux.bridge.refreshMsecs.description + false + 10000 + true + + + + @text/config.velux.bridge.isBulkRetrievalEnabled.description + false + true + true + + + + @text/config.velux.bridge.isSequentialEnforced.description + false + false + true + + + + @text/config.velux.bridge.isProtocolTraceEnabled.description + false + false + true + + + + + + + + + @text/config.velux.thing.scene.sceneName.description + true + false + + + + @text/config.velux.thing.scene.velocity.description + default + true + + + + + + false + true + + + + + + + @text/config.velux.thing.actuator.serial.description + true + false + + + + @text/config.velux.thing.actuator.name.description + false + true + + + + @text/config.velux.thing.actuator.inverted.description + true + false + false + + + + + + + @text/config.velux.thing.rollershutter.serial.description + true + false + + + + @text/config.velux.thing.rollershutter.name.description + false + true + + + + @text/config.velux.thing.rollershutter.inverted.description + true + false + false + + + + + + + @text/config.velux.thing.window.serial.description + true + false + + + + @text/config.velux.thing.window.name.description + false + true + + + + @text/config.velux.thing.window.inverted.description + true + false + false + + + + + + + @text/config.velux.thing.vshutter.sceneLevels.description + true + false + + + + @text/config.velux.thing.vshutter.currentLevel.description + true + 0 + true + + + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties new file mode 100644 index 0000000000000..c48e005de956c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux.properties @@ -0,0 +1,177 @@ +# +# Copyright (c) 2010-2019 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 +# +# @author Guenther Schreiner - Initial contribution. +# +# Binding descriptions +# +binding.velux.name = Velux Binding +binding.velux.description = A binding for the Velux KLF200 Bridge. The Velux Binding interacts via a Velux Bridge with the different Velux devices like controlling window openers, shutters and others. For example a KLF200 can act as interface between the HomeAutomation and the VELUX INTEGRA products with wireless connectivity based on the io-homecontrol standard. +# +# Bridge Thing types descriptions +# +bridge-type.velux.klf200.label = Velux KLF200 Bridge +bridge-type.velux.klf200.description = The Velux KLF200 represents a gateway to all Velux resp. io-homecontrol devices. +# +# Thing types descriptions +# +thing-type.velux.binding.label = Velux Binding Information +thing-type.velux.binding.description = Presents the status of the openHAB Velux binding. +# +thing-type.velux.actuator.label = Velux Actuator Information +thing-type.velux.actuator.description = Control an actuator via the Velux KLF 200. +# +thing-type.velux.rollershutter.label = Velux Rollershutter Information +thing-type.velux.rollershutter.description = Control a rollershutter on the Velux KLF 200. +# +thing-type.velux.scene.label = Velux Scene Information +thing-type.velux.scene.description = Control a scene on the Velux KLF 200. +# +thing-type.velux.vshutter.label = Virtual Shutter Information +thing-type.velux.vshutter.description = A Set of Scenes which act together as Shutter. +# +thing-type.velux.window.label = Velux Window Information +thing-type.velux.window.description = Control a window on the Velux KLF 200. +# +# Discovery descriptions +# +discovery.velux.binding...label = Velux Binding Information Element +# +# Config descriptions +# +config.velux.bridge.ipAddress.label = IP Address +config.velux.bridge.ipAddress.description = The IP address of the Velux Bridge. +config.velux.bridge.protocol.label = Protocol +config.velux.bridge.protocol.description = The connection protocol to contact the Velux Bridge with (either slip or http or https). +config.velux.bridge.tcpPort.label = Port +config.velux.bridge.tcpPort.description = TCP port of the Velux gateway. +config.velux.bridge.password.label = Password +config.velux.bridge.password.description = Password for authentication against the Velux Bridge. +config.velux.bridge.timeoutMsecs.label = Timeout +config.velux.bridge.timeoutMsecs.description = Initial Connection timeout in milliseconds. +config.velux.bridge.retries.label = Connection Retries +config.velux.bridge.retries.description = Number of retries with timing along the Binary Exponential Backoff (BEB) Algorithm. +config.velux.bridge.refreshMsecs.label = Refresh Interval +config.velux.bridge.refreshMsecs.description = Refresh interval in milliseconds. +config.velux.bridge.isBulkRetrievalEnabled.label = Enable Bulk retrieval +config.velux.bridge.isBulkRetrievalEnabled.description = Fetch the complete actuator information in a bulk. +config.velux.bridge.isSequentialEnforced.label = Enforce Sequential Mode +config.velux.bridge.isSequentialEnforced.description = Enforce Sequential Actuator Control. Determine the mode of operation for long-time actions like running commands or activation of scenes. However the parallelism disables the in-depth protocol handshake processing which does not affect or limit any functionalities. +config.velux.bridge.isProtocolTraceEnabled.label = Enable Protocol Trace +config.velux.bridge.isProtocolTraceEnabled.description = Provide KLF200 protocol details. +# +config.velux.thing.scene.sceneName.label = Scene Name +config.velux.thing.scene.sceneName.description = Name of the scene to be handled. +config.velux.thing.scene.velocity.label = Velocity +config.velux.thing.scene.velocity.description = Velocity Level. +# +config.velux.thing.actuator.serial.label = Serial Number +config.velux.thing.actuator.serial.description = Eight hex digits (i.e. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.actuator.name.label = Name +config.velux.thing.actuator.name.description = (Optional) Name of the Actuator as defined in the gateway. +config.velux.thing.actuator.inverted.label = Actuator Position Inverted +config.velux.thing.actuator.inverted.description = The actuator position is inverted (i.e. 0% translates to 100%). +config.velux.thing.actuator.limitMinimum.label = Minimum Limitation Position +config.velux.thing.actuator.limitMinimum.description = The minimum limitation position of the actuator. +config.velux.thing.actuator.limitMaximum.label = Maximum Limitation Position +config.velux.thing.actuator.limitMaximum.description = The maximum limitation position of the actuator. +# +config.velux.thing.rollershutter.serial.label = Serial Number +config.velux.thing.rollershutter.serial.description = Eight hex digits (i.e. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.rollershutter.name.label = Name +config.velux.thing.rollershutter.name.description = (Optional) Name of the Actuator as defined in the gateway. +config.velux.thing.rollershutter.inverted.label = Actuator Position Inverted +config.velux.thing.rollershutter.inverted.description = The actuator position is inverted (i.e. 0% translates to 100%). +config.velux.thing.rollershutter.limitMinimum.label = Minimum Limitation Position +config.velux.thing.rollershutter.limitMinimum.description = The minimum limitation position of the rollershutter. +config.velux.thing.rollershutter.limitMaximum.label = Maximum Limitation Position +config.velux.thing.rollershutter.limitMaximum.description = The maximum limitation position of the rollershutter. +# +config.velux.thing.window.serial.label = Serial Number +config.velux.thing.window.serial.description = Eight hex digits (i.e. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.window.name.label = Name +config.velux.thing.window.name.description = (Optional) Name of the Actuator as defined in the gateway. +config.velux.thing.window.inverted.label = Actuator Position Inverted +config.velux.thing.window.inverted.description = The actuator position is inverted (i.e. 0% translates to 100%). +config.velux.thing.window.limitMinimum.label = Minimum Limitation Position +config.velux.thing.window.limitMinimum.description = The minimum limitation position of the window. +config.velux.thing.window.limitMaximum.label = Maximum Limitation Position +config.velux.thing.window.limitMaximum.description = The maximum limitation position of the window. +# +config.velux.thing.vshutter.sceneLevels.label = List of Positions/Scenes +config.velux.thing.vshutter.sceneLevels.description = Definition of a virtual shutter by declaring one scene corresponding to one position. Therefore this parameter look like: ,,, +config.velux.thing.vshutter.currentLevel.label = Current Position +config.velux.thing.vshutter.currentLevel.description = Value between 0 and 100. +# +# Channel types descriptions +# +channel-type.velux.information.label = Binding Information +channel-type.velux.information.description = Description of current Binding State. +channel-type.velux.status.label = Bridge State +channel-type.velux.status.description = Description of current Bridge State. +channel-type.velux.reload.label = Reload +channel-type.velux.reload.description = Reload Bridge Information. +channel-type.velux.downtime.label = Downtime +channel-type.velux.downtime.description = Time interval between last successful and recent device interaction. +channel-type.velux.doDetection.label = Activate Bridge Detection mode +channel-type.velux.doDetection.description = Start of the product detection mode. +channel-type.velux.firmware.label = Firmware Version +channel-type.velux.firmware.description = Software version of the Bridge. +channel-type.velux.ipAddress.label = IP-Adresse +channel-type.velux.ipAddress.description = IP address of the Bridge. +channel-type.velux.subnetMask.label = IP Subnet Mask +channel-type.velux.subnetMask.description = IP subnetmask of the Bridge. +channel-type.velux.defaultGW.label = Default Gateway +channel-type.velux.defaultGW.description = IP address of the default Gateway of the Bridge. +channel-type.velux.DHCP.label = DHCP Enabled +channel-type.velux.DHCP.description = Flag whether automatic IP configuration is enabled. +channel-type.velux.WLANSSID.label = SSID +channel-type.velux.WLANSSID.description = Name of the wireless network. +channel-type.velux.WLANPassword.label = WLAN Authentication +channel-type.velux.WLANPassword.description = WLAN Authentication Password. +channel-type.velux.products.label = Identified Products +channel-type.velux.products.description = Products which are registered on the Bridge. +channel-type.velux.scenes.label = Identified Scenes +channel-type.velux.scenes.description = Scenes which are configured on the Bridge. +channel-type.velux.check.label = Check of configuration +channel-type.velux.check.description = Result of the check of current item configuration. +# +channel-type.velux.position.label = Position +channel-type.velux.position.description = Device control (UP, DOWN, STOP, closure 0-100%). +channel-type.velux.state.label = State +channel-type.velux.state.description = Device control (ON, OFF). +channel-type.velux.action.label = Start of a Scene +channel-type.velux.action.description = Activates a set of predefined product settings. +channel-type.velux.silentMode.label = Enabling silent mode +channel-type.velux.silentMode.description = Activates the silent mode of the predefined product settings. +channel-type.velux.limitMinimum.label = Minimum Limitation Position +channel-type.velux.limitMinimum.description = The minimum limitation position of the actuator/rollershutter/window. +channel-type.velux.limitMaximum.label = Maximum Limitation Position +channel-type.velux.limitMaximum.description = The maximum limitation position of the actuator/rollershutter/window. +channel-type.velux.vposition.label = Position +channel-type.velux.vposition.description = Device control (UP, DOWN, STOP, closure 0-100%). +# +# Runtime status descriptions +# +runtime.no-bridge = So far no bridge is defined. Please add a thing of type "Velux KLF200" to establish a connection to the gateway, which provides the prerequisite for further commissioning. +runtime.one-bridge = A bridge element is already defined. Thus, you can now set up additional devices by means of search (or discovery) or by adding things manually. +runtime.multiple-bridges = There are more than one bridges defined. This is of course possible with several Velux KLF200 gateways or with a redundant setup (SLIP via LAN parallel to JSON via WLAN). Every other case should be avoided. +runtime.bridge-offline-no-valid-bridgeProtocol-selected = The parameter "bridgeProtocol" has to be set correctly. +runtime.bridge-offline-login-sequence-failed = Login sequence failed. +# +# Thing status descriptions +# +channelValue.check-integrity-failed = Integrity check failed: The following scenes are unused: +channelValue.check-integrity-ok = Integrity check ok. All scenes are used within Items. +# +# end-of-ESH-INF/i18n/velux.properties +# diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties new file mode 100644 index 0000000000000..cede65bf1f723 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_de.properties @@ -0,0 +1,183 @@ +# +# Copyright (c) 2010-2019 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 +# +# @author Guenther Schreiner - Initial contribution. +# +# +# Binding descriptions +# +binding.velux.name = Velux Binding +binding.velux.description = Dieses Binding integriert io-homecontrol-Gerte, wie Rollden und Fenster, mittels dem Velux KLF200 Kopplungselement. +# +# Bridge Thing types descriptions +# +bridge-type.velux.klf200.label = Velux KLF200 +bridge-type.velux.klf200.description = Dieses Element bildet das Kopplungselement ab, welches zur Interaktion mit unterschiedlichen Velux- bzw. io-homecontrol-Gerten dient. Das Kopplungselement kann nicht per Discovery gefunden werden, sondern ist per Konfiguration einmalig einzurichten. +# +# Thing types descriptions +# +thing-type.velux.binding.label = Velux Binding Information +thing-type.velux.binding.description = Dieses Element informiert ber den Status des Velux-Bindings und wird ohne weitere Konfiguration mittels Discovery automatisch erzeugt. Es dient ausschlielich der Information, um die Inbetriebnahmephase zu untersttzen. +# +thing-type.velux.actuator.label = Velux Aktor +thing-type.velux.actuator.description = Aktor, welcher ber io-homecontrol steuert wird. +# +thing-type.velux.rollershutter.label = Velux Rolladen +thing-type.velux.rollershutter.description = Rolladen, welcher ber io-homecontrol steuert wird. +# +thing-type.velux.scene.label = Velux Szene +thing-type.velux.scene.description = Szene als Sammlung von Gertezustnden, welche aktiviert werden knnen. +# +thing-type.velux.vshutter.label = Virtueller Rolladen +thing-type.velux.vshutter.description = Rolladen, welcher ber eine Menge von Szenen definiert wird. +# +thing-type.velux.window.label = Velux Fenster +thing-type.velux.window.description = Fenster, welches ber io-homecontrol steuert wird. +# +# Discovery descriptions +# +discovery.velux.binding...label = Velux Binding Informationselement +# +# Config descriptions +# +config.velux.bridge.ipAddress.label = Adresse des Kopplungselements +config.velux.bridge.ipAddress.description = Name oder IP-Adresse zum Zugriff auf das Velux KLF200 Kopplungselement. +config.velux.bridge.protocol.label = Kommunikationsprotokoll +config.velux.bridge.protocol.description = Protokoll zum Zugriff auf das Velux KLF200 Kopplungselement. +config.velux.bridge.tcpPort.label = Port des Kopplungselements +config.velux.bridge.tcpPort.description = TCP Portnummer zum HTTP-Zugriff auf das Velux KLF200 Kopplungselement. +config.velux.bridge.password.label = Passwort +config.velux.bridge.password.description = Passwort zur Anmeldung an dem Velux KLF200 Kopplungselement. +config.velux.bridge.timeoutMsecs.label = Zeitberschreitung +config.velux.bridge.timeoutMsecs.description = Zeit in Millisekunden. +config.velux.bridge.retries.label = Wiederholungsversuche +config.velux.bridge.retries.description = Anzahl der Verbindungswiederholungen fr den Binary Exponential Backoff (BEB) Algorithmus. +config.velux.bridge.refreshMsecs.label = Auffrischintervall +config.velux.bridge.refreshMsecs.description = Zeit in Millisekunden. +config.velux.bridge.isBulkRetrievalEnabled.label = Massenabfrage +config.velux.bridge.isBulkRetrievalEnabled.description = Aktivierung von Massenabfrage zur Gertebersicht. +config.velux.bridge.isSequentialEnforced.label = Sequentielles Modus +config.velux.bridge.isSequentialEnforced.description = Erzwingt den sequentiellen Modus von Operationen, insbesondere von lnger andauernden Aktionen, wie der Aktivierung von Szenen. +config.velux.bridge.isProtocolTraceEnabled.label = Protokolleinblick +config.velux.bridge.isProtocolTraceEnabled.description = Aktiviert KLF200 Protokolldetails. +# +config.velux.thing.scene.sceneName.label = Scenenname +config.velux.thing.scene.sceneName.description = Name der Szene, wie sie auf dem Kopplungselements definiert wurde. +config.velux.thing.scene.velocity.label = Geschwindigkeit +config.velux.thing.scene.velocity.description = Geschwindigkeitsniveau. +# +config.velux.thing.actuator.serial.label = Seriennumber +config.velux.thing.actuator.serial.description = Acht hexadezimale Ziffern (bspw. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.actuator.name.label = Name +config.velux.thing.actuator.name.description = (Optionaler) Name des Aktuators, wie er auf dem Kopplungselements definiert wurde. +config.velux.thing.actuator.inverted.label = Positioninvertierung +config.velux.thing.actuator.inverted.description = Die Aktuatorposition wird invertiert (bspw. 0% umgesetzt zu 100%). +config.velux.thing.actuator.limitMinimum.label = Minimale Begrenzungsposition +config.velux.thing.actuator.limitMinimum.description = Die minimale Begrenzungsposition des Stellantriebs. +config.velux.thing.actuator.limitMaximum.label = Maximale Begrenzungsposition +config.velux.thing.actuator.limitMaximum.description = Die maximale Begrenzungsposition des Stellantriebs. +# +config.velux.thing.rollershutter.serial.label = Seriennumber +config.velux.thing.rollershutter.serial.description = Acht hexadezimale Ziffern (bspw. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.rollershutter.name.label = Name +config.velux.thing.rollershutter.name.description = (Optionaler) Name des Rolladens, wie er auf dem Kopplungselements definiert wurde. +config.velux.thing.rollershutter.inverted.label = Positioninvertierung +config.velux.thing.rollershutter.inverted.description = Die Rolladenposition wird invertiert (bspw. 0% umgesetzt zu 100%). +config.velux.thing.rollershutter.limitMinimum.label = Minimale Begrenzungsposition +config.velux.thing.rollershutter.limitMinimum.description = Die minimale Begrenzungsposition des Rolladens. +config.velux.thing.rollershutter.limitMaximum.label = Maximale Begrenzungsposition +config.velux.thing.rollershutter.limitMaximum.description = Die maximale Begrenzungsposition des Rolladens. +# +config.velux.thing.window.serial.label = Seriennumber +config.velux.thing.window.serial.description = Acht hexadezimale Ziffern (bspw. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.window.name.label = Name +config.velux.thing.window.name.description = (Optionaler) Name des Fensters, wie er auf dem Kopplungselements definiert wurde. +config.velux.thing.window.inverted.label = Positioninvertierung +config.velux.thing.window.inverted.description = Die Fensterposition wird invertiert (bspw. 0% umgesetzt zu 100%). +config.velux.thing.window.limitMinimum.label = Minimale Begrenzungsposition +config.velux.thing.window.limitMinimum.description = Die minimale Begrenzungsposition des Fensters. +config.velux.thing.window.limitMaximum.label = Maximale Begrenzungsposition +config.velux.thing.window.limitMaximum.description = Die maximale Begrenzungsposition des Fensters. +# +config.velux.thing.vshutter.sceneLevels.label = Liste von Positionen/Szenen +config.velux.thing.vshutter.sceneLevels.description = Definition eines virtuellen Rolladens mittels Festlegung von einer Position durch eine Szene. So sieht der Parameter aus wie: ,,, +config.velux.thing.vshutter.currentLevel.label = Aktuelle Position +config.velux.thing.vshutter.currentLevel.description = Wert zwischen 0 und 100. +# +# Channel types descriptions +# +channel-type.velux.information.label = Binding Information +channel-type.velux.information.description = Beschreibung des aktuellen Binding Status. +channel-type.velux.status.label = Status +channel-type.velux.status.description = Status des Kopplungselements. +channel-type.velux.reload.label = Nachladen +channel-type.velux.reload.description = Konfigurationen neu laden. +channel-type.velux.downtime.label = Ausfallzeit +channel-type.velux.downtime.description = Zeitintervall zwischen der letzten erfolgreichen und der letzten Gerteinteraktion. +channel-type.velux.doDetection.label = Produkterkennung +channel-type.velux.doDetection.description = Schalter zum Start der Produkterkennung. +channel-type.velux.firmware.label = Firmware +channel-type.velux.firmware.description = Softwareversion des Kopplungselements. +channel-type.velux.ipAddress.label = LAN IP-Adresse +channel-type.velux.ipAddress.description = IP-Adresse des Kopplungselements. +channel-type.velux.subnetMask.label = LAN Subnetzmaske +channel-type.velux.subnetMask.description = IP-Subnetzmaske des Kopplungselements. +channel-type.velux.defaultGW.label = LAN Router-IP-Adresse +channel-type.velux.defaultGW.description = IP-Adresse des Routers. +channel-type.velux.DHCP.label = LAN DHCP-Aktivierung +channel-type.velux.DHCP.description = Aktivierung der automatischen IP-Konfiguration mittels DHCP. +channel-type.velux.WLANSSID.label = WLAN-SSID +channel-type.velux.WLANSSID.description = Netzwerkkennung des Wireless-Netzwerks. +channel-type.velux.WLANPassword.label = WLAN-Passwort +channel-type.velux.WLANPassword.description = Passwort zum Zugriff auf das Wireless-Netzwerk. +channel-type.velux.products.label = Identifizierte Produkte +channel-type.velux.products.description = Produkte welche auf dem Kopplungselement im Rahmen von Szenen definiert sind. +channel-type.velux.scenes.label = Identifizierte Szenen +channel-type.velux.scenes.description = Szenen welche auf dem Kopplungselement definiert sind. +channel-type.velux.check.label = Konfigurationsprfung +channel-type.velux.check.description = Vergleich der Gateway-Konfiguration mit der openHAB-Konfiguration. +# +channel-type.velux.position.label = Position +channel-type.velux.position.description = Positionssteuerungselement (hoch/runter/stop). +channel-type.velux.state.label = Zustand +channel-type.velux.state.description = Steuerung (AN, AUS). +channel-type.velux.action.label = Szenenstart +channel-type.velux.action.description = Schalter zum Start dieser Szene +channel-type.velux.silentMode.label = Stillmodus +channel-type.velux.silentMode.description = Aktiviert den Leise-Modus auf einer definierten Szene. +channel-type.velux.limitMinimum.label = Miniale Begrenzungsposition +channel-type.velux.limitMinimum.description = Untere Begrenzungsposition des Aktuators/Rolladens/Fensters. +channel-type.velux.limitMaximum.label = Maximale Begrenzungsposition +channel-type.velux.limitMaximum.description = Obere Begrenzungsposition des Aktuators/Rolladens/Fensters. +channel-type.velux.vposition.label = Position +channel-type.velux.vposition.description = Positionssteuerungselement (hoch/runter/stop). +# +# Runtime status descriptions +# +runtime.no-bridge = Bislang ist noch ist kein Kopplungselement definiert. Bitte fgen Sie ein Thing vom Typ "Velux KLF200" hinzu, um eine Verbindung zum Kopplungselement aufzubauen, welches die Voraussetzung fr die weitere Inbetriebnahme darstellt. +runtime.one-bridge = Es ist bereits ein Kopplungselement definiert. Somit knnen Sie nun weitere Gerte mittels Suche (bzw. Discovery) oder durch manuelles Hinzufgen von Things einrichten. +runtime.multiple-bridges = Es sind mehr als ein Kopplungselement definiert. Dies ist natrlich bei mehreren Velux KLF200 Gateways oder bei einem redundanten Aufbau (SLIP ber LAN parallel zu JSON ber WLAN) mglich. Jeder andere Fall sollte vermieden werden. +runtime.bridge-offline-no-valid-bridgeProtocol-selected = Der Parameter "bridgeProtocol" muss korrekt gesetzt werden. +runtime.bridge-offline-login-sequence-failed = Anmeldung am Kopplungselement fehlgeschlagen. +# +# Thing status descriptions +# +channelValue.check-integrity-failed = Integrittsprfung fehlgeschlagen. Nachfolgende Szenen werden nicht verwendet: +channelValue.check-integrity-ok = Integrittsprfung bestanden. Alle definierten Szenen werden verwendet. +# +# Note: this entry should overwrite the implicit naming within paperUI slider element +# +channel-type.velux.control.label = Steuerung +channel-type.velux.control.description = Steuerungselement (hoch/runter/stop). +# +# end-of-ESH-INF/i18n/velux_de.properties +# diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties new file mode 100644 index 0000000000000..a3e3e8b861509 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_dk.properties @@ -0,0 +1,172 @@ +# +# Copyright (c) 2010-2019 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 +# +# @author Guenther Schreiner - Initial contribution. +# +# Binding descriptions +# +binding.velux.name = Velux-binding +binding.velux.description = En binding til Velux KLF200 Bridge. Velux Binding interagerer via en Velux Bridge med de forskellige Velux-enheder, såsom styring af vinduesåbnere, skodder og andre. F.eks. Kan en KLF200 fungere som interface mellem HomeAutomation og VELUX INTEGRA-produkter med trådløs tilslutning baseret på io-homecontrol-standarden. +# +# Bridge Thing typer beskrivelser +# +bridge-type.velux.klf200.label = Velux KLF200 Bridge +bridge-type.velux.klf200.description = Velux KLF200 repræsenterer en gateway til alle Velux resp. io-homecontrol-enheder. +# +# Beskrivelse af tingstyper +# +thing-type.velux.actuator.label = Velux-aktuatorinformation +thing-type.velux.actuator.description = Styr en aktuator via Velux KLF 200 +# +thing-type.velux.rollershutter.label = Velux-rullershutterinformation +thing-type.velux.rollershutter.description = Styr en rullehjul på Velux KLF 200 +# +thing-type.velux.scene.label = Velux-sceneoplysninger +thing-type.velux.scene.description = Styr en scene på Velux KLF 200 +# +thing-type.velux.vshutter.label = Information om virtuel lukker +thing-type.velux.vshutter.description = Et sæt scener, der fungerer sammen som lukker +# +thing-type.velux.window.label = Velux-vinduesinformation +thing-type.velux.window.description = Styr et vindue på Velux KLF 200 +# +# Discovery descriptions +# +discovery.velux.binding...label = Velux Binding Oplysninger Element +# +# Konfig-beskrivelser +# +config.velux.bridge.ipAddress.label = IP-adresse +config.velux.bridge.ipAddress.description = IP-adressen på Velux Bridge. +config.velux.bridge.protocol.label = Protokol +config.velux.bridge.protocol.description = Forbindelsesprotokollen for at kontakte Velux Bridge med (enten slip eller http eller https). +config.velux.bridge.tcpPort.label = Port +config.velux.bridge.tcpPort.description = TCP-port på Velux-gateway. +config.velux.bridge.password.label = Adgangskode +config.velux.bridge.password.description = Adgangskode til godkendelse mod Velux Bridge. +config.velux.bridge.timeoutMsecs.label = Timeout +config.velux.bridge.timeoutMsecs.description = Timeout for startforbindelse i millisekunder. +config.velux.bridge.retries.label = Tilslutningsforsøg igen +config.velux.bridge.retries.description = Antal forsøg med timing langs algoritmen Binary Exponential Backoff (BEB). +config.velux.bridge.refreshMsecs.label = Opdater interval +config.velux.bridge.refreshMsecs.description = Opdater interval i millisekunder. +config.velux.bridge.isBulkRetrievalEnabled.label = Aktivér masseopsamling +config.velux.bridge.isBulkRetrievalEnabled.description = Hent de komplette aktuatoroplysninger i en bulk. +config.velux.bridge.isSequentialEnforced.label = Håndhæv sekventiel tilstand +config.velux.bridge.isSequentialEnforced.description = Håndhæv sekventiel aktuatorstyring. Bestem driftsmåden for handlinger i lang tid som at køre kommandoer eller aktivering af scener. Paralleliteten deaktiverer imidlertid den dybdegående protokolhåndtryksbehandling, som ikke påvirker eller begrænser nogen funktionaliteter. +config.velux.bridge.isProtocolTraceEnabled.label = Protocol Insight +config.velux.bridge.isProtocolTraceEnabled.description = Aktiverer KLF200-protokoldetaljer. +# +config.velux.thing.scene.sceneName.label = Scenavn +config.velux.thing.scene.sceneName.description = Navn på den scene, der skal håndteres. +config.velux.thing.scene.velocity.label = Velocity +config.velux.thing.scene.velocity.description = Hastighedsniveau. +# +config.velux.thing.actuator.serial.label = Serienummer +config.velux.thing.actuator.serial.description = Otte hexcifre (dvs. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.actuator.name.label = Navn +config.velux.thing.actuator.name.description = (Valgfrit) Navn på aktuatoren som defineret i gateway. +config.velux.thing.actuator.inverted.label = Aktuatorposition Inverteret +config.velux.thing.actuator.inverted.description = Aktuatorpositionen er omvendt (dvs. 0% oversættes til 100%). +config.velux.thing.actuator.limitMinimum.label = Min. Begrænsningsposition +config.velux.thing.actuator.limitMinimum.description = Min. Aktuatorens begrænsningsposition. +config.velux.thing.actuator.limitMaximum.label = Max. Begrænsningsposition +config.velux.thing.actuator.limitMaximum.description = Max. Aktuatorens begrænsningsposition. +# +config.velux.thing.rollershutter.serial.label = Serienummer +config.velux.thing.rollershutter.serial.description = Otte hex-cifre (dvs. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.rollershutter.name.label = Navn +config.velux.thing.rollershutter.name.description = (Valgfrit) Navn på rulleskodder som defineret i gateway. +config.velux.thing.rollershutter.inverted.label = Rulleskodderposition Inverteret +config.velux.thing.rollershutter.inverted.description = Rulleskodderposition er omvendt (dvs. 0% oversættes til 100%). +config.velux.thing.rollershutter.limitMinimum.label = Min. Begrænsningsposition +config.velux.thing.rollershutter.limitMinimum.description = Min. Rulleskodders begrænsningsposition. +config.velux.thing.rollershutter.limitMaximum.label = Max. Begrænsningsposition +config.velux.thing.rollershutter.limitMaximum.description = Max. Rulleskodders begrænsningsposition. +# +config.velux.thing.window.serial.label = Serienummer +config.velux.thing.window.serial.description = Otte hexcifre (dvs. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.window.name.label = Navn +config.velux.thing.window.name.description = (Valgfrit) Navn på vindue som defineret i gatewayen. +config.velux.thing.window.inverted.label = Vindueposition Inverteret +config.velux.thing.window.inverted.description = Vinduepositionen er omvendt (dvs. 0% oversættes til 100%). +config.velux.thing.window.limitMinimum.label = Min. Begrænsningsposition +config.velux.thing.window.limitMinimum.description = Min. Vindues begrænsningsposition. +config.velux.thing.window.limitMaximum.label = Max. Begrænsningsposition +config.velux.thing.window.limitMaximum.description = Max. Vindues begrænsningsposition. +# +config.velux.thing.vshutter.sceneLevels.label = Liste over positioner / scener +config.velux.thing.vshutter.sceneLevels.description = Definition af en virtuel lukker ved at erklære en scene, der svarer til en position. Derfor ser denne parameter ud: , , , +config.velux.thing.vshutter.currentLevel.label = Aktuel position +config.velux.thing.vshutter.currentLevel.description = Værdi mellem 0 og 100. +# +# Channel types descriptions +# +channel-type.velux.status.label = Bridge State +channel-type.velux.status.description = Beskrivelse af den aktuelle Bridge State. +channel-type.velux.reload.label = Genindlæs +channel-type.velux.reload.description = Opdater broinformation. +channel-type.velux.downtime.label = Nedetid +channel-type.velux.downtime.description = Tidsinterval mellem sidst vellykkede og nylige enhedsinteraktion. +channel-type.velux.doDetection.label = Aktivér Bridge Detection-tilstand +channel-type.velux.doDetection.description = Start af produktdetekteringstilstand. +channel-type.velux.firmware.label = Firmwareversion +channel-type.velux.firmware.description = Softwareversion af Bridge. +channel-type.velux.ipAddress.label = IP-adresse +channel-type.velux.ipAddress.description = IP-adressen på Bridge. +channel-type.velux.subnetMask.label = IP-subnetmaske +channel-type.velux.subnetMask.description = IP-subnetmaske af Bridge. +channel-type.velux.defaultGW.label = Standard Gateway +channel-type.velux.defaultGW.description = IP-adresse til standard Gateway of the Bridge. +channel-type.velux.DHCP.label = DHCP aktiveret +channel-type.velux.DHCP.description = Marker, om automatisk IP-konfiguration er aktiveret. +channel-type.velux.WLANSSID.label = SSID +channel-type.velux.WLANSSID.description = Navn på det trådløse netværk. +channel-type.velux.WLANPassword.label = WLAN-godkendelse +channel-type.velux.WLANPassword.description = WLAN-godkendelsesadgangskode. +channel-type.velux.products.label = Identificerede produkter +channel-type.velux.products.description = Produkter, der er registreret på Bridge. +channel-type.velux.scenes.label = Identificerede scener +channel-type.velux.scenes.description = Scener, der er konfigureret på Bridge. +channel-type.velux.check.label = Kontrol af konfiguration +channel-type.velux.check.description = Resultat af kontrollen med den aktuelle varekonfiguration. +# +channel-type.velux.position.label = Position +channel-type.velux.position.description = Enhedskontrol (OP, NED, STOP, lukning 0-100%). +channel-type.velux.state.label = State +channel-type.velux.state.description = Device control (ON, OFF). +channel-type.velux.action.label = Start af en scene +channel-type.velux.action.description = Aktiverer et sæt foruddefinerede produktindstillinger. +channel-type.velux.silentMode.label = Aktiverer lydtilstand +channel-type.velux.silentMode.description = Aktiverer lydløs tilstand for de foruddefinerede produktindstillinger. +channel-type.velux.limitMinimum.label = Min. Begrænsningsposition +channel-type.velux.limitMinimum.description = Min. Aktuatorens/Rulleskodders/Vindues begrænsningsposition. +channel-type.velux.limitMaximum.label = Max. Begrænsningsposition +channel-type.velux.limitMaximum.description = Max. Begrænsningsposition +channel-type.velux.vposition.label = Position +channel-type.velux.vposition.description = Enhedskontrol (OP, NED, STOP, lukning 0-100%). +# +# Statusbeskrivelser for kørsel +# +runtime.no-bridge = Indtil videre er intet koblingselement defineret. Tilføj en "Velux KLF200" ting for at etablere en forbindelse til koblingselementet, hvilket er forudsætningen for yderligere idriftsættelse. +runtime.one-bridge = Et koblingselement er allerede defineret. Således kan du nu konfigurere yderligere enheder ved hjælp af søgning (eller opdagelse) eller ved at tilføje ting manuelt. +runtime.multiple-bridges = Der er mere end et koblingselement defineret. Dette er selvfølgelig muligt med flere Velux KLF200 gateways eller med en redundant opsætning (SLIP via LAN parallel med JSON via WLAN). Alle andre tilfælde bør undgås. +runtime.bridge-offline-no-valid-bridgeProtocol = Parameteren "bridgeProtocol" skal indstilles korrekt. +runtime.bridge-offline-login -sequence-failed = Login-sekvens mislykkedes. +# +# Statusbeskrivelser +# +channelValue.check-integrity-failed = Integritetskontrol mislykkedes: Følgende scener er ubrugte: +channelValue.check-integrity-ok = Integrity check ok. Alle scener bruges i artikler. +# +# end-of-ESH-INF/i18n/velux.properties +# diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties new file mode 100644 index 0000000000000..e4b3cd32d581c --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/i18n/velux_nl.properties @@ -0,0 +1,172 @@ +# +# Copyright (c) 2010-2019 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 +# +# @author Guenther Schreiner - Initial contribution. +# +# Binding descriptions +# +binding.velux.name = Velux binding +binding.velux.description = Een binding voor de Velux KLF200 Bridge. De Velux-binding werkt via een Velux-brug samen met de verschillende Velux-apparaten, zoals het bedienen van vensteropeners, rolluiken en andere. Een KLF200 kan bijvoorbeeld fungeren als interface tussen de HomeAutomation en de VELUX INTEGRA-producten met draadloze connectiviteit op basis van de io-homecontrol-standaard. +# +# Beschrijvingen van Bridge Thing-typen +# +bridge-type.velux.klf200.label = Velux KLF200-brug +bridge-type.velux.klf200.description = De Velux KLF200 vertegenwoordigt een toegangspoort tot alle Velux resp. io-homecontrol-apparaten. +# +# Beschrijvingen van dingtypen +# +thing-type.velux.actuator.label = Velux Actuator-informatie +thing-type.velux.actuator.description = Bedien een actuator via de Velux KLF 200 +# +thing-type.velux.rollershutter.label = Velux Rollershutter-informatie +thing-type.velux.rollershutter.description = Bedien een rolluik op de Velux KLF 200 +# +thing-type.velux.scene.label = Velux Scene-informatie +thing-type.velux.scene.description = Bedien een scène op de Velux KLF 200 +# +thing-type.velux.vshutter.label = Virtuele sluiterinformatie +thing-type.velux.vshutter.description = Een set scènes die samenwerken als sluiter +# +thing-type.velux.window.label = Velux-vensterinformatie +thing-type.velux.window.description = Bedien een venster op de Velux KLF 200 +# +# Discovery descriptions +# +discovery.velux.binding...label = Velux Binding Informatie-Element +# +# Config beschrijvingen +# +config.velux.bridge.ipAddress.label = IP-adres +config.velux.bridge.ipAddress.description = Het IP-adres van de Velux Bridge. +config.velux.bridge.protocol.label = Protocol +config.velux.bridge.protocol.description = Het verbindingsprotocol waarmee contact wordt gemaakt met de Velux Bridge (slip of http of https). +config.velux.bridge.tcpPort.label = Poort +config.velux.bridge.tcpPort.description = TCP-poort van de Velux-gateway. +config.velux.bridge.password.label = Wachtwoord +config.velux.bridge.password.description = Wachtwoord voor authenticatie tegen de Velux Bridge. +config.velux.bridge.timeoutMsecs.label = Timeout +config.velux.bridge.timeoutMsecs.description = Eerste time-out van de verbinding in milliseconden. +config.velux.bridge.retries.label = Opnieuw proberen verbinding +config.velux.bridge.retries.description = Aantal nieuwe pogingen met timing langs het Binary Exponential Backoff (BEB) algoritme. +config.velux.bridge.refreshMsecs.label = Vernieuwingsinterval +config.velux.bridge.refreshMsecs.description = Vernieuwingsinterval in milliseconden. +config.velux.bridge.isBulkRetrievalEnabled.label = Bulk ophalen inschakelen +config.velux.bridge.isBulkRetrievalEnabled.description = Haal de volledige actuatorinformatie in bulk op. +config.velux.bridge.isSequentialEnforced.label = Opeenvolgende modus afdwingen +config.velux.bridge.isSequentialEnforced.description = Handhaving van sequentiële actuatorcontrole. Bepaal de werkingsmodus voor langdurige acties zoals het uitvoeren van opdrachten of het activeren van scènes. Het parallellisme schakelt echter de diepgaande protocolhandshake-verwerking uit die geen enkele functionaliteit beïnvloedt of beperkt. +config.velux.bridge.isProtocolTraceEnabled.label = Protocol Insight +config.velux.bridge.isProtocolTraceEnabled.description = Schakelt KLF200-protocoldetails in. +# +config.velux.thing.scene.sceneName.label = Scènenaam +config.velux.thing.scene.sceneName.description = Naam van de te behandelen scène. +config.velux.thing.scene.velocity.label = Snelheid +config.velux.thing.scene.velocity.description = Velocity Level. +# +config.velux.thing.actuator.serial.label = Serienummer +config.velux.thing.actuator.serial.description = Acht hexadecimale cijfers (d.w.z. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.actuator.name.label = Naam +config.velux.thing.actuator.name.description = (Optioneel) Naam van de actuator zoals gedefinieerd in de gateway. +config.velux.thing.actuator.inverted.label = Actuatorpositie omgekeerd +config.velux.thing.actuator.inverted.description = De actuatorpositie is omgekeerd (d.w.z. 0% vertaalt zich naar 100%). +config.velux.thing.actuator.limitMinimum.label = Min. Beperkingspositie +config.velux.thing.actuator.limitMinimum.description = Min. De beperkingspositie van de actuator. +config.velux.thing.actuator.limitMaximum.label = Max. Beperkingspositie +config.velux.thing.actuator.limitMaximum.description = Max. De beperkingspositie van de actuator. +# +config.velux.thing.rollershutter.serial.label = Serienummer +config.velux.thing.rollershutter.serial.description = Acht hexadecimale cijfers (d.w.z. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.rollershutter.name.label = Naam +config.velux.thing.rollershutter.name.description = (Optioneel) Naam van de rolluik zoals gedefinieerd in de gateway. +config.velux.thing.rollershutter.inverted.label = Rolluikpositie omgekeerd +config.velux.thing.rollershutter.inverted.description = De Rolluikpositie is omgekeerd (d.w.z. 0% vertaalt zich naar 100%). +config.velux.thing.rollershutter.limitMinimum.label = Min. Beperkingspositie +config.velux.thing.rollershutter.limitMinimum.description = Min. De beperkingspositie van de rolluik. +config.velux.thing.rollershutter.limitMaximum.label = Max. Beperkingspositie +config.velux.thing.rollershutter.limitMaximum.description = Max. De beperkingspositie van de rolluik. +# +config.velux.thing.window.serial.label = Serienummer +config.velux.thing.window.serial.description = Acht hexadecimale cijfers (d.w.z. 65:23:3E:26:0C:1B:00:10). +config.velux.thing.window.name.label = Naam +config.velux.thing.window.name.description = (Optioneel) Naam van de venster zoals gedefinieerd in de gateway. +config.velux.thing.window.inverted.label = Vensterpositie omgekeerd +config.velux.thing.window.inverted.description = De Vensterpositie is omgekeerd (d.w.z. 0% vertaalt zich naar 100%). +config.velux.thing.window.limitMinimum.label = Min. Beperkingspositie +config.velux.thing.window.limitMinimum.description = Min. De beperkingspositie van de venster. +config.velux.thing.window.limitMaximum.label = Max. Beperkingspositie +config.velux.thing.window.limitMaximum.description = Max. De beperkingspositie van de venster. +# +config.velux.thing.vshutter.sceneLevels.label = Lijst met posities / scènes +config.velux.thing.vshutter.sceneLevels.description = Definitie van een virtuele sluiter door een scène aan te geven die overeenkomt met een positie. Daarom ziet deze parameter er als volgt uit: , , , +config.velux.thing.vshutter.currentLevel.label = Huidige positie +thing.vshutter.currentLevel.description = Waarde tussen 0 en 100. +# +# Kanaaltypebeschrijvingen +# +channel-type.velux.status.label = Bridge State +channel-type.velux.status.description = Beschrijving van huidige brugstatus. +channel-type.velux.reload.label = Herladen +channel-type.velux.reload.description = Bruginformatie opnieuw laden. +channel-type.velux.downtime.label = Downtime. +channel-type.velux.downtime.description = tussen laatste succesvolle en recente apparaatinteractie. +channel-type.velux.doDetection.label = Activeer de brugdetectiemodus +channel-type.velux.doDetection.description = Start van de productdetectiemodus. +channel-type.velux.firmware.label = Firmwareversie +channel-type.velux.firmware.description = Softwareversie van de Bridge. +channel-type.velux.ipAddress.label = IP-adres +channel-type.velux.ipAddress.description = IP-adres van de Bridge. +channel-type.velux.subnetMask.label = IP-subnetmasker +channel-type.velux.subnetMask.description = IP-subnetmasker van de brug. +channel-type.velux.defaultGW.label = Standaard gateway +channel-type.velux.defaultGW.description = IP-adres van de standaardgateway van de brug. +channel-type.velux.DHCP.label = DHCP ingeschakeld +channel-type.velux.DHCP.description = Markeren of automatische IP-configuratie is ingeschakeld. +channel-type.velux.WLANSSID.label = SSID +channel-type.velux.WLANSSID.description = Naam van het draadloze netwerk. +channel-type.velux.WLANPassword.label = WLAN-verificatie +channel-type.velux.WLANPassword.description = WLAN-verificatiewachtwoord. +channel-type.velux.products.label = Geïdentificeerde producten +channel-type.velux.products.description = Producten die op de Bridge zijn geregistreerd. +channel-type.velux.scenes.label = Geïdentificeerde scènes +channel-type.velux.scenes.description = Scènes die op de Bridge zijn geconfigureerd. +channel-type.velux.check.label = Controle van configuratie +channel-type.velux.check.description = Resultaat van de controle van de huidige itemconfiguratie. +# +channel-type.velux.position.label = Positie +channel-type.velux.position.description = Apparaatbeheer (OMHOOG, OMLAAG, STOP, sluiting 0-100%). +channel-type.velux.state.label = State +channel-type.velux.state.description = Device control (ON, OFF). +channel-type.velux.action.label = Start van een scène +channel-type.velux.action.description = Activeert een set vooraf gedefinieerde productinstellingen. +channel-type.velux.silentMode.label = Stille modus inschakelen +channel-type.velux.silentMode.description = Activeert de stille modus van de vooraf gedefinieerde productinstellingen. +channel-type.velux.limitMinimum.label = Min. Beperkingspositie +channel-type.velux.limitMinimum.description = Min. de beperkingspositie van de actuator/rolluik/venster. +channel-type.velux.limitMaximum.label = Max. Beperkingspositie +channel-type.velux.limitMaximum.description = Max. de De beperkingspositie van de actuator/rolluik/venster +channel-type.velux.vposition.label = Positie +channel-type.velux.vposition.description = Apparaatbeheer (OMHOOG, OMLAAG, STOP, sluiting 0-100%). +# +# Beschrijvingen van de runtimestatus +# +runtime.no-bridge = Tot nu toe is er geen koppelingselement gedefinieerd. Voeg een "Velux KLF200" toe om een verbinding met het koppelingselement tot stand te brengen, wat een voorwaarde is voor verdere inbedrijfstelling. +runtime.one-bridge = Een koppelelement is al gedefinieerd. Je kunt nu dus extra apparaten instellen door te zoeken (of te zoeken) of door dingen handmatig toe te voegen. +runtime.multiple-bridges = Er zijn meer dan één koppelelement gedefinieerd. Dit is natuurlijk mogelijk met verschillende Velux KLF200-gateways of met een redundante installatie (SLIP via LAN parallel aan JSON via WLAN). Elk ander geval moet worden vermeden. +runtime.bridge-offline-no-valid-bridgeProtocol-selected = De parameter "bridgeProtocol" moet correct worden ingesteld. +runtime.bridge-offline-login-sequence-failure = Aanmeldingsreeks mislukt. +# +# Ding statusbeschrijvingen +# +channelValue.check-integrity-failed = Integriteitscontrole mislukt: de volgende scènes worden niet gebruikt: +channelValue.check-integrity-ok = Integriteitscontrole ok. Alle scènes worden gebruikt binnen Items. +# +# end-of-ESH-INF / i18n / velux_nl.properties +# diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/actuator.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/actuator.xml new file mode 100644 index 0000000000000..129311b2616b3 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/actuator.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + @text/thing-type.velux.actuator.description + Blinds + + + + + + + serialNumber + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/binding.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/binding.xml new file mode 100644 index 0000000000000..6089360d02210 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/binding.xml @@ -0,0 +1,21 @@ + + + + + + + + + @text/thing-type.velux.binding.description + NetworkAppliance + + + + + N/A + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml new file mode 100644 index 0000000000000..66bb7587275d7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/bridge.xml @@ -0,0 +1,40 @@ + + + + + + + + @text/bridge-type.velux.klf200.description + + + + + + + + + + Velux + + + + + + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml new file mode 100644 index 0000000000000..8eb7d33c6d8f7 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/channels.xml @@ -0,0 +1,184 @@ + + + + + + + + String + + @text/channel-type.velux.information.description + NetworkAppliance + + + + + + + + String + + @text/channel-type.velux.status.description + NetworkAppliance + + + + + Switch + + @text/channel-type.velux.reload.description + NetworkAppliance + + + + Number + + @text/channel-type.velux.downtime.description + NetworkAppliance + + + + + Switch + + @text/channel-type.velux.doDetection.description + NetworkAppliance + + + + + + + + Rollershutter + + @text/channel-type.velux.position.description + Blinds + + + + Switch + + @text/channel-type.velux.state.description + WallSwitch + + + + Rollershutter + + @text/channel-type.velux.limitMinimum.description + Blinds + + + + Rollershutter + + @text/channel-type.velux.limitMaximum.description + Blinds + + + + Switch + + @text/channel-type.velux.action.description + WallSwitch + + + + Switch + + @text/channel-type.velux.silentMode.description + Blinds + + + + Rollershutter + + @text/channel-type.velux.vposition.description + Blinds + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/rollershutter.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/rollershutter.xml new file mode 100644 index 0000000000000..c4d59c0aeb348 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/rollershutter.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + @text/thing-type.velux.rollershutter.description + Blinds + + + + + + unique + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/scene.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/scene.xml new file mode 100644 index 0000000000000..1bce1894caadc --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/scene.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + @text/thing-type.velux.scene.description + Blinds + + + + + unique + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/vshutter.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/vshutter.xml new file mode 100644 index 0000000000000..7e77495b67fd3 --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/vshutter.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + @text/thing-type.velux.vshutter.description + Blinds + + + + + + diff --git a/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/window.xml b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/window.xml new file mode 100644 index 0000000000000..7a04b12aa877b --- /dev/null +++ b/bundles/org.openhab.binding.velux/src/main/resources/ESH-INF/thing/window.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + @text/thing-type.velux.window.description + Window + + + + + + serialNumber + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index 233ac15c823fd..884b12352e1f5 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -218,6 +218,7 @@ org.openhab.binding.valloxmv org.openhab.binding.vektiva org.openhab.binding.velbus + org.openhab.binding.velux org.openhab.binding.vitotronic org.openhab.binding.volvooncall org.openhab.binding.weathercompany