diff --git a/CODEOWNERS b/CODEOWNERS index d1e56d4ea..7a0fd608b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -283,6 +283,7 @@ /bundles/org.openhab.binding.sonos/ @kgoderis @lolodomo /bundles/org.openhab.binding.sonyaudio/ @freke /bundles/org.openhab.binding.sonyprojector/ @lolodomo +/bundles/org.openhab.binding.souliss/ @lucacalcaterra @fazioa /bundles/org.openhab.binding.spotify/ @Hilbrand /bundles/org.openhab.binding.squeezebox/ @digitaldan @mhilbush /bundles/org.openhab.binding.surepetcare/ @renescherer @HerzScheisse diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 9e4a4b9d4..4353c4027 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1393,6 +1393,11 @@ org.openhab.binding.sonyprojector ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.souliss + ${project.version} + org.openhab.addons.bundles org.openhab.binding.spotify diff --git a/bundles/org.openhab.binding.souliss/NOTICE b/bundles/org.openhab.binding.souliss/NOTICE new file mode 100644 index 000000000..38d625e34 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-addons diff --git a/bundles/org.openhab.binding.souliss/README.md b/bundles/org.openhab.binding.souliss/README.md new file mode 100644 index 000000000..c9de7cae3 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/README.md @@ -0,0 +1,321 @@ +# Souliss Binding + +[Souliss](http://www.souliss.net/) is a networking framework for Arduino and compatibles boards, and is designed to let you easily build a smart home that is distributed over multiple boards via Ethernet, WiFi, wireless point-to-point and RS485 bus. + +Souliss is an open-source and community driven project, you can use the [wiki](https://github.com/souliss/souliss/wiki) and [Community](https://github.com/souliss/souliss/wiki/Community) to get help and share your results. + +## Prerequisites + +The binding requires a deployed network. +As a minimum, you need one Souliss node with Ethernet or WiFi access configured as a [Gateway](https://github.com/souliss/souliss/wiki/Gateway). +A Gateway is a special node that is able to communicate with the user interfaces. +The binding interacts as a user interface for Souliss. + +A starting point is the [Souliss wiki](https://github.com/souliss/souliss/wiki). +The best is to start with a single node and connect with SoulissApp. +The code for networking activities of this binding is based on [SoulissApp](https://github.com/souliss/souliss/wiki/SoulissApp) code, so once connected with SoulissApp, you can move to openHAB directly. + +You can use SoulissApp and the Souliss binding at the same time, and generally up to five (by default, but can be increased) user interfaces simultaneously. + +### Sketches + +The easiest way is start with a simple example to control an ON/OFF light (though a relay). +You can go to project [Souliss](https://github.com/souliss/souliss), see a lot of examples sketches: [Souliss examples](https://github.com/souliss/souliss/tree/friariello/examples) + + +## Discovery + +First add a gateway (one only is permitted on LAN at this moment), then discovery can find other things (Souliss Typicals) + +## Supported Things + +In Souliss Framework a Typical is one of predefined logic dedicated to smart home devices like lights, heating or antitheft. + +Typical can be one of T11, T12, T13, T14, etc... + +They are defined [here](https://github.com/souliss/souliss/wiki/Typicals). + +Typicals match directly with openHAB Thing type. + +| Device type | Typical Code | Thing type | +|-----------------------------------------------------------|----------------|------------| +| ON/OFF Digital Output with Timer Option | T11 | t11 | +| ON/OFF Digital Output with AUTO mode | T12 | t12 | +| Digital Input Value | T13 | t13 | +| Pulse Digital Output | T14 | t14 | +| RGB LED Strip | T16 | t16 | +| ON/OFF Digital Output | T18 | t18 | +| Single Color LED Strip | T19 | t19 | +| Digital Input Pass Through | T1A | t1A | +| Motorized devices with limit switches | T21 | t21 | +| Motorized devices with limit switches and middle position | T22 | t22 | +| Temperature control | T31 | t31 | +| Anti-theft integration (Main) | T41 | t41 | +| Anti-theft integration (Peer) | T42 | t42 | +| Analog input, half-precision floating point | T51 | t51 | +| Temperature measure (-20, +50) °C | T52 | t52 | +| Humidity measure (0, 100) % | T53 | t53 | +| Light Sensor (0, 40) kLux | T54 | t54 | +| Voltage (0, 400) V | T55 | t55 | +| Current (0, 25) A | T56 | t56 | +| Power (0, 6500) W | T57 | t57 | +| Pressure measure (0, 1500) hPa | T58 | t58 | +| Analog Setpoint | T61 | t61 | +| Analog Setpoint-Temperature measure (-20, +50) °C | T62 | t62 | +| Analog Setpoint-Humidity measure (0, 100) % | T63 | t63 | +| Analog Setpoint-Light Sensor (0, 40) kLux | T64 | t64 | +| Analog Setpoint-Voltage (0, 400) V | T65 | t65 | +| Analog Setpoint-Current (0, 25) A | T66 | t66 | +| Analog Setpoint-Power (0, 6500) W | T67 | t67 | +| Analog Setpoint-Pressure measure (0, 1500) hPa | T68 | t68 | +| Broadcast messages | Action Message | topic | + +### Channels + +The following matrix lists the capabilities (channels) for each type: + +| Thing type / Channel | Switch / onOff | Switch / sleep | DateTime / lastStatusStored | Number / healthy | Switch / autoMode | Contact / stateOnOff | Contact / stateOpenClose | Switch / pulse | Switch / whiteMode | Rollershutter / rollerBrightness | Dimmer / dimmerBrightness | Color / ledColor | Switch / one | Switch / two | Switch / three | Switch / four | Switch / five | Switch / six | Switch / seven | Switch / eight | +|----------------------|----------------|----------------|-----------------------------|------------------|-------------------|----------------------|--------------------------|----------------|--------------------|----------------------------------|---------------------------|------------------|--------------|--------------|----------------|---------------|---------------|--------------|----------------|----------------| +| t11 | x | x | x | x | | | | | | | | | | | | | | | | | +| t12 | x | | x | x | x | | | | | | | | | | | | | | | | +| t13 | | | x | x | | x | x | | | | | | | | | | | | | | +| t14 | | | x | x | | | | x | | | | | | | | | | | | | +| t16 | x | x | x | x | | | | | x | x | x | x | | | | | | | | | +| t18 | x | | x | x | | | | | | | | | | | | | | | | | +| t19 | x | x | x | x | | | | | | x | x | | | | | | | | | | +| t1A | | | x | x | | | | | | | | | x | x | x | x | x | x | x | x | + +| Thing type / Channel | DateTime / lastStatusStored | Number / healthy | Rollershutter / rollerShutter | (see below) / rollerShutterState | (see down) / mode | (see down) / fan | Switch / status | Number / setPoint | Switch / setAsMeasured | Switch / measured | Switch / statusAlarm | Switch / onOffAlarm | Switch / rearmAlarm | +|----------------------|-----------------------------|------------------|-------------------------------|----------------------------------|-------------------|------------------|-----------------|-------------------|------------------------|-------------------|----------------------|---------------------|---------------------| +| t21 | x | x | | x | | | | | | | | | | +| t22 | x | x | x | x | | | | | | | | | | +| t31 | x | x | | | x | x | x | x | x | x | | | | +| t41 | x | x | | | | | | | | | x | x | x | +| t42 | x | x | | | | | | | | | x | | x | + +rollershutterstate = opening, closing, limSwitchOpen , limSwitchClose, stateOpen, stateClose, noLimSwitch + +mode = COOLING_MODE, HEATING_MODE, POWEREDOFF_MODE + +fan = AUTO, HIGH, MEDIUM, LOW, FANOFF + + +| Thing type / Channel | DateTime / lastStatusStored | Number / healthy | Number / value | +|----------------------|-----------------------------|------------------|----------------| +| t51 | x | x | x | +| t52 | x | x | x | +| t53 | x | x | x | +| t54 | x | x | x | +| t55 | x | x | x | +| t56 | x | x | x | +| t57 | x | x | x | +| t58 | x | x | x | + +| Thing type / Channel | DateTime / lastStatusStored | Number / healthy | Number / value | +|----------------------|-----------------------------|------------------|----------------| +| t61 | x | x | x | +| t62 | x | x | x | +| t63 | x | x | x | +| t64 | x | x | x | +| t65 | x | x | x | +| t66 | x | x | x | +| t67 | x | x | x | +| t68 | x | x | x | +| topic | x | | x | + +### Parameters + +| Thing type | Parameters Name and Default Value | Description | +|------------|-----------------------------------|-------------------------------------------------------------------------------------------------------| +| Gateway | gatewayLanAddress="" | Mandatory - lan address of Gateway | +| " | gatewayWanAddress="" | (advanced) When gateway is outside local network can insert domain/ip in this field | +| " | gatewayPortNumber=230 | (advanced) Gateway UDP Port | +| " | preferredLocalPortNumber=23000 | (advanced) Local UDP Port | +| " | pingInterval=30 | (advanced) Interval in seconds to check for device presence | +| " | subscriptionInterval=2 | (advanced) Interval in minutes to subscribe Souliss Gateway | +| " | healthyInterval=35 | (advanced) Interval in seconds to send nodes healthy | +| " | userIndex=70 | (advanced) Generally the default is good. It must be different from other ui (ex: SoulissApp) | +| " | nodeIndex=120 | (advanced) Generally the default value work good. It must is different from other ui (ex: SoulissApp) | +| Txx (all) | Node | Node of typical | +| Txx (all) | Slot | Slot of typical | +| T11 | sleep=5 | Set sleep timer in cycles | +| T11 | secureSend=true | Ensure command is correctly executed | +| T12 | | | +| T13 | | | +| T14 | | | +| T16 | sleep=5 | Set sleep timer in cycles | +| T19 | sleep=5 | Set sleep timer in cycles | +| T1A | | | +| T21 | | | +| T22 | | | +| T31 | | | +| T4x | | | +| T5x | | | +| T6x | | | + + +## Full Example + + + +souliss.things: + +``` +Bridge souliss:gateway:105 "Souliss Gateway - 105" [gatewayLanAddress="192.168.1.105", gatewayPortNumber=230, preferredLocalPortNumber=0, pingInterval=30, subscriptionInterval=2, healthyInterval=38, userIndex=72, nodeIndex=38, timeoutToRequeue=5000, timeoutToRemovePacket=20000] +{ +Thing t14 1-6 "Portoncino"@"Rientro" [node=1,slot=6] //thing UID is named as node-slot only as mnemonic convention, but you are free to assign other values +Thing t14 1-7 "Cancello"@"Rientro" [node=1,slot=7] +Thing t57 1-4 "Consumo"@"Soggiorno" [node=1,slot=4] +Thing t57 4-0 "Fotovoltaico"@"Soggiorno" [node=4,slot=0] +Thing t57 4-6 "Pannelli Gruppo 1"@"Soggiorno" [node=4,slot=6] +Thing t57 4-8 "Pannelli Gruppo 2"@"Soggiorno" [node=4,slot=8] +Thing t52 4-10 "Temp.Pannelli Gruppo 1"@"Soggiorno" [node=4,slot=10] +Thing t52 4-12 "Temp.Pannelli Gruppo 2"@"Soggiorno" [node=4,slot=12] + +Thing t52 3-0 "Temperatura Boiler Solare Termico" [node=3,slot=0] +Thing t52 3-2 "Temperatura Termocamino" [node=3,slot=2] +Thing t11 3-4 "Acqua Termocamino" [node=3,slot=4] +Thing t11 3-6 "Auto: Boiler / Termocamino" [node=3,slot=6] +Thing t31 3-7 "Acqua Auto: Boiler / Termocamino" [node=3,slot=7] + +Thing t31 6-0 "Termostato Soggiorno"@"Soggiorno" [node=6,slot=0] +Thing t53 6-7 "Umidità"@"Soggiorno" [node=6,slot=7] +Thing t19 6-9 "Termostato Soggiorno - Luminosità"@"Soggiorno" [node=6,slot=9] + +Thing t11 5-0 "Tettoia"@"Giardino" [node=5,slot=0] + +Thing t11 12-0 "Divano"@"Soggiorno" [node=12,slot=0,sleep=10, secureSend=false] + +Thing t16 8-0 "LYT1" [node=8,slot=0] + +Thing t11 10-0 "Albero di Natale" [node=10,slot=0] +Thing t11 11-0 "Birra"@"Soppalco" [node=11,slot=0] +Thing t52 11-1 "Birra - Temp 1"@"Soppalco" [node=11,slot=1] +Thing t52 11-3 "Birra - Temp 2"@"Soppalco" [node=11,slot=3] +} + +``` + +You have to write your Gateway IP Number and leave all other to default values + + +default.items: + +``` +Group Home "Tonino" + +Group FamilyRoom "Soggiorno" (Home) +Group Divano "Divano" (Home) +Group Outside "Esterno" (Home) +Group TV "TV" (Home) +Group Elettricita +Group Diagnostic +Group TermostatoSoggiorno + +Switch tettoia "Tettoia" (Outside) ["Lighting"] {autoupdate="false", channel="souliss:t11:105:5-0:onoff"} +String tettoia_aggiornamento "Agg [%1$td.%1$tm.%1$tY %1$tk:%1$tM:%1$tS]" (Outside, Diagnostic) {channel="souliss:t31:105:5-0:lastStatusStored"} + +Switch portoncino "Portoncino" (FamilyRoom) ["Lighting"] {autoupdate="false",channel="souliss:t14:105:1-6:pulse"} +Switch cancello "Cancello" (FamilyRoom) ["Lighting"] {autoupdate="false",channel="souliss:t14:105:1-7:pulse"} + +Number FamilyRoom_Temperature "Temperatura [%.1f °C]" (FamilyRoom) {channel="souliss:t31:105:6-0:measured"} +Number FamilyRoom_Humidity "Umidità [%.1f %%]" (FamilyRoom) {channel="souliss:t53:105:6-7:value"} +String AggiornamentoNodo6 "Agg [%1$td.%1$tm.%1$tY %1$tk:%1$tM:%1$tS]" (FamilyRoom, Diagnostic) {channel="souliss:t31:105:6-0:lastStatusStored"} + +Number Consumo "Consumo [%.1f W]" (FamilyRoom, Elettricita) {channel="souliss:t57:105:1-4:value"} +Number Fotovoltaico "Fotovoltaico [%.1f W]" (FamilyRoom, Elettricita) {channel="souliss:t57:105:4-0:value"} +String AggiornamentoNodo1 "Agg.Consumi [%1$td.%1$tm.%1$tY %1$tk:%1$tM:%1$tS]" (FamilyRoom, Elettricita, Diagnostic) {channel="souliss:t57:105:1-4:lastStatusStored"} +String AggiornamentoNodo4 "Agg.Fotovoltaico [%1$td.%1$tm.%1$tY %1$tk:%1$tM:%1$tS]" (FamilyRoom, Elettricita, Diagnostic) {channel="souliss:t57:105:4-0:lastStatusStored"} + +Switch divano "Divano" (FamilyRoom, Divano ) ["Switchable"] {autoupdate="false", channel="souliss:t11:105:12-0:onOff"} +String divano_aggiornamento "Agg. [%1$td.%1$tm.%1$tY %1$tk:%1$tM:%1$tS]" (FamilyRoom, Divano, Diagnostic) {channel="souliss:t57:105:12-0:lastStatusStored"} +String divano_healthy "Salute" (FamilyRoom, Divano, Diagnostic) {channel="souliss:t57:105:12-0:healthy"} + +Number termostatosoggiorno_temperatura "Temperatura [%.1f °C]" (TermostatoSoggiorno) {channel="souliss:t31:105:6-0:measured"} +Number termostatosoggiorno_umidita "Umidità [%.1f %%]" (TermostatoSoggiorno) {channel="souliss:t53:105:6-7:value" } + +Number termostatosoggiorno_umidita "Umidità" (TermostatoSoggiorno) {channel="souliss:t53:105:6-7:value" } + +Number termostatosoggiorno_temperatura "Temperatura" (TermostatoSoggiorno) {channel="souliss:t31:105:6-0:measured"} +Number termostatosoggiorno_setpoint "Regola Set Point [%.1f °c]" (TermostatoSoggiorno) {autoupdate="false", channel="souliss:t31:105:6-0:sePpoint"} +Switch termostatosoggiorno_setasmeasured "Set temp. attuale" (TermostatoSoggiorno) {channel="souliss:t31:105:6-0:setAsMeasured"} +String termostatosoggiorno_modo "Modo" (TermostatoSoggiorno) {autoupdate="false", channel="souliss:t31:105:6-0:mode"} +Switch termostatosoggiorno_power "Termostato" (TermostatoSoggiorno) {channel="souliss:t31:105:6-0:system"} +Switch termostatosoggiorno_fire "Fire" (TermostatoSoggiorno) {channel="souliss:t31:105:6-0:fire"} + +Dimmer TermostatoSoggiorno_displayBright "Lumin.min. display" (TermostatoSoggiorno) {channel="souliss:t19:105:6-9" } +String TermostatoSoggiorno_aggiornamento "Agg.[%1$td.%1$tm.%1$tY %1$tk:%1$tM:%1$tS]" (TermostatoSoggiorno, Diagnostic) {channel="souliss:t31:105:6-0:lastStatusStored"} +Number TermostatoSoggiorno_healthy "Salute" (TermostatoSoggiorno, Diagnostic ) {channel="souliss:t31:105:6-0:healthy"} + +``` + +default.sitemaps: + +``` +sitemap default label="Tonino" { + Frame { + Text label="Rientro casa" icon="light" { + Switch item=portoncino mappings=[ON="Apri"] + Switch item=cancello mappings=[ON="Apri"] + } + } + + Frame { + Group item=Outside + } + +Text item=Consumo label="Consumo [%.1f W]" +Text item=Fotovoltaico label="Fotovoltaico [%.1f W]" + +Frame { + + Group item=Elettricita label="Elettricità" icon="energy" +} + +Frame { + Group item=Divano icon="light" +} + +Frame label="Temperature"{ + + Text label="Temperatura e umidità" icon="temperature" { + Default item=FamilyRoom_Temperature label="Temperatura" + Default item=FamilyRoom_Humidity label="Umidità" + Default item=AggiornamentoNodo6 icon="icon16x16" + +} + +Text label="Termostato soggiorno" icon="temperature" { + Setpoint item=termostatosoggiorno_setpoint step=0.5 minValue=10 maxValue=30 + Default item=termostatosoggiorno_temperatura + Default item=termostatosoggiorno_umidita + Switch item=termostatosoggiorno_setasmeasured mappings=[ON="Set"] + Switch item=termostatosoggiorno_modo label="Heating Mode" mappings=[HEATING_MODE="Set"] + Switch item=termostatosoggiorno_power label="Power On/Off" + Default item=termostatosoggiorno_fire label="Fire" + Text item=termostatoSoggiorno_aggiornamento label="Aggiornato: [%1$td.%1$tm.%1$tY %1$tk:%1$tM:%1$tS]" icon="icon16x16" + Default item=termostatoSoggiorno_healthy + Slider item=termostatoSoggiorno_displayBright + } + } +} +``` +## Community + +Souliss is a small community and doesn't have sufficient human resources to be more active on openHAB official community. + +These are some very popular forum: + +English Group, [here](https://groups.google.com/forum/#!forum/souliss) + +Italian Group, [here](https://groups.google.com/forum/#!forum/souliss-it) + +Spanish Group, [here] (https://groups.google.com/forum/#!forum/souliss-es) + +## Contribution + +Official repository for contributing to the Souliss project, GitHub page: [here](https://github.com/souliss) + +## Known issues + +Securesend is, at moment, enabled and tested only for t11... diff --git a/bundles/org.openhab.binding.souliss/pom.xml b/bundles/org.openhab.binding.souliss/pom.xml new file mode 100644 index 000000000..621e9aac0 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 3.2.0-SNAPSHOT + + + org.openhab.binding.souliss + + openHAB Add-ons :: Bundles :: Souliss Binding + + diff --git a/bundles/org.openhab.binding.souliss/src/main/feature/feature.xml b/bundles/org.openhab.binding.souliss/src/main/feature/feature.xml new file mode 100644 index 000000000..b064c6661 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/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.souliss/${project.version} + + diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissBindingConstants.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissBindingConstants.java new file mode 100644 index 000000000..a9b37f40a --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissBindingConstants.java @@ -0,0 +1,210 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; + +/** + * The {@link SoulissBinding} class defines common constants, which are + * used across the whole binding. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public final class SoulissBindingConstants { + + public static final String BINDING_ID = "souliss"; + + public static final long DISCOVERY_RESEND_TIMEOUT_IN_SECONDS = 45; + public static final int DISCOVERY_TIMEOUT_IN_SECONDS = 120; + public static final long SEND_DISPATCHER_MIN_DELAY_CYCLE_IN_MILLIS = 500; + + // List of all Thing Type UIDs + public static final ThingTypeUID GATEWAY_THING_TYPE = new ThingTypeUID(BINDING_ID, "gateway"); + + public static final String T11 = "t11"; + public static final String T12 = "t12"; + public static final String T13 = "t13"; + public static final String T14 = "t14"; + public static final String T16 = "t16"; + public static final String T18 = "t18"; + public static final String T19 = "t19"; + public static final String T1A = "t1a"; + public static final String T21 = "t21"; + public static final String T22 = "t22"; + public static final String T31 = "t31"; + public static final String T41 = "t41"; + public static final String T42 = "t42"; + public static final String T51 = "t51"; + public static final String T52 = "t52"; + public static final String T53 = "t53"; + public static final String T54 = "t54"; + public static final String T55 = "t55"; + public static final String T56 = "t56"; + public static final String T57 = "t57"; + public static final String T58 = "t58"; + public static final String T61 = "t61"; + public static final String T62 = "t62"; + public static final String T63 = "t63"; + public static final String T64 = "t64"; + public static final String T65 = "t65"; + public static final String T66 = "t66"; + public static final String T67 = "t67"; + public static final String T68 = "t68"; + public static final String TOPICS = "topic"; + + public static final ThingTypeUID T11_THING_TYPE = new ThingTypeUID(BINDING_ID, T11); + public static final ThingTypeUID T12_THING_TYPE = new ThingTypeUID(BINDING_ID, T12); + public static final ThingTypeUID T13_THING_TYPE = new ThingTypeUID(BINDING_ID, T13); + public static final ThingTypeUID T14_THING_TYPE = new ThingTypeUID(BINDING_ID, T14); + public static final ThingTypeUID T16_THING_TYPE = new ThingTypeUID(BINDING_ID, T16); + public static final ThingTypeUID T18_THING_TYPE = new ThingTypeUID(BINDING_ID, T18); + public static final ThingTypeUID T19_THING_TYPE = new ThingTypeUID(BINDING_ID, T19); + public static final ThingTypeUID T1A_THING_TYPE = new ThingTypeUID(BINDING_ID, T1A); + public static final ThingTypeUID T21_THING_TYPE = new ThingTypeUID(BINDING_ID, T21); + public static final ThingTypeUID T22_THING_TYPE = new ThingTypeUID(BINDING_ID, T22); + public static final ThingTypeUID T31_THING_TYPE = new ThingTypeUID(BINDING_ID, T31); + public static final ThingTypeUID T41_THING_TYPE = new ThingTypeUID(BINDING_ID, T41); + public static final ThingTypeUID T42_THING_TYPE = new ThingTypeUID(BINDING_ID, T42); + public static final ThingTypeUID T51_THING_TYPE = new ThingTypeUID(BINDING_ID, T51); + public static final ThingTypeUID T52_THING_TYPE = new ThingTypeUID(BINDING_ID, T52); + public static final ThingTypeUID T53_THING_TYPE = new ThingTypeUID(BINDING_ID, T53); + public static final ThingTypeUID T54_THING_TYPE = new ThingTypeUID(BINDING_ID, T54); + public static final ThingTypeUID T55_THING_TYPE = new ThingTypeUID(BINDING_ID, T55); + public static final ThingTypeUID T56_THING_TYPE = new ThingTypeUID(BINDING_ID, T56); + public static final ThingTypeUID T57_THING_TYPE = new ThingTypeUID(BINDING_ID, T57); + public static final ThingTypeUID T58_THING_TYPE = new ThingTypeUID(BINDING_ID, T58); + public static final ThingTypeUID T61_THING_TYPE = new ThingTypeUID(BINDING_ID, T61); + public static final ThingTypeUID T62_THING_TYPE = new ThingTypeUID(BINDING_ID, T62); + public static final ThingTypeUID T63_THING_TYPE = new ThingTypeUID(BINDING_ID, T63); + public static final ThingTypeUID T64_THING_TYPE = new ThingTypeUID(BINDING_ID, T64); + public static final ThingTypeUID T65_THING_TYPE = new ThingTypeUID(BINDING_ID, T65); + public static final ThingTypeUID T66_THING_TYPE = new ThingTypeUID(BINDING_ID, T66); + public static final ThingTypeUID T67_THING_TYPE = new ThingTypeUID(BINDING_ID, T67); + public static final ThingTypeUID T68_THING_TYPE = new ThingTypeUID(BINDING_ID, T68); + public static final ThingTypeUID TOPICS_THING_TYPE = new ThingTypeUID(BINDING_ID, TOPICS); + + public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(GATEWAY_THING_TYPE, T11_THING_TYPE, + T12_THING_TYPE, T13_THING_TYPE, T14_THING_TYPE, T16_THING_TYPE, T18_THING_TYPE, T19_THING_TYPE, + T1A_THING_TYPE, T21_THING_TYPE, T22_THING_TYPE, T31_THING_TYPE, T41_THING_TYPE, T42_THING_TYPE, + T51_THING_TYPE, T52_THING_TYPE, T53_THING_TYPE, T54_THING_TYPE, T55_THING_TYPE, T56_THING_TYPE, + T57_THING_TYPE, T58_THING_TYPE, T61_THING_TYPE, T62_THING_TYPE, T63_THING_TYPE, T64_THING_TYPE, + T65_THING_TYPE, T66_THING_TYPE, T67_THING_TYPE, T68_THING_TYPE, TOPICS_THING_TYPE); + + // List of all Channel ids + public static final String ONOFF_CHANNEL = "onOff"; + + public static final String PULSE_CHANNEL = "pulse"; + public static final String SLEEP_CHANNEL = "sleep"; + public static final String AUTOMODE_CHANNEL = "autoMode"; + public static final String STATEONOFF_CHANNEL = "stateOnOff"; + public static final String STATEOPENCLOSE_CHANNEL = "stateOpenClose"; + public static final String ROLLERSHUTTER_CHANNEL = "rollerShutter"; + public static final String ROLLERSHUTTER_STATE_CHANNEL_CHANNEL = "rollerShutterState"; + + public static final String ROLLERSHUTTER_MESSAGE_OPENING_CHANNEL = "opening"; + public static final String ROLLERSHUTTER_MESSAGE_CLOSING_CHANNEL = "closing"; + public static final String ROLLERSHUTTER_MESSAGE_LIMITSWITCH_OPEN_CHANNEL = "limSwitchOpen"; + public static final String ROLLERSHUTTER_MESSAGE_LIMITSWITCH_CLOSE_CHANNEL = "limSwitchClose"; + public static final String ROLLERSHUTTER_MESSAGE_STATE_OPEN_CHANNEL = "stateOpen"; + public static final String ROLLERSHUTTER_MESSAGE_STATE_CLOSE_CHANNEL = "stateClose"; + public static final String ROLLERSHUTTER_MESSAGE_NO_LIMITSWITCH_CHANNEL = "NoLimSwitch"; + public static final String ROLLERSHUTTER_MESSAGE_STOP_CHANNEL = "stop"; + public static final String ROLLERSHUTTER_MESSAGE_TIMER_OFF = "timer off"; + + public static final String T1A_1_CHANNEL = "one"; + public static final String T1A_2_CHANNEL = "two"; + public static final String T1A_3_CHANNEL = "three"; + public static final String T1A_4_CHANNEL = "four"; + public static final String T1A_5_CHANNEL = "five"; + public static final String T1A_6_CHANNEL = "six"; + public static final String T1A_7_CHANNEL = "seven"; + public static final String T1A_8_CHANNEL = "eight"; + + public static final String T31_MODE_CHANNEL = "mode"; + public static final String T31_SYSTEM_CHANNEL = "system"; + public static final String T31_FIRE_CHANNEL = "fire"; + public static final String T31_FAN_CHANNEL = "fan"; + public static final String T31_BUTTON_CHANNEL = "setAsMeasured"; + public static final String T31_VALUE_CHANNEL = "measured"; + public static final String T31_SETPOINT_CHANNEL = "setPoint"; + + public static final String T31_COOLINGMODE_MESSAGE_MODE_CHANNEL = "COOLING_MODE"; + public static final String T31_HEATINGMODE_MESSAGE_MODE_CHANNEL = "HEATING_MODE"; + public static final String T31_OFF_MESSAGE_SYSTEM_CHANNEL = "SYSTEM_OFF"; + public static final String T31_ON_MESSAGE_SYSTEM_CHANNEL = "SYSTEM_ON"; + public static final String T31_ON_MESSAGE_FIRE_CHANNEL = "FIRE_ON"; + public static final String T31_OFF_MESSAGE_FIRE_CHANNEL = "FIRE_OFF"; + + public static final String T31_FANAUTO_MESSAGE_FAN_CHANNEL = "AUTO"; + public static final String T31_FANOFF_MESSAGE_FAN_CHANNEL = "FANOFF"; + public static final String T31_FANLOW_MESSAGE_FAN_CHANNEL = "LOW"; + public static final String T31_FANMEDIUM_MESSAGE_FAN_CHANNEL = "MEDIUM"; + public static final String T31_FANHIGH_MESSAGE_FAN_CHANNEL = "HIGH"; + + public static final String T4N_ONOFFALARM_CHANNEL = "onOffAlarm"; + public static final String T4N_STATUSALARM_CHANNEL = "statusAlarm"; + public static final String T4N_REARMALARM_CHANNEL = "rearmAlarm"; + public static final String T41_RESETALARM_CHANNEL = "resetAlarm"; + + public static final String T4N_ALARMON_MESSAGE_CHANNEL = "ALARMON"; + public static final String T4N_ALARMOFF_MESSAGE_CHANNEL = "ALARMOFF"; + public static final String T4N_REARMOFF_MESSAGE_CHANNEL = "REARMOFF"; + public static final String T4N_ARMED_MESSAGE_CHANNEL = "ARMED"; + + public static final String WHITE_MODE_CHANNEL = "whitemode"; + public static final String ROLLER_BRIGHTNESS_CHANNEL = "rollerBrightness"; + public static final String DIMMER_BRIGHTNESS_CHANNEL = "dimmerBrightness"; + public static final String LED_COLOR_CHANNEL = "ledcolor"; + + public static final String LASTMESSAGE_CHANNEL = "lastMessage"; + public static final String LASTSTATUSSTORED_CHANNEL = "lastStatusStored"; + public static final String HEALTHY_CHANNEL = "healthy"; + + public static final String T5N_VALUE_CHANNEL = "value"; + public static final String T6N_VALUE_CHANNEL = "value"; + public static final String FLOATING_POINT_CHANNEL = "float"; + public static final String HUMIDITY_CHANNEL = "humidity"; + public static final String TEMPERATURE_CHANNEL = "temperature"; + public static final String AMPERE_CHANNEL = "ampere"; + public static final String VOLTAGE_CHANNEL = "voltage"; + public static final String POWER_CHANNEL = "power"; + + public static final String CONFIG_ID = "ID"; + public static final String CONFIG_IP_ADDRESS = "gatewayLanAddress"; + + public static final String UUID_NODE_SLOT_SEPARATOR = "-"; + + public static final String UUID_ELEMENTS_SEPARATOR = ":"; + + public static final String CONFIG_SLEEP = "sleep"; + + public static final String CONFIG_SECURE_SEND = "secureSend"; + + public static final String CONFIG_TIMEOUT_TO_REQUEUE = "timeoutToRequeue"; + + public static final String CONFIG_TIMEOUT_TO_REMOVE_PACKET = "timeoutToRemovePacket"; + + // Properties + public static final String PROPERTY_NODE = "node"; + public static final String PROPERTY_SLOT = "slot"; + public static final String PROPERTY_UNIQUEID = "uniqueId"; + + // private constructor + private SoulissBindingConstants() { + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissDatagramSocketFactory.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissDatagramSocketFactory.java new file mode 100644 index 000000000..ee618c1f9 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissDatagramSocketFactory.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal; + +import java.net.DatagramSocket; +import java.net.SocketException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.slf4j.Logger; + +/** + * The {@link SoulissDatagramSocketFactory} is responsible for creating datagramSocket object for trasmission e + * receiving. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissDatagramSocketFactory { + + public static @Nullable DatagramSocket getSocketDatagram(Logger logger) { + return getSocketDatagram(0, logger); + } + + public static @Nullable DatagramSocket getSocketDatagram(int socketPortNumber, Logger logger) { + // return DatagramSocket for packet trasmission + DatagramSocket soulissDatagramSocket = null; + logger.debug("Setup socket"); + try { + if (socketPortNumber != 0) { + soulissDatagramSocket = new DatagramSocket(socketPortNumber); + } else { + soulissDatagramSocket = new DatagramSocket(); + } + logger.debug("Datagram Socket Created on port {}", soulissDatagramSocket.getLocalPort()); + } catch (SocketException e) { + logger.warn("Error on creation of Socket: {}", e.getMessage()); + } + + return soulissDatagramSocket; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissHandlerFactory.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissHandlerFactory.java new file mode 100644 index 000000000..df1ad5e8c --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissHandlerFactory.java @@ -0,0 +1,138 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal; + +import static org.openhab.binding.souliss.internal.SoulissBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.handler.SoulissGatewayHandler; +import org.openhab.binding.souliss.internal.handler.SoulissT11Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT12Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT13Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT14Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT16Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT18Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT19Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT1AHandler; +import org.openhab.binding.souliss.internal.handler.SoulissT22Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT31Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT41Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT42Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT51Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT52Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT53Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT54Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT55Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT56Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT57Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT61Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT62Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT63Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT64Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT65Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT66Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT67Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT68Handler; +import org.openhab.binding.souliss.internal.handler.SoulissTopicsHandler; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link SoulissHandlerFactory} is responsible for creating things and thingGeneric + * handlers. It fire when a new thingGeneric is added. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +@Component(configurationPid = "binding.souliss", service = ThingHandlerFactory.class) +public class SoulissHandlerFactory extends BaseThingHandlerFactory { + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + var thingTypeUID = thing.getThingTypeUID(); + + if (thingTypeUID.equals(GATEWAY_THING_TYPE)) { + return new SoulissGatewayHandler((Bridge) thing); + } else if (thingTypeUID.equals(T11_THING_TYPE)) { + return new SoulissT11Handler(thing); + } else if (thingTypeUID.equals(T12_THING_TYPE)) { + return new SoulissT12Handler(thing); + } else if (thingTypeUID.equals(T13_THING_TYPE)) { + return new SoulissT13Handler(thing); + } else if (thingTypeUID.equals(T14_THING_TYPE)) { + return new SoulissT14Handler(thing); + } else if (thingTypeUID.equals(T16_THING_TYPE)) { + return new SoulissT16Handler(thing); + } else if (thingTypeUID.equals(T18_THING_TYPE)) { + return new SoulissT18Handler(thing); + } else if (thingTypeUID.equals(T19_THING_TYPE)) { + return new SoulissT19Handler(thing); + } else if (thingTypeUID.equals(T1A_THING_TYPE)) { + return new SoulissT1AHandler(thing); + } else if (thingTypeUID.equals(T21_THING_TYPE) || (thingTypeUID.equals(T22_THING_TYPE))) { + return new SoulissT22Handler(thing); + } else if (thingTypeUID.equals(T31_THING_TYPE)) { + return new SoulissT31Handler(thing); + } else if (thingTypeUID.equals(T41_THING_TYPE)) { + return new SoulissT41Handler(thing); + } else if (thingTypeUID.equals(T42_THING_TYPE)) { + return new SoulissT42Handler(thing); + } else if (thingTypeUID.equals(T51_THING_TYPE)) { + return new SoulissT51Handler(thing); + } else if (thingTypeUID.equals(T52_THING_TYPE)) { + return new SoulissT52Handler(thing); + } else if (thingTypeUID.equals(T53_THING_TYPE)) { + return new SoulissT53Handler(thing); + } else if (thingTypeUID.equals(T54_THING_TYPE)) { + return new SoulissT54Handler(thing); + } else if (thingTypeUID.equals(T55_THING_TYPE)) { + return new SoulissT55Handler(thing); + } else if (thingTypeUID.equals(T56_THING_TYPE)) { + return new SoulissT56Handler(thing); + } else if (thingTypeUID.equals(T57_THING_TYPE)) { + return new SoulissT57Handler(thing); + } else if (thingTypeUID.equals(T61_THING_TYPE)) { + return new SoulissT61Handler(thing); + } else if (thingTypeUID.equals(T62_THING_TYPE)) { + return new SoulissT62Handler(thing); + } else if (thingTypeUID.equals(T63_THING_TYPE)) { + return new SoulissT63Handler(thing); + } else if (thingTypeUID.equals(T64_THING_TYPE)) { + return new SoulissT64Handler(thing); + } else if (thingTypeUID.equals(T65_THING_TYPE)) { + return new SoulissT65Handler(thing); + } else if (thingTypeUID.equals(T66_THING_TYPE)) { + return new SoulissT66Handler(thing); + } else if (thingTypeUID.equals(T67_THING_TYPE)) { + return new SoulissT67Handler(thing); + } else if (thingTypeUID.equals(T68_THING_TYPE)) { + return new SoulissT68Handler(thing); + } else if (thingTypeUID.equals(TOPICS_THING_TYPE)) { + return new SoulissTopicsHandler(thing); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissProtocolConstants.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissProtocolConstants.java new file mode 100644 index 000000000..4e05968f9 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissProtocolConstants.java @@ -0,0 +1,309 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * the class {@link SoulissProtocolConstants} class contains Souliss constants. Original version is taken from + * SoulissApp. For scope of this binding not all constants are used. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + * @author Alessandro Del Pex - soulissapp + * @author Tonino Fazio - @since 1.7.0 + * @author Luca Remigio - @since 2.0.0 + * + */ + +@NonNullByDefault +public final class SoulissProtocolConstants { + + public static final String TAG = "SoulissApp:Typicals"; + + /** + * /** // Defines for Typicals C LIBRARY + * + * #define SOULISS_T31 31 // Temperature control #define Souliss_T41 41 // + * Anti-theft integration (Main) #define Souliss_T42 42 // Anti-theft + * integration (Peer) + */ + public static final byte SOULISS_T_EMPTY = 0; + public static final byte SOULISS_T_RELATED = (byte) 0xFF; + + public static final byte SOULISS_TSERVICE_NODE_HEALTHY = (byte) 0x98; + public static final byte SOULISS_TSERVICE_NODE_TIMESTAMP = (byte) 0x99; + + public static final int SOULISS_TSERVICE_NODE_HEALTHY_VIRTUAL_SLOT = 998; + public static final int SOULISS_TSERVICE_NODE_TIMESTAMP_VIRTUAL_SLOT = 999; + + // Defines for Typicals + public static final byte SOULISS_T11 = 0x11; + public static final byte SOULISS_T12 = 0x12; + public static final byte SOULISS_T13 = 0x13; + public static final byte SOULISS_T14 = 0x14; + // RGB Light + public static final byte SOULISS_T1N_RGB = 0x15; + public static final byte SOULISS_T16 = 0x16; + public static final byte SOULISS_T18 = 0x18; + public static final byte SOULISS_T19 = 0x19; + public static final byte SOULISS_T1A = 0x1A; + + // Motorized devices with limit switches + public static final byte SOULISS_T21 = 0x21; + // Motorized devices with limit switches and middle position + public static final byte SOULISS_T22 = 0x22; + public static final byte SOULISS_T31 = 0x31; + public static final byte SOULISS_T32_IRCOM_AIRCON = 0x32; + + // Anti-theft group (used with massive commands) + public static final byte SOULISS_T42_ANTITHEFT_GROUP = 0x40; + // Anti-theft integration (Main) + public static final byte SOULISS_T41_ANTITHEFT_MAIN = 0x41; + // Anti-theft integration (Peer) + public static final byte SOULISS_T42_ANTITHEFT_PEER = 0x42; + + public static final byte SOULISS_T51 = 0x51; + public static final byte SOULISS_T52_TEMPERATURE_SENSOR = 0x52; + public static final byte SOULISS_T53_HUMIDITY_SENSOR = 0x53; + public static final byte SOULISS_T54_LUX_SENSOR = 0x54; + public static final byte SOULISS_T55_VOLTAGE_SENSOR = 0x55; + public static final byte SOULISS_T56_CURRENT_SENSOR = 0x56; + public static final byte SOULISS_T57_POWER_SENSOR = 0x57; + public static final byte SOULISS_T58_PRESSURE_SENSOR = 0x58; + + public static final byte SOULISS_T61 = 0x61; + public static final byte SOULISS_T62_TEMPERATURE_SENSOR = 0x62; + public static final byte SOULISS_T63_HUMIDITY_SENSOR = 0x63; + public static final byte SOULISS_T64_LUX_SENSOR = 0x64; + public static final byte SOULISS_T65_VOLTAGE_SENSOR = 0x65; + public static final byte SOULISS_T66_CURRENT_SENSOR = 0x66; + public static final byte SOULISS_T67_POWER_SENSOR = 0x67; + public static final byte SOULISS_T68_PRESSURE_SENSOR = 0x68; + + public static final byte SOULISS_TOPICS = 0x72; + + // customized (remote) AirCon commands + public static final int SOULISS_T_IRCOM_AIRCON_POW_ON = 0x8FFE; + public static final int SOULISS_T_IRCOM_AIRCON_POW_AUTO_20 = 0x8FFD; + public static final int SOULISS_T_IRCOM_AIRCON_POW_AUTO_24 = 0x8FFE; + public static final int SOULISS_T_IRCOM_AIRCON_POW_COOL_18 = 0x807B; + public static final int SOULISS_T_IRCOM_AIRCON_POW_COOL_22 = 0x8079; + public static final int SOULISS_T_IRCOM_AIRCON_POW_COOL_26 = 0x807A; + public static final int SOULISS_T_IRCOM_AIRCON_POW_FAN = 0x8733; + public static final int SOULISS_T_IRCOM_AIRCON_POW_DRY = 0x87BE; + public static final int SOULISS_T_IRCOM_AIRCON_POW_OFF = 0x70FE; + + // Souliss Aircon Temperature + + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_16C = 0xF; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_17C = 0x7; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_18C = 0xB; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_19C = 0x3; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_20C = 0xD; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_21C = 0x5; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_22C = 0x9; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_23C = 0x1; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_24C = 0xE; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_25C = 0x6; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_26C = 0xA; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_27C = 0x2; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_28C = 0xC; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_29C = 0x4; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_30C = 0x8; + + // Souliss conditioner Function + + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_FUN_AAUTO = 0xF; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_FUN_DRY = 0xB; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_FUN_FAN = 0x3; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_FUN_HEAT = 0xD; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_FUN_COOL = 0x7; + + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_FAN_AUTO = 0x7; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_FAN_HIGH = 0x2; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_FAN_MEDIUM = 0x6; + public static final byte SOULISS_T_IRCOM_AIRCON_TEMP_FAN_LOW = 0x5; + + // optional switches. May be used to toggle + // custom aircon functions as air deflector, ionizer, turbomode, etc. + public static final byte SOULISS_T_IRCOM_AIRCON_OPT1 = 0x2D; + public static final byte SOULISS_T_IRCOM_AIRCON_OPT2 = 0x77; + + public static final byte SOULISS_T_IRCOM_AIRCON_RESET = 0x00; + + // General defines for T1n + public static final byte SOULISS_T1N_TOGGLE_CMD = 0x01; + public static final byte SOULISS_T1N_ON_CMD = 0x02; + public static final byte SOULISS_T1N_OFF_CMD = 0x04; + public static final byte SOULISS_T1N_AUTO_CMD = 0x08; + public static final byte SOULISS_T1N_TIMED = 0x30; + public static final byte SOULISS_T1N_RST_CMD = 0x00; + public static final byte SOULISS_T1N_ON_COIL = 0x01; + public static final byte SOULISS_T1N_OFF_COIL = 0x00; + public static final byte SOULISS_T1N_ON_COIL_AUTO = (byte) 0xF1; + public static final byte SOULISS_T1N_OFF_COIL_AUTO = (byte) 0xF0; + // Set a state + public static final byte SOULISS_T1N_SET = 0x22; + + // Increase Light + public static final byte SOULISS_T1N_BRIGHT_UP = 0x10; + // Decrease Light + public static final byte SOULISS_T1N_BRIGHT_DOWN = 0x20; + // Flash Light + public static final byte SOULISS_T1N_FLASH = 0x21; + + public static final byte SOULISS_T1N_ON_FEEDBACK = 0x23; + public static final byte SOULISS_T1N_OFF_FEEDBACK = 0x24; + public static final String SOULISS_T12_USE_OF_SLOT_AUTO_MODE = "autoMode"; + public static final String SOULISS_T12_USE_OF_SLOT_SWITCH = "switch"; + + // Set a state + public static final long SOULISS_T16_RED = 0x22FF0000; + public static final long SOULISS_T16_GREEN = 0x2200FF00; + public static final long SOULISS_T16_BLUE = 0x220000FF; + public static final long SOULISS_T18_PULSE = 0xA1; + /* + * IR RGB Typical + */ + public static final byte SOULISS_T1N_RGB_ON_CMD = 0x1; + public static final byte SOULISS_T1N_RGB_OFF_CMD = 0x9; + + // Souliss RGB main colours + public static final byte SOULISS_T1N_RGB_R = 0x2; + public static final byte SOULISS_T1N_RGB_G = 0x3; + public static final byte SOULISS_T1N_RGB_B = 0x4; + public static final byte SOULISS_T1N_RGB_W = 0x5; + // Souliss RGB Controls + public static final byte SOULISS_T_IRCOM_RGB_BRIGHT_UP = 0x6; + public static final byte SOULISS_T_IRCOM_RGB_BRIGHT_DOWN = 0x7; + // MODES + public static final byte SOULISS_T_IRCOM_RGB_MODE_FLASH = (byte) 0xA1; + public static final byte SOULISS_T_IRCOM_RGB_MODE_STROBE = (byte) 0xA2; + public static final byte SOULISS_T_IRCOM_RGB_MODE_FADE = (byte) 0xA3; + public static final byte SOULISS_T_IRCOM_RGB_MODE_SMOOTH = (byte) 0xA4; + + public static final byte SOULISS_T1N_RGB_R2 = (byte) 0xB1; + public static final byte SOULISS_T1N_RGB_R3 = (byte) 0xB2; + public static final byte SOULISS_T1N_RGB_R4 = (byte) 0xB3; + public static final byte SOULISS_T1N_RGB_R5 = (byte) 0xB4; + public static final byte SOULISS_T1N_RGB_G2 = (byte) 0xC1; + public static final byte SOULISS_T1N_RGB_G3 = (byte) 0xC2; + public static final byte SOULISS_T1N_RGB_G4 = (byte) 0xC3; + public static final byte SOULISS_T1N_RGB_G5 = (byte) 0xC4; + public static final byte SOULISS_T1N_RGB_B2 = (byte) 0xD1; + public static final byte SOULISS_T1N_RGB_B3 = (byte) 0xD2; + public static final byte SOULISS_T1N_RGB_B4 = (byte) 0xD3; + public static final byte SOULISS_T1N_RGB_B5 = (byte) 0xD4; + + public static final byte SOULISS_T1N_RGB_RST_CMD = 0x00; + + // Defines for Typical 2n + public static final byte SOULISS_T2N_CLOSE_CMD = 0x01; + public static final byte SOULISS_T2N_OPEN_CMD = 0x02; + public static final byte SOULISS_T2N_STOP_CMD = 0x04; + // Close Command (only from local pushbutton) + public static final byte SOULISS_T2N_CLOSE_CMD_LOCAL = 0x08; + // Open Command (only from local pushbutton) + public static final byte SOULISS_T2N_OPEN_CMD_LOCAL = 0x10; + public static final byte SOULISS_T2N_TOGGLE_CMD = 0x08; + public static final byte SOULISS_T2N_RST_CMD = 0x00; + // Timer set value + public static final byte SOULISS_T2N_TIMER_VAL = (byte) 0xC0; + // Timer expired value + public static final byte SOULISS_T2N_TIMER_OFF = (byte) 0xA0; + // Timed stop value + public static final byte SOULISS_T2N_TIMEDSTOP_VAL = (byte) 0xC2; + // Timed stop exipred value + public static final byte SOULISS_T2N_TIMEDSTOP_OFF = (byte) 0xC0; + public static final byte SOULISS_T2N_LIMSWITCH_CLOSE = 0x14; + public static final byte SOULISS_T2N_LIMSWITCH_OPEN = 0x16; + // Close Feedback from Limit Switch + public static final byte SOULISS_T2N_STATE_CLOSE = 0x08; + // Open Feedback from Limit Switch + public static final byte SOULISS_T2N_STATE_OPEN = 0x10; + + public static final byte SOULISS_T2N_NOLIMSWITCH = 0x20; + public static final byte SOULISS_T2N_COIL_CLOSE = 0x01; + public static final byte SOULISS_T2N_COIL_OPEN = 0x02; + public static final byte SOULISS_T2N_COIL_STOP = 0x03; + public static final byte SOULISS_T2N_COIL_OFF = 0x00; + + // General defines for T3n + public static final String SOULISS_T31_USE_OF_SLOT_SETPOINT = "setPoint"; + public static final String SOULISS_T31_USE_OF_SLOT_MEASURED = "measured"; + public static final String SOULISS_T31_USE_OF_SLOT_SETASMEASURED = "setAsMeasured"; + + public static final byte SOULISS_T31_USE_OF_SLOT_SETPOINT_COMMAND = 0x0C; + public static final byte SOULISS_T31_USE_OF_SLOT_HEATING = 0x05; + public static final byte SOULISS_T31_USE_OF_SLOT_COOLING = 0x04; + public static final String SOULISS_T31_USE_OF_SLOT_HEATING_COOLING = "heatingCooling"; + public static final byte SOULISS_T31_USE_OF_SLOT_FAN_OFF = 0x06; + public static final byte SOULISS_T31_USE_OF_SLOT_FAN_LOW = 0x07; + public static final byte SOULISS_T31_USE_OF_SLOT_FAN_MED = 0x08; + public static final byte SOULISS_T31_USE_OF_SLOT_FAN_HIGH = 0x09; + public static final byte SOULISS_T31_USE_OF_SLOT_FAN_AUTOMODE = 0x0A; + public static final String SOULISS_T31_USE_OF_SLOT_POWER = "power"; + + public static final byte SOULISS_T3N_IN_SETPOINT = 0x01; + public static final byte SOULISS_T3N_OUT_SETPOINT = 0x02; + public static final byte SOULISS_T3N_AS_MEASURED = 0x03; + public static final byte SOULISS_T3N_COOLING = 0x04; + public static final byte SOULISS_T3N_HEATING = 0x05; + public static final byte SOULISS_T3N_FAN_OFF = 0x06; + public static final byte SOULISS_T3N_FAN_LOW = 0x07; + public static final byte SOULISS_T3N_FAN_MED = 0x08; + public static final byte SOULISS_T3N_FAN_HIGH = 0x09; + public static final byte SOULISS_T3N_FAN_AUTO = 0x0A; + public static final byte SOULISS_T3N_FAN_MANUAL = 0x0B; + public static final byte SOULISS_T3N_SET_TEMP = 0x0C; + public static final byte SOULISS_T3N_SHUTDOWN = 0x0D; + + public static final String SOULISS_T3N_HEATING_ON = "0x02"; + public static final String SOULISS_T3N_COOLING_ON = "0x03"; + public static final String SOULISS_T3N_FAN_ON_1 = "0x08"; + public static final String SOULISS_T3N_FAN_ON_2 = "0x10"; + public static final String SOULISS_T3N_FAN_ON_3 = "0x20"; + + // General defines for T4n + + // Alarm Condition Detected (Input) + public static final byte SOULISS_T4N_ALARM = 0x01; + public static final byte SOULISS_T4N_RST_CMD = 0x00; + // Silence and Arm Command + public static final byte SOULISS_T4N_REARM = 0x03; + // Anti-theft not Armed Command + public static final byte SOULISS_T4N_NOT_ARMED = 0x04; + // Anti-theft Armed Command + public static final byte SOULISS_T4N_ARMED = 0x05; + // Anti-theft Armed Feedback + public static final byte SOULISS_T4N_ANTITHEFT = 0x01; + // Anti-theft not Armed Feedback + public static final byte SOULISS_T4N_NO_ANTITHEFT = 0x00; + // Anti-theft in Alarm + public static final byte SOULISS_T4N_IN_ALARM = 0x03; + + public static final byte SOULISS_RST_CMD = 0x00; + public static final byte SOULISS_NOT_TRIGGED = 0x00; + public static final byte SOULISS_TRIGGED = 0x01; + + // Defines for current sensor + public static final byte SOULISS_T_CURRENT_SENSOR = 0x65; + + // REMOVE THESE + public static final byte SOULISS_T_TEMPERATURE_SENSOR = 0x67; + public static final byte SOULISS_T_TEMPERATURE_SENSOR_REFRESH = 0x02; + + public static final byte SOULISS_T_HUMIDITY_SENSOR = 0x69; + public static final byte SOULISS_T_HUMIDITY_SENSOR_REFRESH = 0x03; +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissUDPConstants.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissUDPConstants.java new file mode 100644 index 000000000..9d742801f --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissUDPConstants.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Network constants. The class {@link SoulissUDPConstants} contains Souliss constants. Original version is + * taken from SoulissApp. For scope of this binding not all constants are used. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + * @author Alessandro Del Pex - @since 1.7.0 + */ +@NonNullByDefault +public class SoulissUDPConstants { + + public static final String TAG = "SoulissApp"; + + public static final int SOULISS_BINDING_LOCAL_PORT = 0; + public static final int SOULISS_GATEWAY_DEFAULT_PORT = 230; + + public static final Integer SOULISS_DEFAULT_NODE_INDEX = 70; + public static final Integer SOULISS_DEFAULT_USER_INDEX = 120; + + public static final String BROADCASTADDR = "255.255.255.255"; + + public static final byte SOULISS_UDP_FUNCTION_FORCE = 0x33; + public static final byte SOULISS_UDP_FUNCTION_FORCE_MASSIVE = 0x34; + + public static final byte SOULISS_UDP_FUNCTION_SUBSCRIBE_REQ = 0x21; + public static final byte SOULISS_UDP_FUNCTION_SUBSCRIBE_RESP = 0x31; + public static final byte SOULISS_UDP_FUNCTION_POLL_REQ = 0x27; + public static final byte SOULISS_UDP_FUNCTION_POLL_RESP = 0x37; + public static final byte SOULISS_UDP_FUNCTION_TYP_REQ = 0x22; + public static final byte SOULISS_UDP_FUNCTION_TYP_RESP = 0x32; + public static final byte SOULISS_UDP_FUNCTION_HEALTHY_REQ = 0x25; + public static final byte SOULISS_UDP_FUNCTION_HEALTHY_RESP = 0x35; + + public static final byte SOULISS_UDP_FUNCTION_PING_REQ = 0x8; + public static final byte SOULISS_UDP_FUNCTION_PING_RESP = 0x18; + + public static final byte SOULISS_UDP_FUNCTION_DISCOVER_GW_NODE_BCAST_REQ = 0x28; + public static final byte SOULISS_UDP_FUNCTION_DISCOVER_GW_NODE_BCAST_RESP = 0x38; + + public static final int SOULISS_UDP_FUNCTION_DBSTRUCT_REQ = 0x26; + public static final int SOULISS_UDP_FUNCTION_DBSTRUCT_RESP = 0x36; + + public static final int SOULISS_UDP_FUNCTION_ACTION_MESSAGE = 0x72; + + protected static final Byte[] PING_PAYLOAD = { SOULISS_UDP_FUNCTION_PING_REQ, 0, 0, 0, 0 }; + protected static final Byte[] PING_DISCOVER_BCAST_PAYLOAD = { SOULISS_UDP_FUNCTION_DISCOVER_GW_NODE_BCAST_REQ, 0, 0, + 0, 0 }; + protected static final Byte[] DBSTRUCT_PAYLOAD = { SOULISS_UDP_FUNCTION_DBSTRUCT_REQ, 0, 0, 0, 0 }; + + // private constructor + private SoulissUDPConstants() { + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/config/GatewayConfig.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/config/GatewayConfig.java new file mode 100644 index 000000000..1a900f6df --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/config/GatewayConfig.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link GatewayConfig} is responsible for holding souliss gateway config + * + * @author Luca Calcaterra - Initial Contribution + */ +@NonNullByDefault +public final class GatewayConfig { + public int pingInterval; + public int subscriptionInterval; + public int healthyInterval; + public int sendInterval; + public int timeoutToRequeue; + public int timeoutToRemovePacket; + public int preferredLocalPortNumber; + public int gatewayPortNumber; + public int userIndex; + public int nodeIndex; + public String gatewayLanAddress = ""; + public String gatewayWanAddress = ""; +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/discovery/DiscoverResult.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/discovery/DiscoverResult.java new file mode 100644 index 000000000..2bcaca813 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/discovery/DiscoverResult.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.discovery; + +import java.net.InetAddress; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Result callback interface. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public interface DiscoverResult { + static boolean IS_GATEWAY_DETECTED = false; + + void gatewayDetected(InetAddress addr, String id); + + void thingDetectedTypicals(byte lastByteGatewayIP, byte typical, byte node, byte slot); + + void thingDetectedActionMessages(String sTopicNumber, String sTopicVariant); +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/discovery/SoulissDiscoverJob.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/discovery/SoulissDiscoverJob.java new file mode 100644 index 000000000..abace17e0 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/discovery/SoulissDiscoverJob.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.discovery; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.handler.SoulissGatewayHandler; +import org.openhab.binding.souliss.internal.protocol.CommonCommands; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissDiscoverJob implements Runnable { + + private final Logger logger = LoggerFactory.getLogger(SoulissDiscoverJob.class); + + private final CommonCommands commonCommands = new CommonCommands(); + + private int resendCounter = 0; + + private @Nullable SoulissGatewayHandler gwHandler; + + public SoulissDiscoverJob(@Nullable SoulissGatewayHandler soulissGwHandler) { + this.gwHandler = soulissGwHandler; + } + + @Override + public void run() { + var localGwHandler = this.gwHandler; + if (localGwHandler != null) { + commonCommands.sendDBStructFrame(localGwHandler.getGwConfig()); + logger.debug("Sending request to gateway for souliss network - Counter={}", resendCounter); + } else { + logger.debug("Gateway null - Skipped"); + } + resendCounter++; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/discovery/SoulissGatewayDiscovery.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/discovery/SoulissGatewayDiscovery.java new file mode 100644 index 000000000..29c63acb8 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/discovery/SoulissGatewayDiscovery.java @@ -0,0 +1,277 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.discovery; + +import java.net.InetAddress; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissProtocolConstants; +import org.openhab.binding.souliss.internal.handler.SoulissGatewayHandler; +import org.openhab.core.config.discovery.AbstractDiscoveryService; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.config.discovery.DiscoveryService; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link soulissHandlerFactory} is responsible for creating things and thingGeneric + * handlers. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissGatewayDiscovery extends AbstractDiscoveryService + implements DiscoverResult, DiscoveryService, ThingHandlerService { + private @Nullable ScheduledFuture discoveryJob = null; + private final Logger logger = LoggerFactory.getLogger(SoulissGatewayDiscovery.class); + + private @Nullable SoulissDiscoverJob soulissDiscoverRunnableClass = null; + + private @Nullable SoulissGatewayHandler soulissGwHandler; + + public SoulissGatewayDiscovery() { + super(SoulissBindingConstants.SUPPORTED_THING_TYPES_UIDS, SoulissBindingConstants.DISCOVERY_TIMEOUT_IN_SECONDS, + false); + } + + @Override + public void deactivate() { + super.deactivate(); + } + + /** + * The {@link gatewayDetected} callback used to create the Gateway + */ + @Override + public void gatewayDetected(InetAddress addr, String id) { + logger.debug("Souliss gateway found: {} ", addr.getHostName()); + + String label = "Souliss Gateway " + (Byte.parseByte(id) & 0xFF); + Map properties = new TreeMap<>(); + properties.put(SoulissBindingConstants.CONFIG_IP_ADDRESS, addr.getHostAddress()); + var gatewayUID = new ThingUID(SoulissBindingConstants.GATEWAY_THING_TYPE, + Integer.toString((Byte.parseByte(id) & 0xFF))); + var discoveryResult = DiscoveryResultBuilder.create(gatewayUID).withLabel(label) + .withRepresentationProperty(SoulissBindingConstants.CONFIG_IP_ADDRESS).withProperties(properties) + .build(); + thingDiscovered(discoveryResult); + } + + @Override + protected void startScan() { + logger.debug("Starting Scan Service"); + + // create discovery class + if (soulissDiscoverRunnableClass == null) { + soulissDiscoverRunnableClass = new SoulissDiscoverJob(this.soulissGwHandler); + + // send command for gw struct (typicals).. must be not soo much quick.. + discoveryJob = scheduler.scheduleWithFixedDelay(soulissDiscoverRunnableClass, 2, + SoulissBindingConstants.DISCOVERY_RESEND_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS); + logger.debug("Start Discovery Job"); + } + } + + @Override + protected synchronized void stopScan() { + ScheduledFuture localDiscoveryJob = this.discoveryJob; + if (localDiscoveryJob != null) { + localDiscoveryJob.cancel(false); + soulissDiscoverRunnableClass = null; + logger.debug("Discovery Job Stopped"); + } + super.stopScan(); + } + + @Override + public void thingDetectedActionMessages(String topicNumber, String sTopicVariant) { + ThingUID thingUID = null; + var label = ""; + DiscoveryResult discoveryResult; + String sNodeID = topicNumber + SoulissBindingConstants.UUID_NODE_SLOT_SEPARATOR + sTopicVariant; + + var localGwHandler = this.soulissGwHandler; + if (localGwHandler != null) { + var gatewayUID = localGwHandler.getThing().getUID(); + thingUID = new ThingUID(SoulissBindingConstants.TOPICS_THING_TYPE, gatewayUID, sNodeID); + label = "Topic. Number: " + topicNumber + ", Variant: " + sTopicVariant; + + discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(label) + .withProperty("number", topicNumber).withProperty("variant", sTopicVariant) + .withRepresentationProperty("number").withBridge(gatewayUID).build(); + thingDiscovered(discoveryResult); + } + } + + @Override + public void thingDetectedTypicals(byte lastByteGatewayIP, byte typical, byte node, byte slot) { + ThingUID thingUID = null; + var label = ""; + DiscoveryResult discoveryResult; + var gwHandler = this.soulissGwHandler; + if ((gwHandler != null) && (lastByteGatewayIP == (byte) Integer + .parseInt(gwHandler.getGwConfig().gatewayLanAddress.split("\\.")[3]))) { + String sNodeId = node + SoulissBindingConstants.UUID_NODE_SLOT_SEPARATOR + slot; + + ThingUID gatewayUID = gwHandler.getThing().getUID(); + var slotLabel = "slot"; + + switch (typical) { + case SoulissProtocolConstants.SOULISS_T11: + thingUID = new ThingUID(SoulissBindingConstants.T11_THING_TYPE, gatewayUID, sNodeId); + label = "T11: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T12: + thingUID = new ThingUID(SoulissBindingConstants.T12_THING_TYPE, gatewayUID, sNodeId); + label = "T12: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T13: + thingUID = new ThingUID(SoulissBindingConstants.T13_THING_TYPE, gatewayUID, sNodeId); + label = "T13: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T14: + thingUID = new ThingUID(SoulissBindingConstants.T14_THING_TYPE, gatewayUID, sNodeId); + label = "T14: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T16: + thingUID = new ThingUID(SoulissBindingConstants.T16_THING_TYPE, gatewayUID, sNodeId); + label = "T16: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T18: + thingUID = new ThingUID(SoulissBindingConstants.T18_THING_TYPE, gatewayUID, sNodeId); + label = "T18: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T19: + thingUID = new ThingUID(SoulissBindingConstants.T19_THING_TYPE, gatewayUID, sNodeId); + label = "T19: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T1A: + thingUID = new ThingUID(SoulissBindingConstants.T1A_THING_TYPE, gatewayUID, sNodeId); + label = "T1A: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T21: + thingUID = new ThingUID(SoulissBindingConstants.T21_THING_TYPE, gatewayUID, sNodeId); + label = "T21: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T22: + thingUID = new ThingUID(SoulissBindingConstants.T22_THING_TYPE, gatewayUID, sNodeId); + label = "T22: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T41_ANTITHEFT_MAIN: + thingUID = new ThingUID(SoulissBindingConstants.T41_THING_TYPE, gatewayUID, sNodeId); + label = "T41: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T42_ANTITHEFT_PEER: + thingUID = new ThingUID(SoulissBindingConstants.T42_THING_TYPE, gatewayUID, sNodeId); + label = "T42: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T31: + thingUID = new ThingUID(SoulissBindingConstants.T31_THING_TYPE, gatewayUID, sNodeId); + label = "T31: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T52_TEMPERATURE_SENSOR: + thingUID = new ThingUID(SoulissBindingConstants.T52_THING_TYPE, gatewayUID, sNodeId); + label = "T52: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T53_HUMIDITY_SENSOR: + thingUID = new ThingUID(SoulissBindingConstants.T53_THING_TYPE, gatewayUID, sNodeId); + label = "T53: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T54_LUX_SENSOR: + thingUID = new ThingUID(SoulissBindingConstants.T54_THING_TYPE, gatewayUID, sNodeId); + label = "T54: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T55_VOLTAGE_SENSOR: + thingUID = new ThingUID(SoulissBindingConstants.T55_THING_TYPE, gatewayUID, sNodeId); + label = "T55: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T56_CURRENT_SENSOR: + thingUID = new ThingUID(SoulissBindingConstants.T56_THING_TYPE, gatewayUID, sNodeId); + label = "T56: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T57_POWER_SENSOR: + thingUID = new ThingUID(SoulissBindingConstants.T57_THING_TYPE, gatewayUID, sNodeId); + label = "T57: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T61: + thingUID = new ThingUID(SoulissBindingConstants.T61_THING_TYPE, gatewayUID, sNodeId); + label = "T61: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T62_TEMPERATURE_SENSOR: + thingUID = new ThingUID(SoulissBindingConstants.T62_THING_TYPE, gatewayUID, sNodeId); + label = "T62: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T63_HUMIDITY_SENSOR: + thingUID = new ThingUID(SoulissBindingConstants.T63_THING_TYPE, gatewayUID, sNodeId); + label = "T63: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T64_LUX_SENSOR: + thingUID = new ThingUID(SoulissBindingConstants.T64_THING_TYPE, gatewayUID, sNodeId); + label = "T64: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T65_VOLTAGE_SENSOR: + thingUID = new ThingUID(SoulissBindingConstants.T65_THING_TYPE, gatewayUID, sNodeId); + label = "T65: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T66_CURRENT_SENSOR: + thingUID = new ThingUID(SoulissBindingConstants.T66_THING_TYPE, gatewayUID, sNodeId); + label = "T66: node " + node + slotLabel + slot; + break; + case SoulissProtocolConstants.SOULISS_T67_POWER_SENSOR: + thingUID = new ThingUID(SoulissBindingConstants.T67_THING_TYPE, gatewayUID, sNodeId); + label = "T67: node " + node + slotLabel + slot; + break; + default: { + logger.debug("no supported things found ..."); + } + } + if (thingUID != null) { + label = "[" + gwHandler.getThing().getUID().getAsString() + "] " + label; + var uniqueId = "N" + Byte.toString(node) + "S" + Byte.toString(slot); + discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(label) + .withProperty(SoulissBindingConstants.PROPERTY_NODE, node) + .withProperty(SoulissBindingConstants.PROPERTY_SLOT, slot) + .withProperty(SoulissBindingConstants.PROPERTY_UNIQUEID, uniqueId) + .withRepresentationProperty(SoulissBindingConstants.PROPERTY_UNIQUEID) + .withBridge(gwHandler.getThing().getUID()).build(); + thingDiscovered(discoveryResult); + gwHandler.setThereIsAThingDetection(); + } + } + } + + @Override + public void setThingHandler(ThingHandler handler) { + if (handler instanceof SoulissGatewayHandler) { + var localGwHandler = this.soulissGwHandler; + localGwHandler = (SoulissGatewayHandler) handler; + localGwHandler.discoverResult = this; + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return soulissGwHandler; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayHandler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayHandler.java new file mode 100644 index 000000000..bb6eaa564 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayHandler.java @@ -0,0 +1,248 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.config.GatewayConfig; +import org.openhab.binding.souliss.internal.discovery.DiscoverResult; +import org.openhab.binding.souliss.internal.discovery.SoulissGatewayDiscovery; +import org.openhab.binding.souliss.internal.protocol.CommonCommands; +import org.openhab.binding.souliss.internal.protocol.SendDispatcherRunnable; +import org.openhab.binding.souliss.internal.protocol.UDPListenDiscoverRunnable; +import org.openhab.core.common.NamedThreadFactory; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link SoulissGatewayHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissGatewayHandler extends BaseBridgeHandler { + + private final Logger logger = LoggerFactory.getLogger(SoulissGatewayHandler.class); + + private final CommonCommands commonCommands = new CommonCommands(); + + private @Nullable ExecutorService udpExecutorService; + + private @Nullable Future udpListenerJob; + private @Nullable ScheduledFuture pingScheduler; + private @Nullable ScheduledFuture subscriptionScheduler; + private @Nullable ScheduledFuture healthScheduler; + + boolean bGatewayDetected = false; + + private @Nullable SoulissGatewayDiscovery discoveryService; + + public @Nullable DiscoverResult discoverResult = null; + + public boolean thereIsAThingDetection = true; + + private Bridge bridge; + + private int nodes = 0; + private int maxTypicalXnode = 24; + private int countPingKo = 0; + + private GatewayConfig gwConfig = new GatewayConfig(); + + public GatewayConfig getGwConfig() { + return gwConfig; + } + + public SoulissGatewayHandler(Bridge br) { + super(br); + bridge = br; + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + // do nothing + } + + @Override + public Collection> getServices() { + return Collections.singleton(SoulissGatewayDiscovery.class); + } + + @Override + public void initialize() { + gwConfig = getConfigAs(GatewayConfig.class); + + logger.debug("Starting UDP server on Souliss Default Port for Topics (Publish&Subcribe)"); + + // new runnable udp listener + var udpServerDefaultPortRunnableClass = new UDPListenDiscoverRunnable(this.bridge, this.discoverResult); + // and exec on thread + var localUdpListenerJob = this.udpListenerJob; + if (localUdpListenerJob == null || localUdpListenerJob.isCancelled()) { + var localUdpExecutorService = this.udpExecutorService; + localUdpExecutorService = Executors + .newSingleThreadExecutor(new NamedThreadFactory(getThing().getUID().getAsString())); + localUdpExecutorService.submit(udpServerDefaultPortRunnableClass); + } + + // JOB PING + var soulissGatewayJobPingRunnable = new SoulissGatewayJobPing(this.bridge); + pingScheduler = scheduler.scheduleWithFixedDelay(soulissGatewayJobPingRunnable, 2, this.gwConfig.pingInterval, + TimeUnit.SECONDS); + // JOB SUBSCRIPTION + var soulissGatewayJobSubscriptionRunnable = new SoulissGatewayJobSubscription(bridge); + subscriptionScheduler = scheduler.scheduleWithFixedDelay(soulissGatewayJobSubscriptionRunnable, 5, + this.gwConfig.subscriptionInterval, TimeUnit.SECONDS); + + // JOB HEALTH OF NODES + var soulissGatewayJobHealthyRunnable = new SoulissGatewayJobHealthy(this.bridge); + healthScheduler = scheduler.scheduleWithFixedDelay(soulissGatewayJobHealthyRunnable, 5, + this.gwConfig.healthyInterval, TimeUnit.SECONDS); + + var soulissSendDispatcherRunnable = new SendDispatcherRunnable(this.bridge); + scheduler.scheduleWithFixedDelay(soulissSendDispatcherRunnable, 15, + SoulissBindingConstants.SEND_DISPATCHER_MIN_DELAY_CYCLE_IN_MILLIS, TimeUnit.MILLISECONDS); + } + + public void dbStructAnswerReceived() { + commonCommands.sendTypicalRequestFrame(this.gwConfig, nodes); + } + + public void setNodes(int nodes) { + this.nodes = nodes; + } + + public int getNodes() { + var maxNode = 0; + for (Thing thing : getThing().getThings()) { + if (thing.getThingTypeUID().equals(SoulissBindingConstants.TOPICS_THING_TYPE)) { + continue; + } + var cfg = thing.getConfiguration(); + var props = cfg.getProperties(); + var pNode = props.get("node"); + if (pNode != null) { + var thingNode = Integer.parseInt(pNode.toString()); + + if (thingNode > maxNode) { + maxNode = thingNode; + } + } + // at the end the length of the list will be equal to the number of present nodes + } + return maxNode + 1; + } + + public void setMaxTypicalXnode(int maxTypicalXnode) { + this.maxTypicalXnode = maxTypicalXnode; + } + + public int getMaxTypicalXnode() { + return maxTypicalXnode; + } + + /** + * The {@link gatewayDetected} is used to notify that UDPServer decoded a Ping Response from gateway + */ + + public void gatewayDetected() { + updateStatus(ThingStatus.ONLINE); + // reset counter + countPingKo = 0; + } + + public void pingSent() { + if (++countPingKo > 3) { + var bridgeHandler = bridge.getHandler(); + if (bridgeHandler != null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Gateway " + bridgeHandler.getThing().getUID() + " do not respond to " + countPingKo + " ping"); + } + } + } + + public void sendSubscription() { + if (this.gwConfig.gatewayLanAddress.length() > 0) { + int totNodes = getNodes(); + commonCommands.sendSUBSCRIPTIONframe(this.gwConfig, totNodes); + } + logger.debug("Sent subscription packet"); + } + + public void setThereIsAThingDetection() { + thereIsAThingDetection = true; + } + + public void resetThereIsAThingDetection() { + thereIsAThingDetection = false; + } + + public @Nullable SoulissGatewayDiscovery getDiscoveryService() { + return this.discoveryService; + } + + public void setDiscoveryService(SoulissGatewayDiscovery discoveryService) { + this.discoveryService = discoveryService; + } + + @Override + public void dispose() { + var localPingScheduler = this.pingScheduler; + if (localPingScheduler != null) { + localPingScheduler.cancel(true); + } + var localSubscriptionScheduler = this.subscriptionScheduler; + if (localSubscriptionScheduler != null) { + localSubscriptionScheduler.cancel(true); + } + var localHealthScheduler = this.healthScheduler; + if (localHealthScheduler != null) { + localHealthScheduler.cancel(true); + } + var localUdpListenerJob = this.udpListenerJob; + if (localUdpListenerJob != null) { + localUdpListenerJob.cancel(true); + } + var localUdpExecutorService = this.udpExecutorService; + if (localUdpExecutorService != null) { + localUdpExecutorService.shutdownNow(); + } + + super.dispose(); + } + + public void setBridgeStatus(boolean isOnline) { + logger.debug("setBridgeStatus(): Setting Bridge to {}", isOnline ? ThingStatus.ONLINE : ThingStatus.OFFLINE); + + updateStatus(isOnline ? ThingStatus.ONLINE : ThingStatus.OFFLINE); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayJobHealthy.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayJobHealthy.java new file mode 100644 index 000000000..32072317d --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayJobHealthy.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.protocol.CommonCommands; +import org.openhab.core.thing.Bridge; + +/** + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissGatewayJobHealthy implements Runnable { + + private @Nullable SoulissGatewayHandler gwHandler; + + private final CommonCommands commonCommands = new CommonCommands(); + + public SoulissGatewayJobHealthy(Bridge bridge) { + this.gwHandler = (SoulissGatewayHandler) bridge.getHandler(); + } + + @Override + public void run() { + sendHealthyRequest(); + } + + private void sendHealthyRequest() { + var localGwHandler = this.gwHandler; + // sending healthy packet + if ((localGwHandler != null) && (localGwHandler.getGwConfig().gatewayLanAddress.length() > 0)) { + commonCommands.sendHealthyRequestFrame(localGwHandler.getGwConfig(), localGwHandler.getNodes()); + // healthy packet sent + } + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayJobPing.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayJobPing.java new file mode 100644 index 000000000..5b431ba69 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayJobPing.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.protocol.CommonCommands; +import org.openhab.core.thing.Bridge; + +/** + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissGatewayJobPing implements Runnable { + + private @Nullable SoulissGatewayHandler gwHandler; + + private final CommonCommands commonCommands = new CommonCommands(); + + public SoulissGatewayJobPing(Bridge bridge) { + var bridgeHandler = bridge.getHandler(); + if (bridgeHandler != null) { + gwHandler = (SoulissGatewayHandler) bridgeHandler; + } + } + + @Override + public void run() { + SoulissGatewayHandler localGwHandler = this.gwHandler; + if (localGwHandler != null) { + sendPing(localGwHandler); + + localGwHandler.pingSent(); + } + } + + private void sendPing(SoulissGatewayHandler soulissGwHandler) { + // sending ping packet + + if (soulissGwHandler.getGwConfig().gatewayLanAddress.length() > 0) { + commonCommands.sendPing(soulissGwHandler.getGwConfig()); + // ping packet sent + } + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayJobSubscription.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayJobSubscription.java new file mode 100644 index 000000000..9ccbeb3bb --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayJobSubscription.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.thing.Bridge; + +/** + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissGatewayJobSubscription implements Runnable { + + private @Nullable SoulissGatewayHandler gwHandler; + + public SoulissGatewayJobSubscription(Bridge bridge) { + this.gwHandler = (SoulissGatewayHandler) bridge.getHandler(); + } + + @Override + public void run() { + sendSubscription(); + } + + private void sendSubscription() { + SoulissGatewayHandler localGwHandler = this.gwHandler; + if (localGwHandler != null) { + localGwHandler.sendSubscription(); + } + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGenericActionMessage.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGenericActionMessage.java new file mode 100644 index 000000000..a434ab1f4 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGenericActionMessage.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.library.types.DateTimeType; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class implements the base Souliss Action Message. All Action Messages derives from + * this class + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + * @author Tonino Fazio - @since 1.7.0 + */ + +@NonNullByDefault +public abstract class SoulissGenericActionMessage extends BaseThingHandler { + + Thing thingGenActMsg; + + private String sTopicNumber = ""; + private String sTopicVariant = ""; + + private String timestamp = ""; + private final Logger logger = LoggerFactory.getLogger(SoulissGenericActionMessage.class); + + protected SoulissGenericActionMessage(Thing pThing) { + super(pThing); + thingGenActMsg = pThing; + + try { + var cfg = thingGenActMsg.getConfiguration(); + var props = cfg.getProperties(); + var pTopicNumber = props.get("number"); + var pTopicVariant = props.get("number"); + if (pTopicNumber != null) { + sTopicNumber = pTopicNumber.toString(); + } + if (pTopicVariant != null) { + sTopicVariant = pTopicVariant.toString(); + } + } catch (Exception e) { + logger.debug("Item Definition Error. Use ex:'souliss:t11:thing_id'"); + } + } + + /** + * @return the Topic Number + */ + public String getTopicNumber() { + return sTopicNumber; + } + + /** + * @param the Topic Variant + */ + public String getTopicVariant() { + return sTopicVariant; + } + + public DateTimeType getLastUpdateTime() { + return DateTimeType.valueOf(timestamp); + } + + public void setUpdateTimeNow() { + timestamp = getTimestamp(); + } + + /** + * Create a time stamp as "yyyy-MM-dd'T'HH:mm:ssz" + * + * @return String timestamp + */ + private static String getTimestamp() { + // Pattern : yyyy-MM-dd'T'HH:mm:ssz + var sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz"); + var n = new Date(); + return sdf.format(n.getTime()); + } + + @Override + public void thingUpdated(Thing thing) { + this.thingGenActMsg = thing; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGenericHandler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGenericHandler.java new file mode 100644 index 000000000..f6c32d985 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGenericHandler.java @@ -0,0 +1,214 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissProtocolConstants; +import org.openhab.binding.souliss.internal.config.GatewayConfig; +import org.openhab.binding.souliss.internal.protocol.CommonCommands; +import org.openhab.core.library.types.DateTimeType; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.OpenClosedType; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class implements the base Souliss Typical All other Typicals derive from + * this class + * + * ...from wiki of Dario De Maio + * In Souliss the logics that drive your lights, curtains, LED, and + * others are pre-configured into so called Typicals. A Typical is a + * logic with a predefined set of inputs and outputs and a know + * behavior, are used to standardize the user interface and have a + * configuration-less behavior. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public abstract class SoulissGenericHandler extends BaseThingHandler implements TypicalCommonMethods { + + private int iSlot; + private int iNode; + private final Logger logger = LoggerFactory.getLogger(SoulissGenericHandler.class); + + private final CommonCommands commonCommands = new CommonCommands(); + + // 0 means that Secure Send is disabled + boolean bSecureSend = false; + // true means that expected value is setpoint (only for T31, T19 and T6x) + boolean bExpectedValueSameAsSet = false; + + protected SoulissGenericHandler(Thing thing) { + super(thing); + } + + /** + * @return the iSlot + */ + public int getSlot() { + return iSlot; + } + + @Override + public void initialize() { + try { + var cfg = thing.getConfiguration(); + var props = cfg.getProperties(); + + var pNode = props.get("node"); + var pSlot = props.get("slot"); + + if ((pNode != null) && (pSlot != null)) { + iNode = Integer.parseInt(pNode.toString()); + iSlot = Integer.parseInt(pSlot.toString()); + updateProperty(SoulissBindingConstants.PROPERTY_NODE, Integer.toString(iNode)); + updateProperty(SoulissBindingConstants.PROPERTY_SLOT, Integer.toString(iSlot)); + updateProperty(SoulissBindingConstants.PROPERTY_UNIQUEID, + "N" + Integer.toString(iNode) + "S" + Integer.toString(iSlot)); + } + } catch (Exception e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Error getting node/slot from souliss typical (thing config)"); + } + } + + /** + * @param SoulissNode + * the SoulissNodeID to get + */ + public int getNode() { + return iNode; + } + + protected synchronized void commandReadNodeTypsStates() { + var gwConfig = getGatewayConfig(); + if (gwConfig != null) { + commonCommands.sendTypicalRequestFrame(gwConfig, this.getNode(), 1); + } + } + + /** + * Send a command as hexadecimal, e.g.: SOULISS_T1N_ON_CMD = 0x02; short + * SOULISS_T1N_OFF_CMD = 0x04; + * + * @param command + */ + public void commandSEND(byte command) { + var gwConfig = getGatewayConfig(); + if (gwConfig != null) { + commonCommands.sendFORCEFrame(gwConfig, this.getNode(), this.getSlot(), command); + } + } + + public void commandSendRgb(byte command, byte r, byte g, byte b) { + var gwConfig = getGatewayConfig(); + if (gwConfig != null) { + commonCommands.sendFORCEFrame(gwConfig, command, r, g, b); + } + } + + public void commandSEND(byte command, byte b1, byte b2) { + var gwConfig = getGatewayConfig(); + if (gwConfig != null) { + commonCommands.sendFORCEFrameT31SetPoint(gwConfig, this.getNode(), this.getSlot(), command, b1, b2); + } + } + + public void commandSEND(byte b1, byte b2) { + var gwConfig = getGatewayConfig(); + if (gwConfig != null) { + commonCommands.sendFORCEFrameT61SetPoint(gwConfig, this.getNode(), this.getSlot(), b1, b2); + } + } + + /** + * Create a time stamp as "yyyy-MM-dd'T'HH:mm:ssz" + * + * @return String timestamp + */ + private static String getTimestamp() { + // Pattern : yyyy-MM-dd'T'HH:mm:ssz + var sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz"); + var n = new Date(); + return sdf.format(n.getTime()); + } + + @Override + public void thingUpdated(Thing thing) { + updateThing(thing); + } + + public @Nullable GatewayConfig getGatewayConfig() { + var bridge = getBridge(); + if (bridge != null) { + SoulissGatewayHandler bridgeHandler = (SoulissGatewayHandler) bridge.getHandler(); + if (bridgeHandler != null) { + return bridgeHandler.getGwConfig(); + } + } + return null; + } + + public @Nullable String getLabel() { + return getThing().getLabel(); + } + + public void setHealthy(byte shHealthy) { + this.updateState(SoulissBindingConstants.HEALTHY_CHANNEL, new DecimalType(shHealthy & 0xFF)); + this.updateStatus(ThingStatus.ONLINE); + } + + public void setLastStatusStored() { + this.updateState(SoulissBindingConstants.LASTSTATUSSTORED_CHANNEL, DateTimeType.valueOf(getTimestamp())); + } + + protected @Nullable OnOffType getOhStateOnOffFromSoulissVal(byte sVal) { + if (sVal == SoulissProtocolConstants.SOULISS_T1N_ON_COIL) { + return OnOffType.ON; + } else if (sVal == SoulissProtocolConstants.SOULISS_T1N_OFF_COIL) { + return OnOffType.OFF; + } else if (sVal == SoulissProtocolConstants.SOULISS_T1N_ON_FEEDBACK) { + return OnOffType.ON; + } else if (sVal == SoulissProtocolConstants.SOULISS_T1N_OFF_FEEDBACK) { + return OnOffType.OFF; + } else if (sVal == SoulissProtocolConstants.SOULISS_T4N_NOT_ARMED) { + return OnOffType.OFF; + } else if (sVal == SoulissProtocolConstants.SOULISS_T4N_ARMED) { + return OnOffType.ON; + } + + return null; + } + + protected @Nullable OpenClosedType getOhStateOpenCloseFromSoulissVal(byte sVal) { + if (sVal == SoulissProtocolConstants.SOULISS_T1N_ON_COIL) { + return OpenClosedType.CLOSED; + } else if (sVal == SoulissProtocolConstants.SOULISS_T1N_OFF_COIL) { + return OpenClosedType.OPEN; + } + return null; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT11Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT11Handler.java new file mode 100644 index 000000000..e0c85835d --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT11Handler.java @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissProtocolConstants; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; +import org.openhab.core.types.PrimitiveType; +import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link SoulissT11Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissT11Handler extends SoulissGenericHandler { + + private final Logger logger = LoggerFactory.getLogger(SoulissT11Handler.class); + private byte t1nRawState = 0xF; // dummy value for first init + private byte xSleepTime = 0; + + public SoulissT11Handler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + switch (channelUID.getId()) { + case SoulissBindingConstants.ONOFF_CHANNEL: + OnOffType val = getOhStateOnOffFromSoulissVal(t1nRawState); + if (val != null) { + updateState(channelUID, val); + } + break; + default: + logger.debug("Unknown channel for T11 thing: {}", channelUID); + } + } else { + switch (channelUID.getId()) { + case SoulissBindingConstants.ONOFF_CHANNEL: + if (command.equals(OnOffType.ON)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_ON_CMD); + } else if (command.equals(OnOffType.OFF)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_OFF_CMD); + } + break; + + case SoulissBindingConstants.SLEEP_CHANNEL: + if (command.equals(OnOffType.ON)) { + commandSEND((byte) (SoulissProtocolConstants.SOULISS_T1N_TIMED + xSleepTime)); + // set Off + updateState(channelUID, OnOffType.OFF); + + } + break; + default: + logger.debug("Unknown channel for T11 thing: {}", channelUID); + } + } + } + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + + var configurationMap = getThing().getConfiguration(); + if (configurationMap.get(SoulissBindingConstants.SLEEP_CHANNEL) != null) { + xSleepTime = ((BigDecimal) configurationMap.get(SoulissBindingConstants.SLEEP_CHANNEL)).byteValue(); + } + if (configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND) != null) { + bSecureSend = ((Boolean) configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND)).booleanValue(); + } + } + + @Override + public byte getExpectedRawState(byte bCmd) { + if (bSecureSend) { + if (bCmd == SoulissProtocolConstants.SOULISS_T1N_ON_CMD) { + return SoulissProtocolConstants.SOULISS_T1N_ON_COIL; + } else if (bCmd == SoulissProtocolConstants.SOULISS_T1N_OFF_CMD) { + return SoulissProtocolConstants.SOULISS_T1N_OFF_COIL; + } else if (bCmd >= SoulissProtocolConstants.SOULISS_T1N_TIMED) { + // SLEEP + return SoulissProtocolConstants.SOULISS_T1N_ON_COIL; + } + } + return -1; + } + + void setState(@Nullable PrimitiveType state) { + if (state != null) { + this.updateState(SoulissBindingConstants.SLEEP_CHANNEL, OnOffType.OFF); + this.updateState(SoulissBindingConstants.ONOFF_CHANNEL, (OnOffType) state); + } + } + + @Override + public void setRawState(byte rawState) { + // update Last Status stored time + super.setLastStatusStored(); + // update item state only if it is different from previous + if (t1nRawState != rawState) { + this.setState(getOhStateOnOffFromSoulissVal(rawState)); + } + t1nRawState = rawState; + } + + @Override + public byte getRawState() { + return t1nRawState; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT12Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT12Handler.java new file mode 100644 index 000000000..43bf6b56b --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT12Handler.java @@ -0,0 +1,176 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissProtocolConstants; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; +import org.openhab.core.types.PrimitiveType; +import org.openhab.core.types.RefreshType; + +/** + * The {@link SoulissT12Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT12Handler extends SoulissGenericHandler { + + private byte t1nRawState = 0xF; + private byte xSleepTime = 0; + + public SoulissT12Handler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + + var configurationMap = getThing().getConfiguration(); + if (configurationMap.get(SoulissBindingConstants.SLEEP_CHANNEL) != null) { + xSleepTime = ((BigDecimal) configurationMap.get(SoulissBindingConstants.SLEEP_CHANNEL)).byteValue(); + } + if (configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND) != null) { + bSecureSend = ((Boolean) configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND)).booleanValue(); + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + switch (channelUID.getId()) { + case SoulissBindingConstants.ONOFF_CHANNEL: + switch (t1nRawState) { + case SoulissProtocolConstants.SOULISS_T1N_ON_COIL_AUTO: + case SoulissProtocolConstants.SOULISS_T1N_ON_COIL: + this.setState(OnOffType.ON); + break; + case SoulissProtocolConstants.SOULISS_T1N_OFF_COIL_AUTO: + case SoulissProtocolConstants.SOULISS_T1N_OFF_COIL: + this.setState(OnOffType.OFF); + break; + default: + break; + } + break; + case SoulissBindingConstants.AUTOMODE_CHANNEL: + switch (t1nRawState) { + case SoulissProtocolConstants.SOULISS_T1N_ON_COIL_AUTO: + case SoulissProtocolConstants.SOULISS_T1N_OFF_COIL_AUTO: + this.setStateAutomode(OnOffType.ON); + break; + case SoulissProtocolConstants.SOULISS_T1N_ON_COIL: + case SoulissProtocolConstants.SOULISS_T1N_OFF_COIL: + this.setStateAutomode(OnOffType.OFF); + break; + default: + break; + } + break; + default: + break; + } + } else + + { + switch (channelUID.getId()) { + case SoulissBindingConstants.ONOFF_CHANNEL: + if (command.equals(OnOffType.ON)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_ON_CMD); + } else if (command.equals(OnOffType.OFF)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_OFF_CMD); + } + break; + case SoulissBindingConstants.AUTOMODE_CHANNEL: + if (command.equals(OnOffType.ON)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_AUTO_CMD); + } + break; + case SoulissBindingConstants.SLEEP_CHANNEL: + if (command.equals(OnOffType.ON)) { + commandSEND((byte) (SoulissProtocolConstants.SOULISS_T1N_TIMED + xSleepTime)); + // set Off + updateState(channelUID, OnOffType.OFF); + } + break; + default: + break; + + } + } + } + + public void setState(PrimitiveType state) { + this.updateState(SoulissBindingConstants.ONOFF_CHANNEL, (OnOffType) state); + } + + public void setStateAutomode(PrimitiveType state) { + this.updateState(SoulissBindingConstants.AUTOMODE_CHANNEL, (OnOffType) state); + } + + @Override + public void setRawState(byte rawState) { + // update Last Status stored time + super.setLastStatusStored(); + + // update item state only if it is different from previous + if (t1nRawState != rawState) { + if (rawState == SoulissProtocolConstants.SOULISS_T1N_ON_COIL_AUTO) { + this.setState(OnOffType.ON); + this.setStateAutomode(OnOffType.ON); + } else if (rawState == SoulissProtocolConstants.SOULISS_T1N_OFF_COIL_AUTO) { + this.setState(OnOffType.OFF); + this.setStateAutomode(OnOffType.ON); + } else if (rawState == SoulissProtocolConstants.SOULISS_T1N_ON_COIL) { + this.setState(OnOffType.ON); + this.setStateAutomode(OnOffType.OFF); + } else if (rawState == SoulissProtocolConstants.SOULISS_T1N_OFF_COIL) { + this.setState(OnOffType.OFF); + this.setStateAutomode(OnOffType.OFF); + } + } + t1nRawState = rawState; + } + + @Override + public byte getRawState() { + return t1nRawState; + } + + @Override + public byte getExpectedRawState(byte bCommand) { + if (bSecureSend) { + if (bCommand == SoulissProtocolConstants.SOULISS_T1N_ON_CMD) { + return SoulissProtocolConstants.SOULISS_T1N_ON_COIL; + } else if (bCommand == SoulissProtocolConstants.SOULISS_T1N_OFF_CMD) { + return SoulissProtocolConstants.SOULISS_T1N_OFF_COIL; + } else if (bCommand >= SoulissProtocolConstants.SOULISS_T1N_TIMED) { + // SLEEP + return SoulissProtocolConstants.SOULISS_T1N_ON_COIL; + } + } + return -1; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT13Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT13Handler.java new file mode 100644 index 000000000..34786d12e --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT13Handler.java @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.OpenClosedType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; +import org.openhab.core.types.PrimitiveType; +import org.openhab.core.types.RefreshType; + +/** + * The {@link SoulissT13Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissT13Handler extends SoulissGenericHandler { + + private byte t1nRawState = 0xF; + + public SoulissT13Handler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + } + + public void setState(@Nullable PrimitiveType state) { + super.setLastStatusStored(); + if (state != null) { + if (state instanceof OnOffType) { + this.updateState(SoulissBindingConstants.STATEONOFF_CHANNEL, (OnOffType) state); + } + + if (state instanceof OpenClosedType) { + this.updateState(SoulissBindingConstants.STATEOPENCLOSE_CHANNEL, (OpenClosedType) state); + } + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + switch (channelUID.getId()) { + case SoulissBindingConstants.STATEONOFF_CHANNEL: + OnOffType valonOff = getOhStateOnOffFromSoulissVal(t1nRawState); + if (valonOff != null) { + updateState(channelUID, valonOff); + } + break; + case SoulissBindingConstants.STATEOPENCLOSE_CHANNEL: + OpenClosedType valOpenClose = getOhStateOpenCloseFromSoulissVal(t1nRawState); + if (valOpenClose != null) { + updateState(channelUID, valOpenClose); + } + break; + default: + break; + } + } + } + + @Override + public void setRawState(byte rawState) { + // update Last Status stored time + super.setLastStatusStored(); + // update item state only if it is different from previous + if (t1nRawState != rawState) { + this.setState(getOhStateOpenCloseFromSoulissVal(rawState)); + this.setState(getOhStateOnOffFromSoulissVal(rawState)); + } + t1nRawState = rawState; + } + + @Override + public byte getRawState() { + return 0; + } + + @Override + public byte getExpectedRawState(byte bCommand) { + // Secure Send is disabled + return -1; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT14Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT14Handler.java new file mode 100644 index 000000000..4556c6cf3 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT14Handler.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissProtocolConstants; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; +import org.openhab.core.types.PrimitiveType; +import org.openhab.core.types.RefreshType; + +/** + * The {@link SoulissT14Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissT14Handler extends SoulissGenericHandler { + + private byte t1nRawState = 0xF; + + public SoulissT14Handler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + switch (channelUID.getId()) { + case SoulissBindingConstants.PULSE_CHANNEL: + OnOffType valPulse = getOhStateOnOffFromSoulissVal(t1nRawState); + if (valPulse != null) { + updateState(channelUID, valPulse); + } + break; + default: + break; + } + } else { + switch (channelUID.getId()) { + case SoulissBindingConstants.PULSE_CHANNEL: + if (command.equals(OnOffType.ON)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_ON_CMD); + } else if (command.equals(OnOffType.OFF)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_OFF_CMD); + } + break; + default: + break; + } + } + } + + public void setState(@Nullable PrimitiveType state) { + super.setLastStatusStored(); + if (state != null) { + this.updateState(SoulissBindingConstants.PULSE_CHANNEL, (OnOffType) state); + } + } + + @Override + public void setRawState(byte rawState) { + // update Last Status stored time + super.setLastStatusStored(); + // update item state only if it is different from previous + if (t1nRawState != rawState) { + this.setState(getOhStateOnOffFromSoulissVal(rawState)); + } + t1nRawState = rawState; + } + + @Override + public byte getRawState() { + return t1nRawState; + } + + @Override + public byte getExpectedRawState(byte bCommand) { + // Secure Send is disabled for T14 + return -1; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT16Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT16Handler.java new file mode 100644 index 000000000..71e427eeb --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT16Handler.java @@ -0,0 +1,233 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissProtocolConstants; +import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.UpDownType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; +import org.openhab.core.types.PrimitiveType; +import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link SoulissT16Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT16Handler extends SoulissGenericHandler { + private final Logger logger = LoggerFactory.getLogger(SoulissT16Handler.class); + private byte t1nRawStateByte0 = 0xF; + private byte t1nRawStateRedByte1 = 0x00; + private byte t1nRawStateGreenByte2 = 0x00; + private byte t1nRawStateBluByte3 = 0x00; + + private HSBType hsbState = HSBType.WHITE; + + byte xSleepTime = 0; + + public SoulissT16Handler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + switch (channelUID.getId()) { + case SoulissBindingConstants.ONOFF_CHANNEL: + OnOffType valOnOff = getOhStateOnOffFromSoulissVal(t1nRawStateByte0); + if (valOnOff != null) { + updateState(channelUID, valOnOff); + } + break; + case SoulissBindingConstants.LED_COLOR_CHANNEL: + updateState(channelUID, gethsb(t1nRawStateRedByte1, t1nRawStateGreenByte2, t1nRawStateBluByte3)); + break; + case SoulissBindingConstants.DIMMER_BRIGHTNESS_CHANNEL: + updateState(channelUID, + PercentType.valueOf(gethsb(t1nRawStateRedByte1, t1nRawStateGreenByte2, t1nRawStateBluByte3) + .getBrightness().toString())); + break; + default: + break; + } + } else { + switch (channelUID.getId()) { + case SoulissBindingConstants.ONOFF_CHANNEL: + if (command.equals(OnOffType.ON)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_ON_CMD); + + } else if (command.equals(OnOffType.OFF)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_OFF_CMD); + } + break; + case SoulissBindingConstants.WHITE_MODE_CHANNEL: + if (command instanceof OnOffType) { + hsbState = HSBType.fromRGB(255, 255, 255); + commandSendRgb(SoulissProtocolConstants.SOULISS_T1N_SET, (byte) 255, (byte) 255, (byte) 255); + updateState(SoulissBindingConstants.LED_COLOR_CHANNEL, hsbState); + } + break; + case SoulissBindingConstants.SLEEP_CHANNEL: + if (command instanceof OnOffType) { + commandSEND((byte) (SoulissProtocolConstants.SOULISS_T1N_TIMED + xSleepTime)); + // set Off + updateState(channelUID, OnOffType.OFF); + } + break; + + case SoulissBindingConstants.DIMMER_BRIGHTNESS_CHANNEL: + if (command instanceof PercentType) { + updateState(SoulissBindingConstants.LED_COLOR_CHANNEL, + gethsb(t1nRawStateRedByte1, t1nRawStateGreenByte2, t1nRawStateBluByte3)); + commandSendRgb(SoulissProtocolConstants.SOULISS_T1N_SET, + (byte) (hsbState.getRed().shortValue() * (255.00 / 100)), + (byte) (hsbState.getGreen().shortValue() * (255.00 / 100)), + (byte) (hsbState.getBlue().shortValue() * (255.00 / 100))); + + } else if (command.equals(OnOffType.ON)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_ON_CMD); + + } else if (command.equals(OnOffType.OFF)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_OFF_CMD); + } + break; + + case SoulissBindingConstants.ROLLER_BRIGHTNESS_CHANNEL: + if (command.equals(UpDownType.UP)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_BRIGHT_UP); + } else if (command.equals(UpDownType.DOWN)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_BRIGHT_DOWN); + } + break; + + case SoulissBindingConstants.LED_COLOR_CHANNEL: + if (command instanceof HSBType) { + HSBType localHsbState = (HSBType) command; + + updateState(SoulissBindingConstants.DIMMER_BRIGHTNESS_CHANNEL, + PercentType.valueOf(hsbState.getBrightness().toString())); + commandSendRgb(SoulissProtocolConstants.SOULISS_T1N_SET, + (byte) (localHsbState.getRed().shortValue() * 255.00 / 100), + (byte) (localHsbState.getGreen().shortValue() * 255.00 / 100), + (byte) (localHsbState.getBlue().shortValue() * 255.00 / 100)); + } + break; + default: + break; + } + } + } + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + + var configurationMap = getThing().getConfiguration(); + + if (configurationMap.get(SoulissBindingConstants.SLEEP_CHANNEL) != null) { + xSleepTime = ((BigDecimal) configurationMap.get(SoulissBindingConstants.SLEEP_CHANNEL)).byteValue(); + } + if (configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND) != null) { + bSecureSend = ((Boolean) configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND)).booleanValue(); + } + } + + void setState(@Nullable PrimitiveType state) { + super.setLastStatusStored(); + updateState(SoulissBindingConstants.SLEEP_CHANNEL, OnOffType.OFF); + if (state != null) { + logger.debug("T16, setting state to {}", state.toFullString()); + this.updateState(SoulissBindingConstants.ONOFF_CHANNEL, (OnOffType) state); + } + } + + @Override + public void setRawState(byte rawState) { + throw new UnsupportedOperationException("Not Implemented, yet."); + } + + public void setRawStateCommand(byte rawStateByte0) { + super.setLastStatusStored(); + if (rawStateByte0 != t1nRawStateByte0) { + this.setState(getOhStateOnOffFromSoulissVal(rawStateByte0)); + } + } + + public void setRawStateRgb(byte rawStateRedByte1, byte rawStateGreenByte2, byte rawStateBluByte3) { + super.setLastStatusStored(); + + if (rawStateRedByte1 != t1nRawStateRedByte1 || rawStateGreenByte2 != t1nRawStateGreenByte2 + || rawStateBluByte3 != t1nRawStateBluByte3) { + HSBType localHsbState = gethsb(rawStateRedByte1, rawStateGreenByte2, rawStateBluByte3); + logger.debug("T16, setting color to {},{},{}", rawStateRedByte1, rawStateGreenByte2, rawStateBluByte3); + + updateState(SoulissBindingConstants.DIMMER_BRIGHTNESS_CHANNEL, + PercentType.valueOf(localHsbState.getBrightness().toString())); + + updateState(SoulissBindingConstants.LED_COLOR_CHANNEL, localHsbState); + } + + t1nRawStateRedByte1 = rawStateRedByte1; + t1nRawStateGreenByte2 = rawStateGreenByte2; + t1nRawStateBluByte3 = rawStateBluByte3; + } + + @Override + public byte getRawState() { + throw new UnsupportedOperationException("Not Implemented, yet."); + } + + public byte getRawStateCommand() { + return t1nRawStateByte0; + } + + public byte[] getRawStateValues() { + return new byte[] { t1nRawStateRedByte1, t1nRawStateGreenByte2, t1nRawStateBluByte3 }; + } + + @Override + public byte getExpectedRawState(byte bCmd) { + if (bSecureSend) { + if (bCmd == SoulissProtocolConstants.SOULISS_T1N_ON_CMD) { + return SoulissProtocolConstants.SOULISS_T1N_ON_COIL; + } else if (bCmd == SoulissProtocolConstants.SOULISS_T1N_OFF_CMD) { + return SoulissProtocolConstants.SOULISS_T1N_OFF_COIL; + } else if (bCmd >= SoulissProtocolConstants.SOULISS_T1N_TIMED) { + // SLEEP + return SoulissProtocolConstants.SOULISS_T1N_ON_COIL; + } + } + return -1; + } + + HSBType gethsb(byte rawStateRedByte1, byte rawStateGreenByte2, byte rawStateBluByte3) { + return HSBType.fromRGB(rawStateRedByte1, rawStateGreenByte2, rawStateBluByte3); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT18Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT18Handler.java new file mode 100644 index 000000000..8ba705a3b --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT18Handler.java @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissProtocolConstants; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; +import org.openhab.core.types.PrimitiveType; +import org.openhab.core.types.RefreshType; + +/** + * The {@link SoulissT18Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissT18Handler extends SoulissGenericHandler { + + byte t1nRawState = 0xF; + byte xSleepTime = 0; + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + + var configurationMap = getThing().getConfiguration(); + + if (configurationMap.get(SoulissBindingConstants.SLEEP_CHANNEL) != null) { + xSleepTime = ((BigDecimal) configurationMap.get(SoulissBindingConstants.SLEEP_CHANNEL)).byteValue(); + } + if (configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND) != null) { + bSecureSend = ((Boolean) configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND)).booleanValue(); + } + } + + public SoulissT18Handler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + switch (channelUID.getId()) { + case SoulissBindingConstants.PULSE_CHANNEL: + OnOffType valPulse = getOhStateOnOffFromSoulissVal(t1nRawState); + if (valPulse != null) { + updateState(channelUID, valPulse); + } + break; + default: + break; + } + } else { + switch (channelUID.getId()) { + case SoulissBindingConstants.ONOFF_CHANNEL: + if (command.equals(OnOffType.ON)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_ON_CMD); + } else if (command.equals(OnOffType.OFF)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_OFF_CMD); + } + break; + default: + break; + } + } + } + + void setState(@Nullable PrimitiveType state) { + if (state != null) { + updateState(SoulissBindingConstants.SLEEP_CHANNEL, OnOffType.OFF); + this.updateState(SoulissBindingConstants.ONOFF_CHANNEL, (OnOffType) state); + } + } + + @Override + public void setRawState(byte rawState) { + // update Last Status stored time + super.setLastStatusStored(); + // update item state only if it is different from previous + if (t1nRawState != rawState) { + this.setState(getOhStateOnOffFromSoulissVal(rawState)); + } + t1nRawState = rawState; + } + + @Override + public byte getRawState() { + return t1nRawState; + } + + @Override + public byte getExpectedRawState(byte bCmd) { + if (bSecureSend) { + if (bCmd == SoulissProtocolConstants.SOULISS_T1N_ON_CMD) { + return SoulissProtocolConstants.SOULISS_T1N_ON_FEEDBACK; + } else if (bCmd == SoulissProtocolConstants.SOULISS_T1N_OFF_CMD) { + return SoulissProtocolConstants.SOULISS_T1N_OFF_FEEDBACK; + } else if (bCmd >= SoulissProtocolConstants.SOULISS_T1N_TIMED) { + // SLEEP + return SoulissProtocolConstants.SOULISS_T1N_ON_FEEDBACK; + } + } + return -1; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT19Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT19Handler.java new file mode 100644 index 000000000..651550a28 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT19Handler.java @@ -0,0 +1,183 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissProtocolConstants; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.UpDownType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; +import org.openhab.core.types.PrimitiveType; +import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link SoulissT19Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissT19Handler extends SoulissGenericHandler { + private final Logger logger = LoggerFactory.getLogger(SoulissT19Handler.class); + byte t1nRawStateByte0 = 0xF; + byte t1nRawStateBrigthnessByte1 = 0x00; + + byte xSleepTime = 0; + + public SoulissT19Handler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + switch (channelUID.getId()) { + case SoulissBindingConstants.ONOFF_CHANNEL: + OnOffType valOnOff = getOhStateOnOffFromSoulissVal(t1nRawStateByte0); + if (valOnOff != null) { + updateState(channelUID, valOnOff); + } + break; + case SoulissBindingConstants.DIMMER_BRIGHTNESS_CHANNEL: + updateState(SoulissBindingConstants.DIMMER_BRIGHTNESS_CHANNEL, + PercentType.valueOf(String.valueOf((t1nRawStateBrigthnessByte1 / 255) * 100))); + break; + default: + break; + } + } else { + switch (channelUID.getId()) { + case SoulissBindingConstants.ONOFF_CHANNEL: + if (command.equals(OnOffType.ON)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_ON_CMD); + + } else if (command.equals(OnOffType.OFF)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_OFF_CMD); + } + break; + + case SoulissBindingConstants.DIMMER_BRIGHTNESS_CHANNEL: + if (command instanceof PercentType) { + updateState(SoulissBindingConstants.DIMMER_BRIGHTNESS_CHANNEL, (PercentType) command); + commandSEND(SoulissProtocolConstants.SOULISS_T1N_SET, + (byte) (((PercentType) command).shortValue() * 255.00 / 100.00)); + } else if (command.equals(OnOffType.ON)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_ON_CMD); + + } else if (command.equals(OnOffType.OFF)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_OFF_CMD); + } + break; + + case SoulissBindingConstants.ROLLER_BRIGHTNESS_CHANNEL: + if (command.equals(UpDownType.UP)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_BRIGHT_UP); + } else if (command.equals(UpDownType.DOWN)) { + commandSEND(SoulissProtocolConstants.SOULISS_T1N_BRIGHT_DOWN); + } + break; + case SoulissBindingConstants.SLEEP_CHANNEL: + if (command instanceof OnOffType) { + commandSEND((byte) (SoulissProtocolConstants.SOULISS_T1N_TIMED + xSleepTime)); + } + break; + default: + break; + } + } + } + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + + var configurationMap = getThing().getConfiguration(); + if (configurationMap.get(SoulissBindingConstants.SLEEP_CHANNEL) != null) { + xSleepTime = ((BigDecimal) configurationMap.get(SoulissBindingConstants.SLEEP_CHANNEL)).byteValue(); + } + if (configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND) != null) { + bSecureSend = ((Boolean) configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND)).booleanValue(); + } + } + + public void setState(@Nullable PrimitiveType state) { + super.setLastStatusStored(); + if (state != null) { + updateState(SoulissBindingConstants.SLEEP_CHANNEL, OnOffType.OFF); + logger.debug("T19, setting state to {}", state.toFullString()); + this.updateState(SoulissBindingConstants.ONOFF_CHANNEL, (OnOffType) state); + } + } + + public void setRawStateDimmerValue(byte dimmerValue) { + try { + if (dimmerValue != t1nRawStateByte0 && dimmerValue >= 0) { + logger.debug("T19, setting dimmer to {}", dimmerValue); + updateState(SoulissBindingConstants.DIMMER_BRIGHTNESS_CHANNEL, + PercentType.valueOf(String.valueOf(Math.round(((double) dimmerValue / 255) * 100)))); + } + } catch (Exception ex) { + logger.warn("UUID: {}, had an update dimmer state error:{}", this.getThing().getUID().getAsString(), + ex.getMessage()); + } + } + + @Override + public void setRawState(byte rawState) { + // update Last Status stored time + super.setLastStatusStored(); + // update item state only if it is different from previous + if (t1nRawStateByte0 != rawState) { + this.setState(getOhStateOnOffFromSoulissVal(rawState)); + } + t1nRawStateByte0 = rawState; + } + + @Override + public byte getRawState() { + return t1nRawStateByte0; + } + + public byte getRawStateDimmerValue() { + return t1nRawStateBrigthnessByte1; + } + + @Override + public byte getExpectedRawState(byte bCmd) { + if (bSecureSend) { + if (bCmd == SoulissProtocolConstants.SOULISS_T1N_ON_CMD) { + return SoulissProtocolConstants.SOULISS_T1N_ON_COIL; + } else if (bCmd == SoulissProtocolConstants.SOULISS_T1N_OFF_CMD) { + return SoulissProtocolConstants.SOULISS_T1N_OFF_COIL; + } else if (bCmd >= SoulissProtocolConstants.SOULISS_T1N_TIMED) { + // SLEEP + return SoulissProtocolConstants.SOULISS_T1N_ON_COIL; + } + } + return -1; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT1AHandler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT1AHandler.java new file mode 100644 index 000000000..be83d079d --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT1AHandler.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.core.library.types.OpenClosedType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; + +/** + * The {@link SoulissT1AHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Luca Remigio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT1AHandler extends SoulissGenericHandler { + byte t1nRawState = 0xF; + + public SoulissT1AHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + throw new UnsupportedOperationException("Unsupported operation. Read Only"); + } + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + } + + private OpenClosedType getTypeFromBool(boolean value) { + if (!value) { + return OpenClosedType.CLOSED; + } + return OpenClosedType.OPEN; + } + + private boolean getBitState(int value, int bit) { + return ((value & (1L << bit)) != 0); + } + + @Override + public void setRawState(byte rawState) { + // update Last Status stored time + super.setLastStatusStored(); + // update item state only if it is different from previous + if (t1nRawState != rawState) { + this.updateState(SoulissBindingConstants.T1A_1_CHANNEL, getTypeFromBool(getBitState(rawState, 0))); + this.updateState(SoulissBindingConstants.T1A_2_CHANNEL, getTypeFromBool(getBitState(rawState, 1))); + this.updateState(SoulissBindingConstants.T1A_3_CHANNEL, getTypeFromBool(getBitState(rawState, 2))); + this.updateState(SoulissBindingConstants.T1A_4_CHANNEL, getTypeFromBool(getBitState(rawState, 3))); + this.updateState(SoulissBindingConstants.T1A_5_CHANNEL, getTypeFromBool(getBitState(rawState, 4))); + this.updateState(SoulissBindingConstants.T1A_6_CHANNEL, getTypeFromBool(getBitState(rawState, 5))); + this.updateState(SoulissBindingConstants.T1A_7_CHANNEL, getTypeFromBool(getBitState(rawState, 6))); + this.updateState(SoulissBindingConstants.T1A_8_CHANNEL, getTypeFromBool(getBitState(rawState, 7))); + } + t1nRawState = rawState; + } + + @Override + public byte getRawState() { + return t1nRawState; + } + + @Override + public byte getExpectedRawState(byte bCommand) { + // Secure Send is disabled + return -1; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT22Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT22Handler.java new file mode 100644 index 000000000..7b22b0353 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT22Handler.java @@ -0,0 +1,202 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissProtocolConstants; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StopMoveType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.library.types.UpDownType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; +import org.openhab.core.types.PrimitiveType; +import org.openhab.core.types.RefreshType; + +/** + * The {@link SoulissT22Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT22Handler extends SoulissGenericHandler { + byte t2nRawState = 0xF; + + public SoulissT22Handler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + + var configurationMap = getThing().getConfiguration(); + if (configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND) != null) { + bSecureSend = ((Boolean) configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND)).booleanValue(); + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + switch (channelUID.getId()) { + case SoulissBindingConstants.ROLLERSHUTTER_CHANNEL: + break; + default: + break; + } + } else { + switch (channelUID.getId()) { + case SoulissBindingConstants.ROLLERSHUTTER_CHANNEL: + if (command.equals(UpDownType.UP)) { + commandSEND(SoulissProtocolConstants.SOULISS_T2N_OPEN_CMD); + } else if (command.equals(UpDownType.DOWN)) { + commandSEND(SoulissProtocolConstants.SOULISS_T2N_CLOSE_CMD); + } else if (command.equals(StopMoveType.STOP)) { + commandSEND(SoulissProtocolConstants.SOULISS_T2N_STOP_CMD); + } + break; + case SoulissBindingConstants.ONOFF_CHANNEL: + if (command.equals(OnOffType.ON)) { + commandSEND(SoulissProtocolConstants.SOULISS_T2N_OPEN_CMD_LOCAL); + } else if (command.equals(OnOffType.OFF)) { + commandSEND(SoulissProtocolConstants.SOULISS_T2N_CLOSE_CMD_LOCAL); + } + break; + default: + break; + } + } + } + + public void setState(PrimitiveType state) { + if (state instanceof PercentType) { + this.updateState(SoulissBindingConstants.ROLLERSHUTTER_CHANNEL, (PercentType) state); + + } + } + + public void setStateMessage(String rollershutterMessage) { + this.updateState(SoulissBindingConstants.ROLLERSHUTTER_STATE_CHANNEL_CHANNEL, + StringType.valueOf(rollershutterMessage)); + } + + @Override + public void setRawState(byte rawState) { + // update Last Status stored time + super.setLastStatusStored(); + // update item state only if it is different from previous + if (t2nRawState != rawState) { + var val = getOhStateT22FromSoulissVal(rawState); + this.setState(val); + + if (rawState == SoulissProtocolConstants.SOULISS_T2N_OPEN_CMD) { + this.setStateMessage(SoulissBindingConstants.ROLLERSHUTTER_MESSAGE_OPENING_CHANNEL); + } else if (rawState == SoulissProtocolConstants.SOULISS_T2N_CLOSE_CMD) { + this.setStateMessage(SoulissBindingConstants.ROLLERSHUTTER_MESSAGE_CLOSING_CHANNEL); + } + switch (rawState) { + case SoulissProtocolConstants.SOULISS_T2N_COIL_STOP: + this.setStateMessage(SoulissBindingConstants.ROLLERSHUTTER_MESSAGE_STOP_CHANNEL); + break; + case SoulissProtocolConstants.SOULISS_T2N_COIL_OFF: + this.setStateMessage(SoulissBindingConstants.ROLLERSHUTTER_MESSAGE_OPENING_CHANNEL); + break; + case SoulissProtocolConstants.SOULISS_T2N_LIMSWITCH_CLOSE: + this.setStateMessage(SoulissBindingConstants.ROLLERSHUTTER_MESSAGE_LIMITSWITCH_CLOSE_CHANNEL); + break; + case SoulissProtocolConstants.SOULISS_T2N_LIMSWITCH_OPEN: + this.setStateMessage(SoulissBindingConstants.ROLLERSHUTTER_MESSAGE_LIMITSWITCH_OPEN_CHANNEL); + break; + case SoulissProtocolConstants.SOULISS_T2N_NOLIMSWITCH: + this.setStateMessage(SoulissBindingConstants.ROLLERSHUTTER_MESSAGE_LIMITSWITCH_OPEN_CHANNEL); + break; + case SoulissProtocolConstants.SOULISS_T2N_TIMER_OFF: + this.setStateMessage(SoulissBindingConstants.ROLLERSHUTTER_MESSAGE_TIMER_OFF); + break; + case SoulissProtocolConstants.SOULISS_T2N_STATE_OPEN: + this.setStateMessage(SoulissBindingConstants.ROLLERSHUTTER_MESSAGE_STATE_OPEN_CHANNEL); + break; + case SoulissProtocolConstants.SOULISS_T2N_STATE_CLOSE: + this.setStateMessage(SoulissBindingConstants.ROLLERSHUTTER_MESSAGE_STATE_CLOSE_CHANNEL); + break; + default: + break; + } + t2nRawState = rawState; + } + } + + private PercentType getOhStateT22FromSoulissVal(short sVal) { + var iState = 0; + switch (sVal) { + case SoulissProtocolConstants.SOULISS_T2N_COIL_OPEN: + iState = 0; + break; + case SoulissProtocolConstants.SOULISS_T2N_COIL_CLOSE: + iState = 100; + break; + case SoulissProtocolConstants.SOULISS_T2N_COIL_STOP: + iState = 50; + break; + case SoulissProtocolConstants.SOULISS_T2N_LIMSWITCH_CLOSE: + iState = 100; + break; + case SoulissProtocolConstants.SOULISS_T2N_LIMSWITCH_OPEN: + iState = 0; + break; + case SoulissProtocolConstants.SOULISS_T2N_NOLIMSWITCH: + iState = 50; + break; + case SoulissProtocolConstants.SOULISS_T2N_TIMER_OFF: + iState = 50; + break; + case SoulissProtocolConstants.SOULISS_T2N_STATE_OPEN: + iState = 0; + break; + case SoulissProtocolConstants.SOULISS_T2N_STATE_CLOSE: + iState = 100; + break; + default: + break; + } + return PercentType.valueOf(String.valueOf(iState)); + } + + @Override + public byte getRawState() { + return t2nRawState; + } + + @Override + public byte getExpectedRawState(byte bCmd) { + if (bSecureSend) { + if (bCmd == SoulissProtocolConstants.SOULISS_T2N_OPEN_CMD) { + return SoulissProtocolConstants.SOULISS_T2N_COIL_OPEN; + } else if (bCmd == SoulissProtocolConstants.SOULISS_T2N_CLOSE_CMD) { + return SoulissProtocolConstants.SOULISS_T2N_COIL_CLOSE; + } else if (bCmd >= SoulissProtocolConstants.SOULISS_T2N_STOP_CMD) { + return SoulissProtocolConstants.SOULISS_T2N_COIL_STOP; + } + } + return -1; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT31Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT31Handler.java new file mode 100644 index 000000000..e938c6208 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT31Handler.java @@ -0,0 +1,320 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.openhab.binding.souliss.internal.handler; + +import javax.measure.quantity.Temperature; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissProtocolConstants; +import org.openhab.binding.souliss.internal.protocol.HalfFloatUtils; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; +import org.openhab.core.types.PrimitiveType; +import org.openhab.core.types.RefreshType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link SoulissT31Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Luca Remigio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT31Handler extends SoulissGenericHandler { + + private final Logger logger = LoggerFactory.getLogger(SoulissT31Handler.class); + + QuantityType setMeasuredValue = new QuantityType<>("0"); + QuantityType setPointValue = new QuantityType<>("0"); + StringType fanStateValue = StringType.EMPTY; + StringType powerState = StringType.EMPTY; + StringType fireState = StringType.EMPTY; + + StringType lastModeState = StringType.EMPTY; + StringType modeStateValue = StringType.EMPTY; + + public SoulissT31Handler(Thing pThing) { + super(pThing); + thing = pThing; + } + + // called on every status change or change request + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (!(command instanceof RefreshType)) { + switch (channelUID.getId()) { + // FAN + case SoulissBindingConstants.T31_SYSTEM_CHANNEL: + if (command.equals(OnOffType.OFF)) { + commandSEND(SoulissProtocolConstants.SOULISS_T3N_SHUTDOWN); + } else { + if (modeStateValue.toString() + .equals(SoulissBindingConstants.T31_HEATINGMODE_MESSAGE_MODE_CHANNEL)) { + commandSEND(SoulissProtocolConstants.SOULISS_T3N_HEATING); + } else { + commandSEND(SoulissProtocolConstants.SOULISS_T3N_COOLING); + } + } + break; + case SoulissBindingConstants.T31_MODE_CHANNEL: + if (command.toString().equals(SoulissBindingConstants.T31_HEATINGMODE_MESSAGE_MODE_CHANNEL)) { + commandSEND(SoulissProtocolConstants.SOULISS_T3N_HEATING); + } else { + commandSEND(SoulissProtocolConstants.SOULISS_T3N_COOLING); + } + break; + case SoulissBindingConstants.T31_BUTTON_CHANNEL: + if (command.equals(OnOffType.ON)) { + commandSEND(SoulissProtocolConstants.SOULISS_T3N_AS_MEASURED); + } + break; + case SoulissBindingConstants.T31_FAN_CHANNEL: + switch (command.toString()) { + case SoulissBindingConstants.T31_FANHIGH_MESSAGE_FAN_CHANNEL: + commandSEND(SoulissProtocolConstants.SOULISS_T3N_FAN_MANUAL); + commandSEND(SoulissProtocolConstants.SOULISS_T3N_FAN_HIGH); + fanStateValue = StringType.valueOf(SoulissBindingConstants.T31_FANHIGH_MESSAGE_FAN_CHANNEL); + break; + case SoulissBindingConstants.T31_FANMEDIUM_MESSAGE_FAN_CHANNEL: + commandSEND(SoulissProtocolConstants.SOULISS_T3N_FAN_MANUAL); + commandSEND(SoulissProtocolConstants.SOULISS_T3N_FAN_MED); + fanStateValue = StringType + .valueOf(SoulissBindingConstants.T31_FANMEDIUM_MESSAGE_FAN_CHANNEL); + break; + case SoulissBindingConstants.T31_FANLOW_MESSAGE_FAN_CHANNEL: + commandSEND(SoulissProtocolConstants.SOULISS_T3N_FAN_MANUAL); + commandSEND(SoulissProtocolConstants.SOULISS_T3N_FAN_LOW); + fanStateValue = StringType.valueOf(SoulissBindingConstants.T31_FANLOW_MESSAGE_FAN_CHANNEL); + break; + case SoulissBindingConstants.T31_FANAUTO_MESSAGE_FAN_CHANNEL: + commandSEND(SoulissProtocolConstants.SOULISS_T3N_FAN_AUTO); + fanStateValue = StringType.valueOf(SoulissBindingConstants.T31_FANAUTO_MESSAGE_FAN_CHANNEL); + break; + case SoulissBindingConstants.T31_FANOFF_MESSAGE_FAN_CHANNEL: + commandSEND(SoulissProtocolConstants.SOULISS_T3N_FAN_OFF); + fanStateValue = StringType.valueOf(SoulissBindingConstants.T31_FANOFF_MESSAGE_FAN_CHANNEL); + break; + default: + logger.debug("Fan Channel handle not recognized, skipping.."); + break; + } + break; + case SoulissBindingConstants.T31_SETPOINT_CHANNEL: + if (command instanceof QuantityType) { + int uu = HalfFloatUtils.fromFloat(((QuantityType) command).floatValue()); + byte b2 = (byte) (uu >> 8); + byte b1 = (byte) uu; + // setpoint command + commandSEND(SoulissProtocolConstants.SOULISS_T31_USE_OF_SLOT_SETPOINT_COMMAND, b1, b2); + } + break; + + default: + logger.debug("state not recognized! skipping.."); + break; + } + } + } + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + + var configurationMap = getThing().getConfiguration(); + if (configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND) != null) { + bSecureSend = ((Boolean) configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND)).booleanValue(); + } + } + + public void setState(PrimitiveType state) { + this.updateState(SoulissBindingConstants.T31_BUTTON_CHANNEL, OnOffType.OFF); + + super.setLastStatusStored(); + if (state instanceof StringType) { + switch (state.toString()) { + case SoulissBindingConstants.T31_FANLOW_MESSAGE_FAN_CHANNEL: + case SoulissBindingConstants.T31_FANMEDIUM_MESSAGE_FAN_CHANNEL: + case SoulissBindingConstants.T31_FANHIGH_MESSAGE_FAN_CHANNEL: + case SoulissBindingConstants.T31_FANAUTO_MESSAGE_FAN_CHANNEL: + case SoulissBindingConstants.T31_FANOFF_MESSAGE_FAN_CHANNEL: + if (!fanStateValue.equals(state)) { + this.updateState(SoulissBindingConstants.T31_FAN_CHANNEL, (StringType) state); + fanStateValue = (StringType) state; + } + break; + + case SoulissBindingConstants.T31_HEATINGMODE_MESSAGE_MODE_CHANNEL: + case SoulissBindingConstants.T31_COOLINGMODE_MESSAGE_MODE_CHANNEL: + if (!modeStateValue.equals(state)) { + this.updateState(SoulissBindingConstants.T31_MODE_CHANNEL, (StringType) state); + modeStateValue = (StringType) state; + } + break; + + case SoulissBindingConstants.T31_OFF_MESSAGE_SYSTEM_CHANNEL: + if (!powerState.equals(state)) { + this.updateState(SoulissBindingConstants.T31_SYSTEM_CHANNEL, OnOffType.OFF); + powerState = (StringType) state; + } + break; + case SoulissBindingConstants.T31_ON_MESSAGE_SYSTEM_CHANNEL: + if (!powerState.equals(state)) { + this.updateState(SoulissBindingConstants.T31_SYSTEM_CHANNEL, OnOffType.ON); + powerState = (StringType) state; + } + break; + + case SoulissBindingConstants.T31_ON_MESSAGE_FIRE_CHANNEL: + if (!fireState.equals(state)) { + this.updateState(SoulissBindingConstants.T31_FIRE_CHANNEL, OnOffType.ON); + powerState = (StringType) state; + } + break; + case SoulissBindingConstants.T31_OFF_MESSAGE_FIRE_CHANNEL: + if (!fireState.equals(state)) { + this.updateState(SoulissBindingConstants.T31_FIRE_CHANNEL, OnOffType.OFF); + powerState = (StringType) state; + } + break; + + default: + } + + } + } + + public void setMeasuredValue(QuantityType valueOf) { + if ((valueOf instanceof QuantityType) && (!setMeasuredValue.equals(valueOf))) { + this.updateState(SoulissBindingConstants.T31_VALUE_CHANNEL, valueOf); + setMeasuredValue = valueOf; + } + } + + public void setSetpointValue(QuantityType valueOf) { + if ((valueOf instanceof QuantityType) && (!setPointValue.equals(valueOf))) { + this.updateState(SoulissBindingConstants.T31_SETPOINT_CHANNEL, valueOf); + setPointValue = valueOf; + } + } + + public void setRawStateValues(byte rawStateByte0, float valTemp, float valSetPoint) { + var sMessage = ""; + switch (getBitState(rawStateByte0, 0)) { + case 0: + sMessage = SoulissBindingConstants.T31_OFF_MESSAGE_SYSTEM_CHANNEL; + break; + case 1: + sMessage = SoulissBindingConstants.T31_ON_MESSAGE_SYSTEM_CHANNEL; + break; + default: + logger.debug("System Channel on/off not recognized, skipping"); + break; + } + this.setState(StringType.valueOf(sMessage)); + + switch (getBitState(rawStateByte0, 7)) { + case 0: + sMessage = SoulissBindingConstants.T31_HEATINGMODE_MESSAGE_MODE_CHANNEL; + break; + case 1: + sMessage = SoulissBindingConstants.T31_COOLINGMODE_MESSAGE_MODE_CHANNEL; + break; + default: + logger.debug("Mode not recognized, skipping"); + break; + } + this.setState(StringType.valueOf(sMessage)); + + // button indicating whether the system is running or not + switch (getBitState(rawStateByte0, 1) + getBitState(rawStateByte0, 2)) { + case 0: + sMessage = SoulissBindingConstants.T31_OFF_MESSAGE_FIRE_CHANNEL; + break; + case 1: + sMessage = SoulissBindingConstants.T31_ON_MESSAGE_FIRE_CHANNEL; + break; + default: + logger.debug("Fire not recognized, skipping"); + break; + } + this.setState(StringType.valueOf(sMessage)); + + // FAN SPEED + switch (getBitState(rawStateByte0, 3) + getBitState(rawStateByte0, 4) + getBitState(rawStateByte0, 5)) { + case 0: + sMessage = SoulissBindingConstants.T31_FANOFF_MESSAGE_FAN_CHANNEL; + break; + case 1: + sMessage = SoulissBindingConstants.T31_FANLOW_MESSAGE_FAN_CHANNEL; + break; + case 2: + sMessage = SoulissBindingConstants.T31_FANMEDIUM_MESSAGE_FAN_CHANNEL; + break; + case 3: + sMessage = SoulissBindingConstants.T31_FANHIGH_MESSAGE_FAN_CHANNEL; + break; + default: + logger.debug("Fan speed not recognized, skipping"); + break; + } + + this.setState(StringType.valueOf(sMessage)); + + // SLOT 1-2: Temperature Value + if (!Float.isNaN(valTemp)) { + this.setMeasuredValue(QuantityType.valueOf(valTemp, SIUnits.CELSIUS)); + } + + // SLOT 3-4: Setpoint Value + if (!Float.isNaN(valSetPoint)) { + this.setSetpointValue(QuantityType.valueOf(valSetPoint, SIUnits.CELSIUS)); + } + } + + @Override + public byte getRawState() { + return 0; + } + + @Override + public byte getExpectedRawState(byte bCommand) { + return 0; + } + + public byte getBitState(byte vRaw, int iBit) { + final var maskBit1 = 0x1; + + if (((vRaw >>> iBit) & maskBit1) == 0) { + return 0; + } else { + return 1; + } + } + + @Override + public void setRawState(byte rawState) { + throw new UnsupportedOperationException("Not Implemented, yet."); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT41Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT41Handler.java new file mode 100644 index 000000000..401b8efbc --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT41Handler.java @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissProtocolConstants; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; +import org.openhab.core.types.PrimitiveType; +import org.openhab.core.types.RefreshType; + +/** + * The {@link SoulissT41Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Luca Remigio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissT41Handler extends SoulissGenericHandler { + + byte t4nRawState = 0xF; + + public SoulissT41Handler(Thing thing) { + super(thing); + } + + // called on every status change or change request + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (!(command instanceof RefreshType)) { + if (channelUID.getId().equals(SoulissBindingConstants.T4N_ONOFFALARM_CHANNEL)) { + if (command instanceof OnOffType) { + if (command.equals(OnOffType.OFF)) { + commandSEND(SoulissProtocolConstants.SOULISS_T4N_NOT_ARMED); + } else if (command.equals(OnOffType.ON)) { + commandSEND(SoulissProtocolConstants.SOULISS_T4N_ARMED); + } + } + } else if ((channelUID.getAsString().split(":")[3].equals(SoulissBindingConstants.T4N_REARMALARM_CHANNEL)) + && (command instanceof OnOffType) && (command.equals(OnOffType.OFF))) { + commandSEND(SoulissProtocolConstants.SOULISS_T4N_REARM); + this.setState(StringType.valueOf(SoulissBindingConstants.T4N_REARMOFF_MESSAGE_CHANNEL)); + } + } + } + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + + var configurationMap = getThing().getConfiguration(); + if (configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND) != null) { + bSecureSend = ((Boolean) configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND)).booleanValue(); + } + } + + public void setState(PrimitiveType state) { + if (state instanceof OnOffType) { + this.updateState(SoulissBindingConstants.T4N_ONOFFALARM_CHANNEL, (OnOffType) state); + } else if (state instanceof StringType) { + switch (String.valueOf(state)) { + case SoulissBindingConstants.T4N_ALARMON_MESSAGE_CHANNEL: + this.updateState(SoulissBindingConstants.T4N_STATUSALARM_CHANNEL, OnOffType.ON); + break; + case SoulissBindingConstants.T4N_ALARMOFF_MESSAGE_CHANNEL: + this.updateState(SoulissBindingConstants.T4N_STATUSALARM_CHANNEL, OnOffType.OFF); + break; + default: + break; + } + } + // // Reset the rearm button. This is because if pressed, it does not turn off by itself + updateState(SoulissBindingConstants.T4N_REARMALARM_CHANNEL, OnOffType.OFF); + } + + @Override + public void setRawState(byte rawState) { + // update Last Status stored time + super.setLastStatusStored(); + // update item state only if it is different from previous + if (t4nRawState != rawState) { + switch (rawState) { + case SoulissProtocolConstants.SOULISS_T4N_NO_ANTITHEFT: + this.setState(OnOffType.OFF); + this.setState(StringType.valueOf(SoulissBindingConstants.T4N_ALARMOFF_MESSAGE_CHANNEL)); + break; + case SoulissProtocolConstants.SOULISS_T4N_ANTITHEFT: + this.setState(OnOffType.ON); + this.setState(StringType.valueOf(SoulissBindingConstants.T4N_ALARMOFF_MESSAGE_CHANNEL)); + break; + case SoulissProtocolConstants.SOULISS_T4N_IN_ALARM: + this.setState(StringType.valueOf(SoulissBindingConstants.T4N_ALARMON_MESSAGE_CHANNEL)); + break; + case SoulissProtocolConstants.SOULISS_T4N_ARMED: + this.setState(StringType.valueOf(SoulissBindingConstants.T4N_ARMED_MESSAGE_CHANNEL)); + break; + default: + break; + } + } + t4nRawState = rawState; + } + + @Override + public byte getRawState() { + return t4nRawState; + } + + @Override + public byte getExpectedRawState(byte bCmd) { + if (bSecureSend) { + if (bCmd == SoulissProtocolConstants.SOULISS_T4N_ARMED) { + return SoulissProtocolConstants.SOULISS_T4N_ANTITHEFT; + } else if (bCmd == SoulissProtocolConstants.SOULISS_T4N_NOT_ARMED) { + return SoulissProtocolConstants.SOULISS_T4N_NO_ANTITHEFT; + } else if (bCmd >= SoulissProtocolConstants.SOULISS_T4N_REARM) { + return SoulissProtocolConstants.SOULISS_T4N_ANTITHEFT; + } + } + return -1; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT42Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT42Handler.java new file mode 100644 index 000000000..cbcfb533e --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT42Handler.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissProtocolConstants; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; +import org.openhab.core.types.PrimitiveType; + +/** + * The {@link SoulissT42Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissT42Handler extends SoulissGenericHandler { + byte t4nRawState = 0xF; + + public SoulissT42Handler(Thing thing) { + super(thing); + } + + // called on every status change or change request + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if ((channelUID.getAsString().split(":")[3].equals(SoulissBindingConstants.T4N_REARMALARM_CHANNEL)) + && (command instanceof OnOffType) && (command.equals(OnOffType.ON))) { + commandSEND(SoulissProtocolConstants.SOULISS_T4N_REARM); + this.setState(StringType.valueOf(SoulissBindingConstants.T4N_REARMOFF_MESSAGE_CHANNEL)); + } + } + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + + var configurationMap = getThing().getConfiguration(); + if (configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND) != null) { + bSecureSend = ((Boolean) configurationMap.get(SoulissBindingConstants.CONFIG_SECURE_SEND)).booleanValue(); + } + } + + public void setState(PrimitiveType state) { + if (state instanceof StringType) { + switch (String.valueOf(state)) { + case SoulissBindingConstants.T4N_ALARMON_MESSAGE_CHANNEL: + this.updateState(SoulissBindingConstants.T4N_STATUSALARM_CHANNEL, OnOffType.ON); + break; + case SoulissBindingConstants.T4N_ALARMOFF_MESSAGE_CHANNEL: + this.updateState(SoulissBindingConstants.T4N_STATUSALARM_CHANNEL, OnOffType.OFF); + break; + default: + break; + } + } + // Reset the rearm button. This is because if pressed, it does not turn off by itself + updateState(SoulissBindingConstants.T4N_REARMALARM_CHANNEL, OnOffType.OFF); + + super.setLastStatusStored(); + } + + @Override + public void setRawState(byte rawState) { + // update Last Status stored time + super.setLastStatusStored(); + // update item state only if it is different from previous + if (t4nRawState != rawState) { + OnOffType onOffVal = getOhStateOnOffFromSoulissVal(rawState); + if (onOffVal != null) { + this.setState(onOffVal); + } + } + t4nRawState = rawState; + } + + @Override + public byte getRawState() { + return t4nRawState; + } + + @Override + public byte getExpectedRawState(byte bCmd) { + if ((bSecureSend) && (bCmd == SoulissProtocolConstants.SOULISS_T4N_REARM)) { + return SoulissProtocolConstants.SOULISS_T4N_ANTITHEFT; + } + return -1; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT51Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT51Handler.java new file mode 100644 index 000000000..ae9b07bd4 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT51Handler.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT51Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT51Handler extends SoulissT5nHandler { + + public SoulissT51Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT52Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT52Handler.java new file mode 100644 index 000000000..3e3459bce --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT52Handler.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT52Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT52Handler extends SoulissT5nHandler { + + public SoulissT52Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT53Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT53Handler.java new file mode 100644 index 000000000..5caa02703 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT53Handler.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT53Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT53Handler extends SoulissT5nHandler { + + public SoulissT53Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT54Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT54Handler.java new file mode 100644 index 000000000..a60da6eeb --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT54Handler.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT54Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT54Handler extends SoulissT5nHandler { + + public SoulissT54Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT55Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT55Handler.java new file mode 100644 index 000000000..f38f04f3d --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT55Handler.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT55Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissT55Handler extends SoulissT5nHandler { + + public SoulissT55Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT56Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT56Handler.java new file mode 100644 index 000000000..c388e5ff3 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT56Handler.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT56Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissT56Handler extends SoulissT5nHandler { + + public SoulissT56Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT57Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT57Handler.java new file mode 100644 index 000000000..1fea7fc43 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT57Handler.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT57Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissT57Handler extends SoulissT5nHandler { + + public SoulissT57Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT58Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT58Handler.java new file mode 100644 index 000000000..c4a033256 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT58Handler.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT58Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ + +@NonNullByDefault +public class SoulissT58Handler extends SoulissT5nHandler { + + public SoulissT58Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT5nHandler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT5nHandler.java new file mode 100644 index 000000000..6565a8082 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT5nHandler.java @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; + +/** + * The {@link SoulissT5nHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT5nHandler extends SoulissGenericHandler { + + private float fVal = 0xF; + + public SoulissT5nHandler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + } + + public void setState(QuantityType state) { + this.updateState(SoulissBindingConstants.T5N_VALUE_CHANNEL, state); + } + + @Override + public void setRawState(byte rawState) { + throw new UnsupportedOperationException("Not Implemented, yet."); + } + + public void setFloatValue(float valueOf) { + super.setLastStatusStored(); + if (fVal != valueOf) { + this.setState(QuantityType.valueOf(Float.toString(valueOf))); + fVal = valueOf; + } + } + + @Override + public byte getRawState() { + throw new UnsupportedOperationException("Not Implemented, yet."); + } + + public float getFloatState() { + return fVal; + } + + @Override + public byte getExpectedRawState(byte bCommand) { + // Secure Send is disabled + return -1; + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + throw new UnsupportedOperationException("Unsupported operation. Read Only"); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT61Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT61Handler.java new file mode 100644 index 000000000..09e007966 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT61Handler.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT61Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Luca Remigio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT61Handler extends SoulissT6nHandler { + + // constructor + public SoulissT61Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT62Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT62Handler.java new file mode 100644 index 000000000..b98c74e4e --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT62Handler.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT62Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Luca Remigio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT62Handler extends SoulissT6nHandler { + + // constructor + public SoulissT62Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT63Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT63Handler.java new file mode 100644 index 000000000..249d6a15e --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT63Handler.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT63Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Luca Remigio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT63Handler extends SoulissT6nHandler { + + // constructor + public SoulissT63Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT64Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT64Handler.java new file mode 100644 index 000000000..e5992cd2b --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT64Handler.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT64Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Luca Remigio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT64Handler extends SoulissT6nHandler { + + // constructor + public SoulissT64Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT65Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT65Handler.java new file mode 100644 index 000000000..97ac80181 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT65Handler.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT65Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Luca Remigio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT65Handler extends SoulissT6nHandler { + + // constructor + public SoulissT65Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT66Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT66Handler.java new file mode 100644 index 000000000..6c8575db4 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT66Handler.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT66Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Luca Remigio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT66Handler extends SoulissT6nHandler { + + // constructor + public SoulissT66Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT67Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT67Handler.java new file mode 100644 index 000000000..719e00956 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT67Handler.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT67Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Luca Remigio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT67Handler extends SoulissT6nHandler { + + // constructor + public SoulissT67Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT68Handler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT68Handler.java new file mode 100644 index 000000000..40f4b76ac --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT68Handler.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link SoulissT68Handler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Luca Remigio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT68Handler extends SoulissT6nHandler { + + // constructor + public SoulissT68Handler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT6nHandler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT6nHandler.java new file mode 100644 index 000000000..db1a626c0 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT6nHandler.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.protocol.HalfFloatUtils; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; +import org.openhab.core.types.PrimitiveType; + +/** + * The {@link SoulissT6nHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Luca Remigio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissT6nHandler extends SoulissGenericHandler { + + private float fSetPointValue = 0xFFFF; + + public SoulissT6nHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof DecimalType) { + int uu = HalfFloatUtils.fromFloat(((DecimalType) command).floatValue()); + byte b2 = (byte) (uu >> 8); + byte b1 = (byte) uu; + // setpoint command + commandSEND(b1, b2); + } + } + + @Override + public void initialize() { + super.initialize(); + + updateStatus(ThingStatus.UNKNOWN); + } + + public void setState(PrimitiveType state) { + this.updateState(SoulissBindingConstants.T6N_VALUE_CHANNEL, (DecimalType) state); + } + + @Override + public void setRawState(byte rawState) { + throw new UnsupportedOperationException("Not Implemented, yet."); + } + + public void setFloatValue(float valueOf) { + super.setLastStatusStored(); + if (fSetPointValue != valueOf) { + this.setState(DecimalType.valueOf(Float.toString(valueOf))); + fSetPointValue = valueOf; + } + } + + @Override + public byte getRawState() { + throw new UnsupportedOperationException("Not Implemented, yet."); + } + + public float getFloatState() { + return fSetPointValue; + } + + @Override + public byte getExpectedRawState(byte bCommand) { + return -1; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissTopicsHandler.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissTopicsHandler.java new file mode 100644 index 000000000..4fc8a62cc --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissTopicsHandler.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.types.Command; +import org.openhab.core.types.PrimitiveType; + +/** + * The {@link SoulissTopicsHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Luca Remigio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SoulissTopicsHandler extends SoulissGenericActionMessage implements TypicalCommonMethods { + + private float fSetPointValue = 0xFFFF; + + public SoulissTopicsHandler(Thing pThing) { + super(pThing); + thingGenActMsg = pThing; + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + } + + @Override + public void initialize() { + // status online + updateStatus(ThingStatus.ONLINE); + } + + public void setState(PrimitiveType state) { + this.updateState(SoulissBindingConstants.T5N_VALUE_CHANNEL, (DecimalType) state); + } + + public void setFloatValue(float valueOf) { + this.updateState(SoulissBindingConstants.LASTSTATUSSTORED_CHANNEL, this.getLastUpdateTime()); + if (fSetPointValue != valueOf) { + this.setState(DecimalType.valueOf(Float.toString(valueOf))); + fSetPointValue = valueOf; + } + } + + public float getFloatState() { + return fSetPointValue; + } + + @Override + public void setRawState(byte rawState) { + throw new UnsupportedOperationException("Not Implemented, yet."); + } + + @Override + public byte getRawState() { + throw new UnsupportedOperationException("Not Implemented, yet."); + } + + @Override + public byte getExpectedRawState(byte bCommand) { + return -1; + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/TypicalCommonMethods.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/TypicalCommonMethods.java new file mode 100644 index 000000000..6e1883159 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/TypicalCommonMethods.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 +======= + * Copyright (c) 2014-2019 by the respective copyright holders. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.souliss.internal.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Result callback interface. + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public interface TypicalCommonMethods { + void setRawState(byte rawState); + + byte getRawState(); + + byte getExpectedRawState(byte bCommand); +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/CommonCommands.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/CommonCommands.java new file mode 100644 index 000000000..1832c3e1e --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/CommonCommands.java @@ -0,0 +1,499 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.protocol; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.channels.DatagramChannel; +import java.util.ArrayList; +import java.util.Enumeration; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.SoulissUDPConstants; +import org.openhab.binding.souliss.internal.config.GatewayConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class provide to construct MaCaco and UDP frame + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + * @author Alessandro Del Pex - Souliss App + */ +@NonNullByDefault +public class CommonCommands { + + private final Logger logger = LoggerFactory.getLogger(CommonCommands.class); + + private static final String LITERAL_SEND_FRAME = "sendFORCEFrame - {}, soulissNodeIPAddressOnLAN: {}"; + + public final void sendFORCEFrame(GatewayConfig gwConfig, int idNode, int slot, byte shortCommand) { + sendFORCEFrame(gwConfig, idNode, slot, shortCommand, null, null, null); + } + + /* + * used for set dimmer value. It set command at first byte and dimmerVal to + * second byte + */ + public final void sendFORCEFrame(GatewayConfig gwConfig, int idNode, int slot, byte shortCommand, byte lDimmer) { + sendFORCEFrame(gwConfig, idNode, slot, shortCommand, lDimmer, null, null); + } + + /* + * send force frame with command and RGB value + */ + public final void sendFORCEFrame(GatewayConfig gwConfig, int idNode, int slot, byte shortCommand, + @Nullable Byte byte1, @Nullable Byte byte2, @Nullable Byte byte3) { + ArrayList macacoFrame = new ArrayList<>(); + macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_FORCE); + + // PUTIN, STARTOFFEST, NUMBEROF + // PUTIN + macacoFrame.add((byte) 0x0); + // PUTIN + macacoFrame.add((byte) 0x0); + + macacoFrame.add((byte) (idNode));// Start Offset + + if (byte1 == null && byte2 == null && byte3 == null) { + // Number Of + macacoFrame.add((byte) ((byte) slot + 1)); + } else if (byte2 == null && byte3 == null) { + // Number Of byte of payload= command + set byte + macacoFrame.add((byte) ((byte) slot + 2)); + } else { + // Number Of byte of payload= OnOFF + Red + Green + Blu + macacoFrame.add((byte) ((byte) slot + 4)); + } + + for (var i = 0; i <= slot - 1; i++) { + // I set the bytes preceding the slot to be modified to zero + macacoFrame.add((byte) 00); + } + // PAYLOAD + macacoFrame.add(shortCommand); + + if (byte1 != null && byte2 != null && byte3 != null) { + // PAYLOAD RED + macacoFrame.add(byte1); + // PAYLOAD GREEN + macacoFrame.add(byte2); + // PAYLOAD BLUE + macacoFrame.add(byte3); + } else if (byte1 != null) { + // PAYLOAD DIMMER + macacoFrame.add(byte1); + } + + logger.debug(LITERAL_SEND_FRAME, macacoToString(macacoFrame), gwConfig); + queueToDispatcher(macacoFrame, gwConfig); + } + + /* + * T61 send frame to push the setpoint value + */ + + public final void sendFORCEFrameT61SetPoint(GatewayConfig gwConfig, int idNode, int slot, Byte byte1, Byte byte2) { + ArrayList macacoFrame = new ArrayList<>(); + macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_FORCE); + + // PUTIN, STARTOFFEST, NUMBEROF + // PUTIN + macacoFrame.add((byte) 0x00); + // PUTIN + macacoFrame.add((byte) 0x00); + // Start Offset + macacoFrame.add((byte) (idNode)); + // Number Of byte of payload= command + set byte + macacoFrame.add((byte) ((byte) slot + 2)); + + for (var i = 0; i <= slot - 1; i++) { + // I set the bytes preceding the slot to be modified to zero + macacoFrame.add((byte) 00); + } + // PAYLOAD + // first byte Setpoint Value + macacoFrame.add(byte1); + // second byte Setpoint Value + macacoFrame.add(byte2); + + logger.debug(LITERAL_SEND_FRAME, macacoToString(macacoFrame), gwConfig); + + queueToDispatcher(macacoFrame, gwConfig); + } + + /* + * T31 send force frame with command and setpoint float + */ + public final void sendFORCEFrameT31SetPoint(GatewayConfig gwConfig, int idNode, int slot, byte shortCommand, + Byte byte1, Byte byte2) { + ArrayList macacoFrame = new ArrayList<>(); + macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_FORCE); + + // PUTIN, STARTOFFEST, NUMBEROF + // PUTIN + macacoFrame.add((byte) 0x00); + // PUTIN + macacoFrame.add((byte) 0x00); + + // Start Offset + macacoFrame.add((byte) (idNode)); + // Number Of byte of payload= command + set byte + macacoFrame.add((byte) ((byte) slot + 5)); + + for (var i = 0; i <= slot - 1; i++) { + // prvious byte to zero + macacoFrame.add((byte) 00); + // slot to be changed + } + // PAYLOAD + macacoFrame.add(shortCommand); + + // Empty - Temperature Measured Value + macacoFrame.add((byte) 0x0); + // Empty - Temperature Measured Value + macacoFrame.add((byte) 0x0); + // Temperature Setpoint Value + macacoFrame.add(byte1); + // Temperature Setpoint Value + macacoFrame.add(byte2); + + logger.debug(LITERAL_SEND_FRAME, macacoToString(macacoFrame), gwConfig); + queueToDispatcher(macacoFrame, gwConfig); + } + + public final void sendDBStructFrame(GatewayConfig gwConfig) { + ArrayList macacoFrame = new ArrayList<>(); + macacoFrame.add((byte) SoulissUDPConstants.SOULISS_UDP_FUNCTION_DBSTRUCT_REQ); + // PUTIN + macacoFrame.add((byte) 0x0); + // PUTIN + macacoFrame.add((byte) 0x0); + // Start Offset + macacoFrame.add((byte) 0x0); + // Number Of + macacoFrame.add((byte) 0x0); + + logger.debug("sendDBStructFrame - {}, soulissNodeIPAddressOnLAN: {}", macacoToString(macacoFrame), gwConfig); + queueToDispatcher(macacoFrame, gwConfig); + } + + /* + * Queue command to Dispatcher (for securesend retransmission) + */ + private final void queueToDispatcher(ArrayList macacoFrame, GatewayConfig gwConfig) { + ArrayList buf = buildVNetFrame(macacoFrame, gwConfig.gatewayLanAddress, (byte) gwConfig.userIndex, + (byte) gwConfig.nodeIndex); + byte[] merd = toByteArray(buf); + + InetAddress serverAddr; + try { + serverAddr = gwConfig.gatewayWanAddress.isEmpty() ? InetAddress.getByName(gwConfig.gatewayLanAddress) + : InetAddress.getByName(gwConfig.gatewayWanAddress); + var packet = new DatagramPacket(merd, merd.length, serverAddr, + SoulissUDPConstants.SOULISS_GATEWAY_DEFAULT_PORT); + SendDispatcherRunnable.put(packet, logger); + } catch (IOException e) { + logger.warn("Error: {} ", e.getMessage()); + } + } + + /* + * send broadcast UDP frame - unused in this version + */ + private final void sendBroadcastNow(ArrayList macacoFrame) { + byte iUserIndex = (byte) 120; + byte iNodeIndex = (byte) 70; + + // Broadcast the message over all the network interfaces + Enumeration<@Nullable NetworkInterface> interfaces; + DatagramSocket sender = null; + try { + interfaces = NetworkInterface.getNetworkInterfaces(); + + while (interfaces.hasMoreElements()) { + var networkInterface = interfaces.nextElement(); + if (networkInterface != null) { + if (networkInterface.isLoopback() || !networkInterface.isUp()) { + continue; + } + for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) { + var broadcast = new InetAddress[3]; + broadcast[0] = InetAddress.getByName("224.0.0.1"); + broadcast[1] = InetAddress.getByName("255.255.255.255"); + broadcast[2] = interfaceAddress.getBroadcast(); + for (InetAddress bc : broadcast) { + // Send the broadcast package! + if (bc != null) { + try { + ArrayList buf = buildVNetFrame(macacoFrame, "255.255.255.255", iUserIndex, + iNodeIndex); + byte[] merd = toByteArray(buf); + var packet = new DatagramPacket(merd, merd.length, bc, + SoulissUDPConstants.SOULISS_GATEWAY_DEFAULT_PORT); + // Datagramsocket creation + var channel = DatagramChannel.open(); + sender = channel.socket(); + sender.setReuseAddress(true); + sender.setBroadcast(true); + + var sa = new InetSocketAddress(230); + sender.bind(sa); + + sender.send(packet); + logger.debug("Request packet sent to: {} Interface: {}", bc.getHostAddress(), + networkInterface.getDisplayName()); + + } catch (IOException e) { + logger.debug("IO error: {}", e.getMessage()); + } catch (Exception e) { + logger.debug("{}", e.getMessage(), e); + } finally { + if ((sender != null) && (!sender.isClosed())) { + sender.close(); + } + } + } + } + } + } + } + } catch (SocketException | UnknownHostException e) { + logger.warn("{}", e.getMessage()); + } + } + + /* + * Build VNet Frame + */ + private final ArrayList buildVNetFrame(ArrayList macacoFrame2, @Nullable String gatewayLanAddress, + byte iUserIndex, byte iNodeIndex) { + if (gatewayLanAddress != null) { + ArrayList frame = new ArrayList<>(); + InetAddress ip; + try { + ip = InetAddress.getByName(gatewayLanAddress); + } catch (UnknownHostException e) { + logger.warn("{}", e.getMessage()); + return frame; + } + byte[] dude = ip.getAddress(); + + // Port + frame.add((byte) 23); + // es 192.168.1.XX BOARD + frame.add((byte) (dude[3] & 0xFF)); + + // n broadcast : communication by Ip + // 255.255.255.255 to associate vNet 0xFFFF address. + frame.add(gatewayLanAddress.compareTo(SoulissUDPConstants.BROADCASTADDR) == 0 ? dude[2] : 0); + // NODE INDEX - source vNet address User Interface + frame.add(iNodeIndex); + // USER INDEX - source vNet address User Interface + frame.add(iUserIndex); + + // adds the calculation in the head + // Length + frame.add(0, (byte) (frame.size() + macacoFrame2.size() + 1)); + // Length Check 2 + frame.add(0, (byte) (frame.size() + macacoFrame2.size() + 1)); + + frame.addAll(macacoFrame2); + return frame; + } else { + throw new IllegalArgumentException("Cannot build VNet Frame . Null Souliss IP address"); + } + } + + /** + * Builds old-school byte array + * + * @param buf + * @return + */ + private final byte[] toByteArray(ArrayList buf) { + var merd = new byte[buf.size()]; + for (var i = 0; i < buf.size(); i++) { + merd[i] = buf.get(i); + } + return merd; + } + + /** + * Build MULTICAST FORCE Frame + */ + public final void sendMULTICASTFORCEFrame(GatewayConfig gwConfig, byte typical, byte shortCommand) { + ArrayList macacoFrame = new ArrayList<>(); + macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_FORCE_MASSIVE); + + // PUTIN, STARTOFFEST, NUMBEROF + // PUTIN + macacoFrame.add((byte) 0x0); + // PUTIN + macacoFrame.add((byte) 0x0); + // Start Offset + macacoFrame.add(typical); + // Number Of + macacoFrame.add((byte) 1); + // PAYLOAD + macacoFrame.add(shortCommand); + logger.debug("sendMULTICASTFORCEFrame - {}, soulissNodeIPAddressOnLAN: {}", macacoToString(macacoFrame), + gwConfig); + queueToDispatcher(macacoFrame, gwConfig); + } + + /** + * Build PING Frame + */ + public final void sendPing(@Nullable GatewayConfig gwConfig) { + if (gwConfig != null) { + ArrayList macacoFrame = new ArrayList<>(); + macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_PING_REQ); + + // PUTIN, STARTOFFEST, NUMBEROF + // PUTIN + macacoFrame.add((byte) 0x00); + // PUTIN + macacoFrame.add((byte) 0x00); + // Start Offset + macacoFrame.add((byte) 0x00); + // Number Of + macacoFrame.add((byte) 0x00); + logger.debug("sendPing - {}, IP: {} ", macacoToString(macacoFrame), gwConfig); + queueToDispatcher(macacoFrame, gwConfig); + } else { + logger.warn("Cannot send Souliss Ping - Ip null"); + } + } + + /** + * Build BROADCAST PING Frame + */ + public final void sendBroadcastGatewayDiscover() { + ArrayList macacoFrame = new ArrayList<>(); + macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_DISCOVER_GW_NODE_BCAST_REQ); + + // PUTIN, STARTOFFEST, NUMBEROF + // PUTIN + macacoFrame.add((byte) 0x05); + // PUTIN + macacoFrame.add((byte) 0x00); + // Start Offset + macacoFrame.add((byte) 0x00); + // Number Of + macacoFrame.add((byte) 0x00); + logger.debug("sendBroadcastPing - {} ", macacoToString(macacoFrame)); + sendBroadcastNow(macacoFrame); + } + + /** + * Build SUBSCRIPTION Frame + */ + public final void sendSUBSCRIPTIONframe(GatewayConfig gwConfig, int iNodes) { + ArrayList macacoFrame = new ArrayList<>(); + macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_SUBSCRIBE_REQ); + + // PUTIN, STARTOFFEST, NUMBEROF + // PUTIN + macacoFrame.add((byte) 0x00); + // PUTIN + macacoFrame.add((byte) 0x00); + macacoFrame.add((byte) 0x00); + + macacoFrame.add((byte) iNodes); + logger.debug("sendSUBSCRIPTIONframe - {}, IP: {} ", macacoToString(macacoFrame), gwConfig); + queueToDispatcher(macacoFrame, gwConfig); + } + + /** + * Build HEALTHY REQUEST Frame + */ + public final void sendHealthyRequestFrame(GatewayConfig gwConfig, int iNodes) { + ArrayList macacoFrame = new ArrayList<>(); + macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_HEALTHY_REQ); + + // PUTIN, STARTOFFSET, NUMBEROF + // PUTIN + macacoFrame.add((byte) 0x00); + // PUTIN + macacoFrame.add((byte) 0x00); + macacoFrame.add((byte) 0x00); + macacoFrame.add((byte) iNodes); + logger.debug("sendHealthyRequestFrame - {}, IP: {} ", macacoToString(macacoFrame), gwConfig); + queueToDispatcher(macacoFrame, gwConfig); + } + + /** + * Build TYPICAL REQUEST Frame + */ + public final void sendTypicalRequestFrame(GatewayConfig gwConfig, int nodes) { + ArrayList macacoFrame = new ArrayList<>(); + macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_TYP_REQ); + // PUTIN, STARTOFFEST, NUMBEROF + // PUTIN + macacoFrame.add((byte) 0x00); + // PUTIN + macacoFrame.add((byte) 0x00); + // startOffset + macacoFrame.add((byte) 0x00); + // iNodes + macacoFrame.add((byte) nodes); + logger.debug("sendTypicalRequestFrame - {}, IP: {} ", macacoToString(macacoFrame), gwConfig.gatewayLanAddress); + queueToDispatcher(macacoFrame, gwConfig); + } + + /** + * Build TYPICAL REQUEST Frame with start offset + */ + public final void sendTypicalRequestFrame(GatewayConfig gwConfig, int start, int nodes) { + ArrayList macacoFrame = new ArrayList<>(); + macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_TYP_REQ); + // PUTIN, STARTOFFEST, NUMBEROF + // PUTIN + macacoFrame.add((byte) 0x00); + // PUTIN + macacoFrame.add((byte) 0x00); + // startOffset + macacoFrame.add((byte) start); + // iNodes + macacoFrame.add((byte) nodes); + logger.debug("sendTypicalRequestFrame - {}, IP: {} ", macacoToString(macacoFrame), gwConfig.gatewayLanAddress); + queueToDispatcher(macacoFrame, gwConfig); + } + + boolean flag = true; + + private final String macacoToString(ArrayList mACACOframe) { + // I copy arrays to avoid concurrent changes + ArrayList mACACOframe2 = new ArrayList<>(); + mACACOframe2.addAll(mACACOframe); + flag = false; + var sb = new StringBuilder(); + sb.append("HEX: ["); + for (byte b : mACACOframe2) { + sb.append(String.format("%02X ", b)); + } + sb.append("]"); + flag = true; + return sb.toString(); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/HalfFloatUtils.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/HalfFloatUtils.java new file mode 100644 index 000000000..aef293a19 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/HalfFloatUtils.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.protocol; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Helper class to conver half precision float to int int are used on analogue + * typicals (2 bytes) and should be reversed because of endianess + * http://stackoverflow.com/users/237321/x4u + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public final class HalfFloatUtils { + + public static boolean isNaN(float x) { + return x != x; + } + + // ignores the higher 16 bits + public static float toFloat(int hbits) { + // 10 bits mantissa + int mant = hbits & 0x03ff; + // 5 bits exponent + int exp = hbits & 0x7c00; + if (exp == 0x7c00) { + // -> NaN/Inf + exp = 0x3fc00; + // normalized value + } else if (exp != 0) { + // exp - 15 + 127 + exp += 0x1c000; + if (mant == 0 && exp > 0x1c400) { + return Float.intBitsToFloat((hbits & 0x8000) << 16 | exp << 13 | 0x3ff); + } + // && exp==0 -> subnormal + } else if (mant != 0) { + // make it normal + exp = 0x1c400; + do { + // mantissa * 2 + mant <<= 1; + // decrease exp by 1 + exp -= 0x400; + // while not normal + } while ((mant & 0x400) == 0); + // discard subnormal bit + mant &= 0x3ff; + // else +/-0 -> +/-0 + } + // combine all parts + return Float.intBitsToFloat( + // sign << ( 31 - 15 ) + (hbits & 0x8000) << 16 + // value << ( 23 - 10 ) + | (exp | mant) << 13); + } + + // returns all higher 16 bits as 0 for all results + public static int fromFloat(float fval) { + var fbits = Float.floatToIntBits(fval); + // sign only + int sign = fbits >>> 16 & 0x8000; + // rounded value + int val = (fbits & 0x7fffffff) + 0x1000; + + // might be or become NaN/Inf + if (val >= 0x47800000) + // avoid Inf due to rounding + { + // is or must become + // NaN/Inf + if ((fbits & 0x7fffffff) >= 0x47800000) { + if (val < 0x7f800000) { + // make it +/-Inf + return sign | 0x7c00; + } + // remains +/-Inf or NaN + return sign | 0x7c00 | + // keep NaN (and Inf) bits + (fbits & 0x007fffff) >>> 13; + } + // unrounded not quite Inf + return sign | 0x7bff; + } + if (val >= 0x38800000) { + // exp - 127 + 15 + return sign | val - 0x38000000 >>> 13; + } + if (val < 0x33000000) { + // becomes +/-0 + return sign; + } + // tmp exp for subnormal calc + val = (fbits & 0x7fffffff) >>> 23; + // add subnormal bit + return sign | ((fbits & 0x7fffff | 0x800000) + // round depending on cut off + + (0x800000 >>> val - 102) + // div by 2^(1-(exp-127+15)) and >> 13 | exp=0 + >>> 126 - val); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/PacketStruct.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/PacketStruct.java new file mode 100644 index 000000000..80a2f581f --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/PacketStruct.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.protocol; + +import java.net.DatagramPacket; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Data Structure for class SendDispatcherThread + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class PacketStruct { + private DatagramPacket packet; + + public DatagramPacket getPacket() { + return packet; + } + + private boolean sent = false; + private long time = 0; + + public PacketStruct(DatagramPacket packetPar) { + packet = packetPar; + } + + public long getTime() { + return time; + } + + public boolean getSent() { + return sent; + } + + public void setSent(boolean sent) { + this.sent = sent; + } + + public void setTime(long time) { + // set the time only if it has not already been set once + if (this.time == 0) { + this.time = time; + } + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/SendDispatcherRunnable.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/SendDispatcherRunnable.java new file mode 100644 index 000000000..8bc083c90 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/SendDispatcherRunnable.java @@ -0,0 +1,442 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.protocol; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetSocketAddress; +import java.nio.channels.DatagramChannel; +import java.util.ArrayList; +import java.util.Iterator; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissUDPConstants; +import org.openhab.binding.souliss.internal.handler.SoulissGatewayHandler; +import org.openhab.binding.souliss.internal.handler.SoulissGenericHandler; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class provide to take packet, and send it to regular interval to Souliss + * Network + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + */ +@NonNullByDefault +public class SendDispatcherRunnable implements Runnable { + + private final Logger logger = LoggerFactory.getLogger(SendDispatcherRunnable.class); + + private @Nullable SoulissGatewayHandler gwHandler; + static boolean bPopSuspend = false; + protected static ArrayList packetsList = new ArrayList<>(); + private long startTime = System.currentTimeMillis(); + static int iDelay = 0; // equal to 0 if array is empty + static int sendMinDelay = 0; + + public SendDispatcherRunnable(Bridge bridge) { + this.gwHandler = (SoulissGatewayHandler) bridge.getHandler(); + } + + /** + * Put packet to send in ArrayList PacketList + */ + public static synchronized void put(DatagramPacket packetToPUT, Logger logger) { + bPopSuspend = true; + var bPacchettoGestito = false; + // I extract the node addressed by the incoming packet. returns -1 if the package is not of the + // SOULISS_UDP_FUNCTION_FORCE type + int node = getNode(packetToPUT); + if (node >= 0) { + logger.debug("Push packet in queue - Node {}", node); + } + + if (packetsList.isEmpty() || node < 0) { + bPacchettoGestito = false; + } else { + // OPTIMIZER + // scan packets list to sent + for (var i = 0; i < packetsList.size(); i++) { + if (node >= 0 && getNode(packetsList.get(i).getPacket()) == node && !packetsList.get(i).getSent()) { + // frame for the same node already present in the list + logger.debug("Frame UPD per nodo {} già presente in coda. Esecuzione ottimizzazione.", node); + bPacchettoGestito = true; + // if the packet to be inserted is shorter (or equal) than the one in the queue + // then I overwrite the bytes of the packet present in the queue + if (packetToPUT.getData().length <= packetsList.get(i).getPacket().getData().length) { + // it scrolls the command bytes and if the byte is non-zero overwrites the byte present in the + // queued packet + logger.trace("Optimizer. Packet to push: {}", macacoToString(packetToPUT.getData())); + logger.trace("Optimizer. Previous frame: {}", + macacoToString(packetsList.get(i).getPacket().getData())); + // typical values ​​start from byte 12 onwards + for (var j = 12; j < packetToPUT.getData().length; j++) { + // if the j-th byte is different from zero then + // I overwrite it with the byte of the packet already present + if (packetToPUT.getData()[j] != 0) { + packetsList.get(i).getPacket().getData()[j] = packetToPUT.getData()[j]; + } + } + logger.debug("Optimizer. Previous frame modified to: {}", + macacoToString(packetsList.get(i).getPacket().getData())); + } else { + // if the packet to be inserted is longer than the one in the list then + // I overwrite the bytes of the packet to be inserted, then I delete the one in the list + // and insert the new one + if (packetToPUT.getData().length > packetsList.get(i).getPacket().getData().length) { + for (var j = 12; j < packetsList.get(i).getPacket().getData().length; j++) { + // if the j-th byte is different from zero then I overwrite it with the byte of the + // packet already present + if ((packetsList.get(i).getPacket().getData()[j] != 0) + && (packetToPUT.getData()[j] == 0)) { + // overwrite the bytes of the last frame + // only if the byte equals zero. + // If the last frame is nonzero + // takes precedence and must override + packetToPUT.getData()[j] = packetsList.get(i).getPacket().getData()[j]; + + } + } + // removes the packet + logger.debug("Optimizer. Remove frame: {}", + macacoToString(packetsList.get(i).getPacket().getData())); + packetsList.remove(i); + // inserts the new + logger.debug("Optimizer. Add frame: {}", macacoToString(packetToPUT.getData())); + packetsList.add(new PacketStruct(packetToPUT)); + } + } + } + } + } + + if (!bPacchettoGestito) { + logger.debug("Add packet: {}", macacoToString(packetToPUT.getData())); + packetsList.add(new PacketStruct(packetToPUT)); + } + bPopSuspend = false; + } + + @Override + public void run() { + DatagramSocket sender = null; + + try (var channel = DatagramChannel.open();) { + if (checkTime()) { + PacketStruct sp = pop(); + if (sp != null) { + logger.debug( + "SendDispatcherJob - Functional Code 0x{} - Packet: {} - Elementi rimanenti in lista: {}", + Integer.toHexString(sp.getPacket().getData()[7]), macacoToString(sp.getPacket().getData()), + packetsList.size()); + + sender = channel.socket(); + sender.setReuseAddress(true); + sender.setBroadcast(true); + + var localGwHandler = this.gwHandler; + if (localGwHandler != null) { + var sa = new InetSocketAddress(localGwHandler.getGwConfig().preferredLocalPortNumber); + sender.bind(sa); + sender.send(sp.getPacket()); + } + } + + // compare the states in memory with the frames sent. + // If match deletes the frame from the sent list + safeSendCheck(); + + resetTime(); + } + } catch (Exception e) { + logger.warn("{}", e.getMessage()); + } finally { + if (sender != null && !sender.isClosed()) { + sender.close(); + } + } + } + + /** + * Get node number from packet + */ + private static int getNode(DatagramPacket packet) { + // 7 is the byte of the VNet frame at which I find the command code + // 10 is the byte of the VNet frame at which I find the node ID + if (packet.getData()[7] == SoulissUDPConstants.SOULISS_UDP_FUNCTION_FORCE) { + return packet.getData()[10]; + } + return -1; + } + + private static String macacoToString(byte[] frame2) { + byte[] frame = frame2.clone(); + var sb = new StringBuilder(); + sb.append("HEX: ["); + for (byte b : frame) { + sb.append(String.format("%02X ", b)); + } + sb.append("]"); + return sb.toString(); + } + + /** + * check frame updates with packetList, where flag "sent" is true. If all + * commands was executed there delete packet in list. + */ + public void safeSendCheck() { + int node; + int iSlot; + SoulissGenericHandler localTyp; + var sCmd = ""; + byte bExpected; + + var sExpected = ""; + + // scan of the sent packets list + for (var i = 0; i < packetsList.size(); i++) { + + if (packetsList.get(i).getSent()) { + node = getNode(packetsList.get(i).getPacket()); + iSlot = 0; + for (var j = 12; j < packetsList.get(i).getPacket().getData().length; j++) { + // I check the slot only if the command is different from ZERO + if ((packetsList.get(i).getPacket().getData()[j] != 0) && (this.gwHandler != null)) { + localTyp = getHandler(node, iSlot, this.logger); + + if (localTyp != null) { + bExpected = localTyp.getExpectedRawState(packetsList.get(i).getPacket().getData()[j]); + + // if the expected value of the typical is -1 then it means that the typical does not + // support the + // function + // secureSend + if (bExpected < 0) { + localTyp = null; + } + + // translate the command sent with the expected state e + // then compare with the current state + if (logger.isDebugEnabled() && localTyp != null) { + sCmd = Integer.toHexString(packetsList.get(i).getPacket().getData()[j]); + // command sent + sCmd = sCmd.length() < 2 ? "0x0" + sCmd.toUpperCase() : "0x" + sCmd.toUpperCase(); + sExpected = Integer.toHexString(bExpected); + sExpected = sExpected.length() < 2 ? "0x0" + sExpected.toUpperCase() + : "0x" + sExpected.toUpperCase(); + logger.debug( + "Compare. Node: {} Slot: {} Node Name: {} Command: {} Expected Souliss State: {} - Actual OH item State: {}", + node, iSlot, localTyp.getLabel(), sCmd, sExpected, localTyp.getRawState()); + } + + if (localTyp != null && checkExpectedState(localTyp.getRawState(), bExpected)) { + // if the value of the typical matches the value + // transmitted then I set the byte to zero. + // when all bytes are equal to zero then + // delete the frame + packetsList.get(i).getPacket().getData()[j] = 0; + logger.debug("{} Node: {} Slot: {} - OK Expected State", localTyp.getLabel(), node, + iSlot); + } else if (localTyp == null) { + if (bExpected < 0) { + // if the typical is not managed then I set the byte of the relative slot to zero + packetsList.get(i).getPacket().getData()[j] = 0; + } else { + // if there is no typical at slot j then it means that it is one + // slot + // connected + // to the previous one (ex: RGB, T31, ...) + // then if slot j-1 = 0 then j can also be set to 0 + if (packetsList.get(i).getPacket().getData()[j - 1] == 0) { + packetsList.get(i).getPacket().getData()[j] = 0; + } + } + + } + } + } + iSlot++; + } + + // if the value of all bytes that make up the packet is 0 then I remove the packet from + // list + // also if the timout has elapsed then I set the packet to be resent + if (checkAllsSlotZero(packetsList.get(i).getPacket())) { + logger.debug("Command packet executed - Removed"); + packetsList.remove(i); + } else { + // if the frame is not equal to zero I check the TIMEOUT and if + // it has expired so I set the SENT flag to false + long time = System.currentTimeMillis(); + + SoulissGatewayHandler localGwHandler = this.gwHandler; + if (localGwHandler != null) { + if ((localGwHandler.getGwConfig().timeoutToRequeue < time - packetsList.get(i).getTime()) + && (localGwHandler.getGwConfig().timeoutToRemovePacket < time + - packetsList.get(i).getTime())) { + logger.debug("Packet Execution timeout - Removed"); + packetsList.remove(i); + } else { + logger.debug("Packet Execution timeout - Requeued"); + packetsList.get(i).setSent(false); + } + + } + } + } + } + } + + private @Nullable SoulissGenericHandler getHandler(int node, int slot, Logger logger) { + SoulissGatewayHandler localGwHandler = this.gwHandler; + + Iterator thingsIterator; + if (localGwHandler != null) { + thingsIterator = localGwHandler.getThing().getThings().iterator(); + Thing typ = null; + while (thingsIterator.hasNext()) { + typ = thingsIterator.next(); + if (typ.getThingTypeUID().equals(SoulissBindingConstants.TOPICS_THING_TYPE)) { + continue; + } + SoulissGenericHandler handler = (SoulissGenericHandler) typ.getHandler(); + + // execute it only if binding is Souliss and update is for my + // Gateway + if ((handler != null) && (handler.getNode() == node && handler.getSlot() == slot)) { + return handler; + } + } + } + return null; + } + + private static boolean checkExpectedState(byte itemState, byte expectedState) { + // if expected state is null than return true. The frame will not requeued + if (expectedState <= -1) { + return true; + } + return itemState == expectedState; + } + + private static boolean checkAllsSlotZero(DatagramPacket packet) { + var bflag = true; + for (var j = 12; j < packet.getData().length; j++) { + if ((packet.getData()[j] != 0)) { + bflag = false; + } + } + return bflag; + } + + long t = 0; + long tPrec = 0; + + /** + * Pop SocketAndPacket from ArrayList PacketList + */ + @Nullable + private synchronized PacketStruct pop() { + synchronized (this) { + SoulissGatewayHandler localGwHandler = this.gwHandler; + + // don't pop if bPopSuspend = true + // bPopSuspend is set by the put method + if ((localGwHandler != null) && (!bPopSuspend)) { + t = System.currentTimeMillis(); + + // brings the interval to the minimum only if: + // the length of the tail less than or equal to 1; + // if the SEND_DELAY time has elapsed. + + if (packetsList.size() <= 1) { + iDelay = sendMinDelay; + } else { + iDelay = localGwHandler.getGwConfig().sendInterval; + + } + + var iPacket = 0; + var bFlagWhile = true; + // discard packages already sent + while ((iPacket < packetsList.size()) && bFlagWhile) { + if (packetsList.get(iPacket).getSent()) { + iPacket++; + } else { + bFlagWhile = false; + } + } + + boolean tFlag = (t - tPrec) >= localGwHandler.getGwConfig().sendInterval; + + // if we have reached the end of the list and then all + // packets have already been sent so I also place the tFlag + // to false (as if the timeout hasn't elapsed yet) + if (iPacket >= packetsList.size()) { + tFlag = false; + } + + if ((!packetsList.isEmpty()) && tFlag) { + tPrec = System.currentTimeMillis(); + + // extract the first element of the list + PacketStruct sp = packetsList.get(iPacket); + + // PACKAGE MANAGEMENT: deleted from the list or + // marked as sent if it is a FORCE + if (packetsList.get(iPacket).getPacket() + .getData()[7] == SoulissUDPConstants.SOULISS_UDP_FUNCTION_FORCE) { + // flag sent set to true + packetsList.get(iPacket).setSent(true); + // set time + packetsList.get(iPacket).setTime(System.currentTimeMillis()); + } else { + packetsList.remove(iPacket); + } + + logger.debug("POP: {} packets in memory", packetsList.size()); + if (logger.isDebugEnabled()) { + var iPacketSentCounter = 0; + var i = 0; + while ((i < packetsList.size())) { + if (packetsList.get(i).getSent()) { + iPacketSentCounter++; + } + i++; + } + logger.debug("POP: {} force frame sent", iPacketSentCounter); + } + + logger.debug("Pop frame {} - Delay for 'SendDispatcherThread' setted to {} mills.", + macacoToString(sp.getPacket().getData()), iDelay); + return sp; + } + } + + } + return null; + } + + private void resetTime() { + startTime = System.currentTimeMillis(); + } + + private boolean checkTime() { + return startTime < (System.currentTimeMillis() - iDelay); + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/UDPDecoder.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/UDPDecoder.java new file mode 100644 index 000000000..bfa1ae8f1 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/UDPDecoder.java @@ -0,0 +1,586 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.protocol; + +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.SoulissBindingConstants; +import org.openhab.binding.souliss.internal.SoulissProtocolConstants; +import org.openhab.binding.souliss.internal.SoulissUDPConstants; +import org.openhab.binding.souliss.internal.discovery.DiscoverResult; +import org.openhab.binding.souliss.internal.handler.SoulissGatewayHandler; +import org.openhab.binding.souliss.internal.handler.SoulissGenericHandler; +import org.openhab.binding.souliss.internal.handler.SoulissT11Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT12Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT13Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT14Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT16Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT18Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT19Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT1AHandler; +import org.openhab.binding.souliss.internal.handler.SoulissT22Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT31Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT41Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT42Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT5nHandler; +import org.openhab.binding.souliss.internal.handler.SoulissT61Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT62Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT63Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT64Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT65Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT66Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT67Handler; +import org.openhab.binding.souliss.internal.handler.SoulissT68Handler; +import org.openhab.binding.souliss.internal.handler.SoulissTopicsHandler; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.binding.ThingHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class decodes incoming Souliss packets, starting from decodevNet + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + * @author Alessandro Del Pex - Souliss App + */ +@NonNullByDefault +public class UDPDecoder { + + private final Logger logger = LoggerFactory.getLogger(UDPDecoder.class); + private @Nullable DiscoverResult discoverResult; + private @Nullable SoulissGatewayHandler gwHandler; + + private @Nullable Byte lastByteGatewayIp = null; + + public UDPDecoder(Bridge bridge, @Nullable DiscoverResult pDiscoverResult) { + this.gwHandler = (SoulissGatewayHandler) bridge.getHandler(); + this.discoverResult = pDiscoverResult; + var localGwHandler = this.gwHandler; + if (localGwHandler != null) { + this.lastByteGatewayIp = (byte) Integer + .parseInt(localGwHandler.getGwConfig().gatewayLanAddress.split("\\.")[3]); + } + } + + /** + * Get packet from VNET Frame + * + * @param packet + * incoming datagram + */ + public void decodeVNetDatagram(DatagramPacket packet) { + int checklen = packet.getLength(); + ArrayList mac = new ArrayList<>(); + for (var ig = 7; ig < checklen; ig++) { + mac.add((byte) (packet.getData()[ig] & 0xFF)); + } + + // Check if decoded Gw equal to ip of bridge handler or 1 (action messages) + Byte gwCheck = (byte) (packet.getData()[5] & 0xFF); + if ((gwCheck == 1) || (gwCheck.equals(this.lastByteGatewayIp))) { + decodeMacaco((byte) (packet.getData()[5] & 0xFF), mac); + } + } + + /** + * Decodes lower level MaCaCo packet + * + * @param lastByteGatewayIP + * + * @param macacoPck + */ + private void decodeMacaco(byte lastByteGatewayIP, ArrayList macacoPck) { + int functionalCode = macacoPck.get(0); + switch (functionalCode) { + case SoulissUDPConstants.SOULISS_UDP_FUNCTION_PING_RESP: + logger.debug("Received functional code: 0x{}- Ping answer", Integer.toHexString(functionalCode)); + decodePing(macacoPck); + break; + + case SoulissUDPConstants.SOULISS_UDP_FUNCTION_DISCOVER_GW_NODE_BCAST_RESP: + logger.debug("Received functional code: 0x{} - Discover a gateway node answer (broadcast)", + Integer.toHexString(functionalCode)); + try { + decodePingBroadcast(macacoPck); + } catch (UnknownHostException e) { + logger.warn("Error: {}", e.getLocalizedMessage()); + } + break; + + case SoulissUDPConstants.SOULISS_UDP_FUNCTION_POLL_RESP: + logger.debug("Received functional code: 0x{} - subscribe response", + Integer.toHexString(functionalCode)); + decodeStateRequest(macacoPck); + break; + case SoulissUDPConstants.SOULISS_UDP_FUNCTION_SUBSCRIBE_RESP: + logger.debug("Received functional code: 0x{} - Read state answer", Integer.toHexString(functionalCode)); + decodeStateRequest(macacoPck); + break; + + // Answer for assigned typical logic + case SoulissUDPConstants.SOULISS_UDP_FUNCTION_TYP_RESP: + logger.debug("Received functional code: 0x{}- Read typical logic answer", + Integer.toHexString(functionalCode)); + decodeTypRequest(lastByteGatewayIP, macacoPck); + break; + // Answer + case SoulissUDPConstants.SOULISS_UDP_FUNCTION_HEALTHY_RESP: + // nodes healthy + logger.debug("Received functional code: 0x{} - Nodes Healthy", Integer.toHexString(functionalCode)); + decodeHealthyRequest(macacoPck); + break; + + case (byte) SoulissUDPConstants.SOULISS_UDP_FUNCTION_DBSTRUCT_RESP: + logger.debug("Received functional code: 0x{} - Database structure answer", + Integer.toHexString(functionalCode)); + decodeDBStructRequest(macacoPck); + break; + case 0x83: + logger.debug("Functional code not supported"); + break; + case 0x84: + logger.debug("Data out of range"); + break; + case 0x85: + logger.debug("Subscription refused"); + break; + case (byte) SoulissUDPConstants.SOULISS_UDP_FUNCTION_ACTION_MESSAGE: + logger.debug("Received functional code: 0x{} - Action Message (Topic)", + Integer.toHexString(functionalCode)); + decodeActionMessages(macacoPck); + break; + default: + logger.debug("Received functional code: 0x{} - unused by OH Binding", + Integer.toHexString(functionalCode)); + } + } + + /** + * @param mac + */ + private void decodePing(ArrayList mac) { + // not used + int putIn1 = mac.get(1); + // not used + int putIn2 = mac.get(2); + logger.debug("decodePing: putIn code: {}, {}", putIn1, putIn2); + var localGwHandler = this.gwHandler; + if (localGwHandler != null) { + localGwHandler.gatewayDetected(); + } + } + + private void decodePingBroadcast(ArrayList macaco) throws UnknownHostException { + String ip = macaco.get(5) + "." + macaco.get(6) + "." + macaco.get(7) + "." + macaco.get(8); + byte[] addr = { (macaco.get(5)).byteValue(), (macaco.get(6)).byteValue(), (macaco.get(7)).byteValue(), + (macaco.get(8)).byteValue() }; + logger.debug("decodePingBroadcast. Gateway Discovery. IP: {}", ip); + + var localDiscoverResult = this.discoverResult; + if (localDiscoverResult != null) { + localDiscoverResult.gatewayDetected(InetAddress.getByAddress(addr), macaco.get(8).toString()); + } else { + logger.debug("decodePingBroadcast aborted. 'discoverResult' is null"); + } + } + + /** + * decode Typicals Request Packet + * It read Souliss Network and create OH items + * + * @param lastByteGatewayIP + * + * @param mac + */ + private void decodeTypRequest(byte lastByteGatewayIP, ArrayList mac) { + var localGwHandler = this.gwHandler; + if (localGwHandler != null) { + int typXnodo = localGwHandler.getMaxTypicalXnode(); + + byte tgtnode = mac.get(3); + int numberOf = mac.get(4); + + // creates Souliss nodes + for (var j = 0; j < numberOf; j++) { + // create only not-empty typicals + if ((mac.get(5 + j) != 0) && (mac.get(5 + j) != SoulissProtocolConstants.SOULISS_T_RELATED)) { + byte typical = mac.get(5 + j); + byte slot = (byte) (j % typXnodo); + byte node = (byte) (j / typXnodo + tgtnode); + logger.debug("Thing Detected. IP (last byte): {}, Typical: 0x{}, Node: {}, Slot: {} ", + lastByteGatewayIP, Integer.toHexString(typical), node, slot); + + var localDiscoverResult = this.discoverResult; + if (localDiscoverResult != null) { + localDiscoverResult.thingDetectedTypicals(lastByteGatewayIP, typical, node, slot); + } else { + logger.debug("decodeTypRequest aborted. 'discoverResult' is null"); + } + } + } + } + } + + /** + * decode Typicals Request Packet + * It read Action Messages on Souliss Network and create items + * + * @param lastByteGatewayIP + * + * @param mac + */ + private void decodeActionMessages(ArrayList mac) { + String sTopicNumber; + String sTopicVariant; + float fRet = 0; + + try { + // A 16-bit Topic Number: Define the topic itself + // A 8-bit Topic Variant : Define a variant for the topic + + String[] sTopicNumberArray = { Integer.toHexString(mac.get(2)).toUpperCase(), + Integer.toHexString(mac.get(1)).toUpperCase() }; + if (sTopicNumberArray[0].length() == 1) { + sTopicNumberArray[0] = "0" + sTopicNumberArray[0]; + } + if (sTopicNumberArray[1].length() == 1) { + sTopicNumberArray[1] = "0" + sTopicNumberArray[1]; + } + sTopicNumber = sTopicNumberArray[0] + sTopicNumberArray[1]; + logger.debug("Topic Number: 0x{}", sTopicNumber); + + sTopicVariant = Integer.toHexString(mac.get(3)).toUpperCase(); + if (sTopicVariant.length() == 1) { + sTopicVariant = "0" + sTopicVariant; + } + logger.debug("Topic Variant: 0x{}", sTopicVariant); + if (mac.get(4) == 1) { + fRet = mac.get(5); + logger.debug("Topic Value (Payload one byte): {} ", Integer.toHexString(mac.get(5)).toUpperCase()); + } else if (mac.get(4) == 2) { + byte[] value = { mac.get(5), mac.get(6) }; + + int shifted = value[1] << 8; + fRet = HalfFloatUtils.toFloat(shifted + value[0]); + logger.debug("Topic Value (Payload 2 bytes): {}", fRet); + } + var localGwHandler = this.gwHandler; + if (localGwHandler != null) { + var listThings = localGwHandler.getThing().getThings(); + + Boolean bIsPresent = false; + + for (Thing t : listThings) { + if (t.getUID().toString().split(":")[2] + .equals(sTopicNumber + SoulissBindingConstants.UUID_NODE_SLOT_SEPARATOR + sTopicVariant)) { + var topicHandler = (SoulissTopicsHandler) (t.getHandler()); + if (topicHandler != null) { + topicHandler.setState(DecimalType.valueOf(Float.toString(fRet))); + bIsPresent = true; + } + } + } + var localDiscoverResult = this.discoverResult; + if (localDiscoverResult != null && !bIsPresent) { + localDiscoverResult.thingDetectedActionMessages(sTopicNumber, sTopicVariant); + } + } + } catch (Exception uy) { + logger.warn("decodeActionMessages ERROR"); + } + } + + /** + * decode DB Struct Request Packet + * It return Souliss Network: + * node number + * max supported number of nodes + * max typical per node + * max requests + * See Souliss wiki for details + * + * @param lastByteGatewayIP + * + * @param mac + */ + private void decodeDBStructRequest(ArrayList mac) { + int nodes = mac.get(5); + int maxTypicalXnode = mac.get(7); + + SoulissGatewayHandler localGwHandler = this.gwHandler; + if (localGwHandler != null) { + localGwHandler.setNodes(nodes); + localGwHandler.setMaxTypicalXnode(maxTypicalXnode); + localGwHandler.dbStructAnswerReceived(); + } + } + + /** + * Decodes a souliss nodes health request + * + * @param macaco + * packet + */ + private void decodeHealthyRequest(ArrayList mac) { + int numberOf = mac.get(4); + + for (var i = 5; i < 5 + numberOf; i++) { + var localGwHandler = this.gwHandler; + if (localGwHandler != null) { + // build an array containing healths + List listaThings = localGwHandler.getThing().getThings(); + + ThingHandler handler = null; + for (Thing thing : listaThings) { + if (thing.getThingTypeUID().equals(SoulissBindingConstants.TOPICS_THING_TYPE)) { + continue; + } + handler = thing.getHandler(); + if (handler != null) { + int tgtnode = i - 5; + if (((SoulissGenericHandler) handler).getNode() == tgtnode) { + ((SoulissGenericHandler) handler).setHealthy((mac.get(i))); + } + } else { + logger.debug("decode Healthy Request Warning. Thing handler is null"); + } + } + } + } + } + + private void decodeStateRequest(ArrayList mac) { + int tgtnode = mac.get(3); + + Iterator thingsIterator = null; + var localGwHandler = this.gwHandler; + if (localGwHandler != null) { + thingsIterator = localGwHandler.getThing().getThings().iterator(); + + var bFound = false; + Thing typ = null; + while (thingsIterator.hasNext() && !bFound) { + typ = thingsIterator.next(); + // if a topic continue + // ignoring it + if (typ.getThingTypeUID().equals(SoulissBindingConstants.TOPICS_THING_TYPE)) { + continue; + } + String[] sUIDArray = typ.getUID().getAsString().split(":"); + ThingHandler handler = typ.getHandler(); + // execute it only if binding is Souliss and update is for my + // Gateway + if (handler != null) { + // execute it + // only + // if it is + // node + // to update + if (((SoulissGenericHandler) handler).getNode() == tgtnode) { + // ...now check slot + int slot = ((SoulissGenericHandler) handler).getSlot(); + // get typical value + var sVal = getByteAtSlot(mac, slot); + var decodingLiteralLabel = "Decoding {}{}"; + var packetLabel = " packet"; + // update Txx + switch (sUIDArray[1]) { + case SoulissBindingConstants.T11: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T11, packetLabel); + ((SoulissT11Handler) handler).setRawState(sVal); + break; + case SoulissBindingConstants.T12: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T12, packetLabel); + ((SoulissT12Handler) handler).setRawState(sVal); + break; + case SoulissBindingConstants.T13: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T13, packetLabel); + ((SoulissT13Handler) handler).setRawState(sVal); + break; + case SoulissBindingConstants.T14: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T14, packetLabel); + ((SoulissT14Handler) handler).setRawState(sVal); + break; + case SoulissBindingConstants.T16: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T16, packetLabel); + ((SoulissT16Handler) handler).setRawStateCommand(sVal); + ((SoulissT16Handler) handler).setRawStateRgb(getByteAtSlot(mac, slot + 1), + getByteAtSlot(mac, slot + 2), getByteAtSlot(mac, slot + 3)); + break; + + case SoulissBindingConstants.T18: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T18, packetLabel); + ((SoulissT18Handler) handler).setRawState(sVal); + break; + + case SoulissBindingConstants.T19: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T19, packetLabel); + ((SoulissT19Handler) handler).setRawState(sVal); + ((SoulissT19Handler) handler).setRawStateDimmerValue(getByteAtSlot(mac, slot + 1)); + break; + + case SoulissBindingConstants.T1A: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T1A, packetLabel); + ((SoulissT1AHandler) handler).setRawState(sVal); + break; + case SoulissBindingConstants.T21: + case SoulissBindingConstants.T22: + logger.debug(decodingLiteralLabel, + SoulissBindingConstants.T21 + "/" + SoulissBindingConstants.T22, packetLabel); + ((SoulissT22Handler) handler).setRawState(sVal); + break; + case SoulissBindingConstants.T31: + logger.debug("Decoding {}/{}", SoulissBindingConstants.T31, + SoulissBindingConstants.T31); + logger.debug("packet: "); + logger.debug("- bit0 (system on-off): {}", getBitState(sVal, 0)); + logger.debug("- bit1 (heating on-off): {}", getBitState(sVal, 1)); + logger.debug("- bit2 (cooling on-off): {}", getBitState(sVal, 2)); + logger.debug("- bit3 (fan1 on-off): {}", getBitState(sVal, 3)); + logger.debug("- bit4 (fan2 on-off): {}", getBitState(sVal, 4)); + logger.debug("- bit5 (fan3 on-off): {}", getBitState(sVal, 5)); + logger.debug("- bit6 (Manual/automatic fan mode): {}", getBitState(sVal, 6)); + logger.debug("- bit7 (heating/cooling mode): {}", getBitState(sVal, 7)); + + ((SoulissT31Handler) handler).setRawStateValues(sVal, getFloatAtSlot(mac, slot + 1), + getFloatAtSlot(mac, slot + 3)); + + break; + case SoulissBindingConstants.T41: + ((SoulissT41Handler) handler).setRawState(sVal); + break; + case SoulissBindingConstants.T42: + ((SoulissT42Handler) handler).setRawState(sVal); + switch (sVal) { + case SoulissProtocolConstants.SOULISS_T4N_NO_ANTITHEFT: + ((SoulissT42Handler) handler).setState(StringType + .valueOf(SoulissBindingConstants.T4N_ALARMOFF_MESSAGE_CHANNEL)); + break; + case SoulissProtocolConstants.SOULISS_T4N_ALARM: + ((SoulissT42Handler) handler).setState(StringType + .valueOf(SoulissBindingConstants.T4N_ALARMON_MESSAGE_CHANNEL)); + break; + default: + break; + } + break; + case SoulissBindingConstants.T51: + case SoulissBindingConstants.T52: + case SoulissBindingConstants.T53: + case SoulissBindingConstants.T54: + case SoulissBindingConstants.T55: + case SoulissBindingConstants.T56: + case SoulissBindingConstants.T57: + case SoulissBindingConstants.T58: + logger.debug("Decoding T5n packet"); + if (!Float.isNaN(getFloatAtSlot(mac, slot))) { + ((SoulissT5nHandler) handler).setFloatValue(getFloatAtSlot(mac, slot)); + } + break; + case SoulissBindingConstants.T61: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T61, packetLabel); + if (!Float.isNaN(getFloatAtSlot(mac, slot))) { + ((SoulissT61Handler) handler).setFloatValue(getFloatAtSlot(mac, slot)); + } + break; + case SoulissBindingConstants.T62: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T62, packetLabel); + if (!Float.isNaN(getFloatAtSlot(mac, slot))) { + ((SoulissT62Handler) handler).setFloatValue(getFloatAtSlot(mac, slot)); + } + break; + case SoulissBindingConstants.T63: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T63, packetLabel); + if (!Float.isNaN(getFloatAtSlot(mac, slot))) { + ((SoulissT63Handler) handler).setFloatValue(getFloatAtSlot(mac, slot)); + } + break; + case SoulissBindingConstants.T64: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T64, packetLabel); + if (!Float.isNaN(getFloatAtSlot(mac, slot))) { + ((SoulissT64Handler) handler).setFloatValue(getFloatAtSlot(mac, slot)); + } + break; + case SoulissBindingConstants.T65: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T65, packetLabel); + if (!Float.isNaN(getFloatAtSlot(mac, slot))) { + ((SoulissT65Handler) handler).setFloatValue(getFloatAtSlot(mac, slot)); + } + break; + case SoulissBindingConstants.T66: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T66, packetLabel); + if (!Float.isNaN(getFloatAtSlot(mac, slot))) { + ((SoulissT66Handler) handler).setFloatValue(getFloatAtSlot(mac, slot)); + } + break; + case SoulissBindingConstants.T67: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T67, packetLabel); + if (!Float.isNaN(getFloatAtSlot(mac, slot))) { + ((SoulissT67Handler) handler).setFloatValue(getFloatAtSlot(mac, slot)); + } + break; + case SoulissBindingConstants.T68: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.T68, packetLabel); + if (!Float.isNaN(getFloatAtSlot(mac, slot))) { + ((SoulissT68Handler) handler).setFloatValue(getFloatAtSlot(mac, slot)); + } + break; + + case SoulissBindingConstants.TOPICS: + logger.debug(decodingLiteralLabel, SoulissBindingConstants.TOPICS, packetLabel); + if (!Float.isNaN(getFloatAtSlot(mac, slot))) { + ((SoulissTopicsHandler) handler).setFloatValue(getFloatAtSlot(mac, slot)); + } + break; + default: + logger.debug("Unsupported typical"); + } + } + } + } + } + } + + private byte getByteAtSlot(ArrayList mac, int slot) { + return mac.get(5 + slot); + } + + private float getFloatAtSlot(ArrayList mac, int slot) { + int iOutput = mac.get(5 + slot) & 0xFF; + int iOutput2 = mac.get(5 + slot + 1) & 0xFF; + // we have two bytes, convert them... + int shifted = iOutput2 << 8; + return HalfFloatUtils.toFloat(shifted + iOutput); + } + + public byte getBitState(byte vRaw, int iBit) { + final var maskBit1 = 0x1; + + if (((vRaw >>> iBit) & maskBit1) == 0) { + return 0; + } else { + return 1; + } + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/UDPListenDiscoverRunnable.java b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/UDPListenDiscoverRunnable.java new file mode 100644 index 000000000..4c585f10c --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/UDPListenDiscoverRunnable.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.souliss.internal.protocol; + +import java.net.BindException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetSocketAddress; +import java.net.SocketTimeoutException; +import java.nio.channels.DatagramChannel; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.souliss.internal.discovery.DiscoverResult; +import org.openhab.binding.souliss.internal.handler.SoulissGatewayHandler; +import org.openhab.core.thing.Bridge; +import org.openhab.core.util.HexUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class provide receive packet from network + * + * @author Tonino Fazio - Initial contribution + * @author Luca Calcaterra - Refactor for OH3 + * @author Alessandro Del Pex - Souliss App + */ +@NonNullByDefault +public class UDPListenDiscoverRunnable implements Runnable { + + protected boolean bExit = false; + private @Nullable UDPDecoder decoder = null; + + private final Logger logger = LoggerFactory.getLogger(UDPListenDiscoverRunnable.class); + + private @Nullable SoulissGatewayHandler gwHandler; + + public UDPListenDiscoverRunnable(Bridge bridge, @Nullable DiscoverResult pDiscoverResult) { + this.gwHandler = (SoulissGatewayHandler) bridge.getHandler(); + decoder = new UDPDecoder(bridge, pDiscoverResult); + } + + @Override + public void run() { + DatagramSocket socket = null; + + while (!Thread.currentThread().isInterrupted()) { + try { + // open socket for listening... + var channel = DatagramChannel.open(); + socket = channel.socket(); + + socket.setReuseAddress(true); + socket.setBroadcast(true); + + var localGwHandler = this.gwHandler; + if (localGwHandler != null) { + var sa = new InetSocketAddress(localGwHandler.getGwConfig().preferredLocalPortNumber); + socket.bind(sa); + + var buf = new byte[200]; + // receive request + final var packet = new DatagramPacket(buf, buf.length); + socket.setSoTimeout(60000); + socket.receive(packet); + buf = packet.getData(); + + // **************** DECODER ******************** + logger.debug("Packet received (port {}) {}", socket.getLocalPort(), HexUtils.bytesToHex(buf)); + + var localDecoder = this.decoder; + if (localDecoder != null) { + localDecoder.decodeVNetDatagram(packet); + } + } + + } catch (BindException e) { + logger.warn("UDP Port busy, Souliss already listening? {} ", e.getLocalizedMessage()); + try { + if (socket != null && !socket.isClosed()) { + socket.close(); + } + } catch (Exception e1) { + logger.warn("UDP socket close failed: {} ", e1.getLocalizedMessage()); + } + } catch (SocketTimeoutException e2) { + logger.warn("UDP SocketTimeoutException close: {}", e2.getLocalizedMessage()); + if (socket != null && !socket.isClosed()) { + socket.close(); + } + } catch (Exception ee) { + logger.warn("Exception receiving-decoding message: {} ", ee.getLocalizedMessage()); + if (socket != null && !socket.isClosed()) { + socket.close(); + } + } finally { + var localGwHandler = this.gwHandler; + if (socket != null && !socket.isClosed()) { + socket.close(); + } else if ((socket == null) && (localGwHandler != null)) { + localGwHandler.setBridgeStatus(false); + } + } + } + } +} diff --git a/bundles/org.openhab.binding.souliss/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.souliss/src/main/resources/OH-INF/binding/binding.xml new file mode 100644 index 000000000..f092bbc64 --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/resources/OH-INF/binding/binding.xml @@ -0,0 +1,9 @@ + + + + Souliss Binding + This is the binding for Souliss. The Arduino based SmartHome.. + + diff --git a/bundles/org.openhab.binding.souliss/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.souliss/src/main/resources/OH-INF/thing/thing-types.xml new file mode 100644 index 000000000..66a4e92bd --- /dev/null +++ b/bundles/org.openhab.binding.souliss/src/main/resources/OH-INF/thing/thing-types.xml @@ -0,0 +1,1286 @@ + + + + + + + Represents a Souliss Gateway. + + gatewayLanAddress + + + + + LAN Ip address (mandatory) + + true + + + + WAN Hostname or Ip in case of external network access + + true + + + + Default is 230 UDP. + + true + 230 + true + + + + Default port is 23000 + + 23000 + true + true + + + + + Interval in seconds to check for device presence. + 30 + true + true + + + + Interval in seconds to subscribe Souliss Gateway. + + 30 + true + true + + + + Interval in seconds to send nodes healthy. + + 60 + true + true + + + + + Interval in milliseconds to get packet from binding queue and send it to Souliss. First packet is sent + immediately. + + 30 + true + true + + + + + Interval in milliseconds to requeue packet in queue if not yet executed + true + 5000 + true + + + + + Interval in milliseconds to remove packet from queue if not yet executed + true + 20000 + true + + + + + User Index + 70 + true + + + + Node Index + 120 + true + + + + + + + + + + Simple Light + + + + + + + + + + + + Node + true + + + + Slot + true + + + + Set sleep timer in cycles (conf.parameter: "sleep") + false + 5 + + + + (conf.parameter: "secureSend") + false + true + + + + + + + + + + Simple Light with Auto Mode + + + + + + + + + + Node + true + + + + Slot + true + + + + Set sleep timer in cycles + false + 5 + + + + + false + true + + + + + + + + + + Digital Input + + + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Pulse Digital Output + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + RGB LED Strip + + + + + + + + + + + + + + + + Node + true + + + + Slot + true + + + + Set sleep timer in cycles + false + 5 + + + + + + false + true + + + + + + + + + + Simple Light with feedback + + + + + + + + + + Node + true + + + + Slot + true + + + + Set sleep timer in cycles + false + 5 + + + + + + false + true + + + + + + + + + + Single Color LED Strip + + + + + + + + + + + + + + Node + true + + + + Slot + true + + + + Set sleep timer in cycles + false + 5 + + + + + + + + + + Digital input pass through + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Motorized devices with limit switches + + + + + + + + + + Node + true + + + + Slot + true + + + + + false + true + + + + + + + + + + Motorized devices with limit switches and middle position + + + + + + + + + + Node + true + + + + Slot + true + + + + + false + true + + + + + + + + + + Temperature control with cooling and heating mode + + + + + + + + + + + + + + + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Anti-theft integration (Main) + + + + + + + + + + + + + + + + + + Node + true + + + + Slot + true + + + + + false + true + + + + + + + + + + Anti-theft integration (Peer) + + + + + + + + + + + + + + + + + Node + true + + + + Slot + true + + + + + false + true + + + + + + + + + + + Floating Point Input + + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Temperature measure (-20, +50) °C + + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Humidity measure (0, 100) % + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Light Sensor (0, 40) kLux + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Voltage (0, 400) V + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Current (0, 25) A + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Power (0, 6500) W + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Pressure measure (0, 1500) hPa + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Analog setpoint + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Temperature measure (-20, +50) °C + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Humidity measure (0, 100) % + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Light Sensor (0, 40) kLux + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Voltage (0, 400) V + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Current (0, 25) A + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Power (0, 6500) W + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Pressure measure (0, 1500) hPa + + + + + + + + + Node + true + + + + Slot + true + + + + + + + + + + Look at: Souliss Wiki, Peer 2 Peer Communication. These are messages published in broadcast from souliss + nodes in channels defined by two value: number and variant. + + + + + number + + + + Topic Number + true + + + + Topic Variant + true + + + + + + + + String + + + + + + + + + + + + + + String + + + + + + + + + + + trigger + + Button to trigger something + Switch + + + + trigger + + Button to trigger something + Switch + + + + + trigger + + Set + Switch + + + + Switch + + Switch on/off + + + + Switch + + The output will be timed for nCYCLES of the Node associated timer + + + + Switch + + Switch on/off + + + + Contact + + Light on/off + + + + + + Contact + + Contact Open/Closed + + + + + RollerShutter + + Rollershutter Up/Down + Blinds + + + + + String + + State of rollershutter/windows + + + + + + + + + + + + + + + + DateTime + + Last Message emitted by the module + QualityOfService + + + + + DateTime + + Last Status Store + QualityOfService + + + + + Number + + Souliss Healthy + + QualityOfService + + + + + + Number + + Floating Point Input + + sensors + + + + + + Number + trigger + + Floating Point Input + Temperature + + + + + Number + + Current humidity in % + Temperature + + sensors + + + + + + + Number:Temperature + + Current temperature + Temperature + + sensors + + + + + + Number:Temperature + + Setpoint temperature + Temperature + + sensors + + + + + + Number + + Lux + + sensors + + + + + + Number + + Current Voltage + Energy + + sensors + + + + + Number + + Current Ampere + Energy + + sensors + + + + + + Number + + Current Power + Energy + + sensors + + + + + + Number + + Pressure + + sensors + + + + + + Color + + Color of the LED. Bound to a Dimmer to just set the brightness, bind to a Color chooser for the full + control and bind to a Switch for turning the led on or off. + ColorLight + + + + Dimmer + + The brightness can be set in 16 steps for RGBW/White leds and in 64 steps for RGBWW leds + DimmableLight + + + + + Switch + + Switch lamp to white mode + DimmableLight + + + + RollerShutter + + Brightness Up/Down + DimmableLight + + + diff --git a/bundles/pom.xml b/bundles/pom.xml index 2ac8d20ae..83ed47c63 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -317,6 +317,7 @@ org.openhab.binding.sonos org.openhab.binding.sonyaudio org.openhab.binding.sonyprojector + org.openhab.binding.souliss org.openhab.binding.spotify org.openhab.binding.squeezebox org.openhab.binding.surepetcare