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