]> git.basschouten.com Git - openhab-addons.git/commitdiff
[souliss] Souliss Binding initial contribution (#11083)
authorLuca Calcaterra <calcaterra.luca@gmail.com>
Sun, 17 Oct 2021 11:31:36 +0000 (13:31 +0200)
committerGitHub <noreply@github.com>
Sun, 17 Oct 2021 11:31:36 +0000 (13:31 +0200)
* Initial Contribution

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
Co-Authored-By: Tonino Fazio <fazioa@gmail.com>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update pom.xml

spotless-apply

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
Co-Authored-By: Tonino Fazio <fazioa@gmail.com>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Renamed healty in healthy (simple word error)

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update thing-types.xml

onOff to CamelCase
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* camelCase fixed on some types

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/README.md

Co-authored-by: Matthew Skinner <matt@pcmus.com>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/README.md

Co-authored-by: Matthew Skinner <matt@pcmus.com>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update README.md

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update README.md

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update README.md

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update README.md

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fixed camelCase on thhings parameters

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update thing-types.xml

fixed label cases
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update thing-types.xml

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Revert "Update thing-types.xml"

This reverts commit 5c19fbc69dee53f41d56a847bc82660192e0158c.

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Initial Contribution

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
Co-Authored-By: Tonino Fazio <fazioa@gmail.com>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Fix some errors (Nullable issues) and pom.xml format

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update pom.xml

spotless-apply

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
Co-Authored-By: Tonino Fazio <fazioa@gmail.com>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissT11Handler.java

Default case on switch (handlecommand )

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* added secoresend to t31 skeleton and variable smessage fix declaration

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Initial Contribution

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
Co-Authored-By: Tonino Fazio <fazioa@gmail.com>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update pom.xml

spotless-apply

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
Co-Authored-By: Tonino Fazio <fazioa@gmail.com>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissGatewayHandler.java

Cutted comments and uneccessary log on gw status

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Renamed healty in healthy (simple word error)

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fixed camelCase on thhings parameters

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update thing-types.xml

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* cleanup and quality code fixes

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissBindingUDPDecoder.java

fixed some npe

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* npe check fix

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissCommonCommands.java

Remove comments unused code

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissCommonCommands.java

commented out code unused

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissCommonCommands.java

remove unused code commented out
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissBindingUDPServerJob.java

remove unused code comments
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissBindingSendDispatcherJob.java

fixed logger trace and remove unused code comments
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/README.md

Co-authored-by: Matthew Skinner <matt@pcmus.com>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* removed completely unnecessary comments

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fixed some thing types cases

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissGatewayHandler.java

removed unnecessary log line
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissGatewayHandler.java

removed unnecessary comment
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* loggers as final !

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissGatewayJobHealthy.java

removed unnecessary logs

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissGatewayJobPing.java

removed unnecessary logs
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fixed redundancy on types checks

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update README.md

fixed examples parms
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissGatewayHandler.java

changed parm var name bridge
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/handler/SoulissT11Handler.java

remove comments

Co-authored-by: Matthew Skinner <matt@pcmus.com>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/handler/SoulissT12Handler.java

comments removed

Co-authored-by: Matthew Skinner <matt@pcmus.com>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/handler/SoulissT13Handler.java

comments removed

Co-authored-by: Matthew Skinner <matt@pcmus.com>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fixed thing type on README and some case on xml thing types

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update README.md

cutted part about manually thing config specs on README . Who use oh already knows it .
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Code cleanup and optimizations based on @Skinah tips

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fixes from @Skinah suggestions

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>

Fixed typeid's on costants files

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* various optimizations follow @Skinah tips (thanks!)

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* spotless:apply fix

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fixes constants and channel id types of t31

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Fix various NPE warnings

Fixes many ... Some added suppresswarnings.
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissGenericActionMessage.java

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Refactor handlers into souliss.internal.handler

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* spoless:apply fix

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* spotless:apply

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fix t19 (securesend parm ,labels and setvalue)

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update thing-types.xml

spotless:apply fix

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/README.md

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/README.md

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/README.md

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/README.md

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/README.md

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* spotless:apply fix

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Null checks Warnings removed

(mitigated with local copy of field)  - removed comments

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* syntax sugar fixed of consts and section removed from README

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* moved files to internal package

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissHandlerFactory.java

check types on object and not on strings

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* various fixes based on @fwolter

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Gateway ip address regex on config param

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* changed description of gateway ip on param

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* formatted tables in README.md

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* removed comments

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* wip for npe checks - breaking functionality :-(

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* spotless fix Author: Luca Calcaterra <calcaterra.luca@gmail.com>

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fix contrib header in some files

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* reworked udp - only one bridge allowed and  UDP  listen only to bridge port 230

...passing datagramsocket  with soulissnetworkparameter class

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* quite ok. Where to close socket in case of thread interruption ?

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* wip

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* wip2

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* wip3 udp receive but not on vpn

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Classes names Refactor

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* wip

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* wip

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* replaced datagramsocket with nio socket- seems to be ok

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update UDPListenDiscoverRunnable.java

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Fixed some null checks removed securesend option (only t11 end similar)

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* dummy initial value of raw values - removed thread on decoder line

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fix t19 ex catch and broadcast function

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Reworked Config of Gateway as Class (todo check nulls...)

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Added representation property on gw - some cleanups

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissGatewayHandler.java

cleanup unused vars

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* refactor methods of gw parameters

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* WIP to remove NetworkParameter class

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* wip refactor DiscoverResult

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* wip remove networkparameters

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* WIP remove NetworkParameter Class. Things online only on a health message

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* WIP Fix Topics

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* WIP2 Fix topics

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update pom.xml

upgrade  binding version to 3.2 snapshot
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* topics bound to bridge - seems to be ok.

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* remove NetworkParameters class - topics ok but need parse rework

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* code cleanup and bugs check fix

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Add node and slot to property - other fixes

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* change default interval subscription -cleanup constants

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* WIP WAN Address gw

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* WAN option external network. Fixed Putin on commands - to test well

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* cleanup - safesend check WIP

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fixed secursend for t11-18

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* WIP Fix null checks

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissGenericHandler.java

fixed wrong assignment of prop
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* null checks fixes for code quality

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fixed listen port according to gw parm (default 23000)

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* nuances

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update thing-types.xml

changed default values
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* CommonCommands to static - other fixes .

seems quite ok, remain T31 to fix (securesend)

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update UDPListenDiscoverRunnable.java

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissGatewayHandler.java

changed executor imp for udp
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* restored commoncommands non static

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SendDispatcherRunnable.java

safesendcheck fix (not really)
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* various nuances (sonarlint)

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update README.md

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/README.md

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/README.md

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update README.md

removed explain how oh works on example
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/README.md

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update bundles/org.openhab.binding.souliss/src/main/feature/feature.xml

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* @fwolter various fixes

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissT11Handler.java

wrong header

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissT11Handler.java

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fix headers descriptions before authors on all classes

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* all var on begin of classes and @nullable sugar syntax

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* removed @nullable on local vars

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* wip comments translations and cleanup

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* cleanup comments and translated them (if italian occourred)

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* @fwolter suggestions for approval...various fixes

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* various fixes based on @fwolter suggestions.

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* various fixes based on @fwolter suggestions.

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissGatewayHandler.java

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update thing-types.xml

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update UDPListenDiscoverRunnable.java

hexutils usage
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissT22Handler.java

removed unused method
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissT22Handler.java

fix previous commit
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissT11Handler.java

@Nullable on configuration
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* method to put bridge offline (network exception on listener)

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update thing-types.xml

changed some channels to trigger type
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* WIP broken

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update thing-types.xml

spotless:apply
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* various changes

call super on initialize() of all handlers. Changed to QuantityType where appliable
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissGatewayDiscovery.java

added uniqueId (ip of gateway+node+slot) as representationProperty
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* wip for load discovery component

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* big fixes and code quality improvments

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* fixes

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update SoulissGenericHandler.java

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
* Update README.md

wrong format ..

Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
Co-authored-by: Tonino Fazio <fazioa@gmail.com>
Co-authored-by: Matthew Skinner <matt@pcmus.com>
Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
62 files changed:
CODEOWNERS
bom/openhab-addons/pom.xml
bundles/org.openhab.binding.souliss/NOTICE [new file with mode: 0644]
bundles/org.openhab.binding.souliss/README.md [new file with mode: 0644]
bundles/org.openhab.binding.souliss/pom.xml [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/feature/feature.xml [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissBindingConstants.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissDatagramSocketFactory.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissHandlerFactory.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissProtocolConstants.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/SoulissUDPConstants.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/config/GatewayConfig.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/discovery/DiscoverResult.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/discovery/SoulissDiscoverJob.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/discovery/SoulissGatewayDiscovery.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayJobHealthy.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayJobPing.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGatewayJobSubscription.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGenericActionMessage.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissGenericHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT11Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT12Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT13Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT14Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT16Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT18Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT19Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT1AHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT22Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT31Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT41Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT42Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT51Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT52Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT53Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT54Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT55Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT56Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT57Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT58Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT5nHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT61Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT62Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT63Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT64Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT65Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT66Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT67Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT68Handler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissT6nHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/SoulissTopicsHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/handler/TypicalCommonMethods.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/CommonCommands.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/HalfFloatUtils.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/PacketStruct.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/SendDispatcherRunnable.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/UDPDecoder.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/java/org/openhab/binding/souliss/internal/protocol/UDPListenDiscoverRunnable.java [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/resources/OH-INF/binding/binding.xml [new file with mode: 0644]
bundles/org.openhab.binding.souliss/src/main/resources/OH-INF/thing/thing-types.xml [new file with mode: 0644]
bundles/pom.xml

index d1e56d4ea753885ec4c4f245737eb7780425adf3..7a0fd608b6919f94a7653fc2d2543a07f0b6861a 100644 (file)
 /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
index 9e4a4b9d41cba2eb07a2fb3834d66bff17d955ec..4353c4027c1396930859b51a8fd771c4510f6f27 100644 (file)
       <artifactId>org.openhab.binding.sonyprojector</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.openhab.addons.bundles</groupId>
+      <artifactId>org.openhab.binding.souliss</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.openhab.addons.bundles</groupId>
       <artifactId>org.openhab.binding.spotify</artifactId>
diff --git a/bundles/org.openhab.binding.souliss/NOTICE b/bundles/org.openhab.binding.souliss/NOTICE
new file mode 100644 (file)
index 0000000..38d625e
--- /dev/null
@@ -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 (file)
index 0000000..c9de7ca
--- /dev/null
@@ -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"        <house>
+
+Group    FamilyRoom           "Soggiorno"     <parents_2_4>   (Home)
+Group    Divano               "Divano"        (Home)
+Group    Outside              "Esterno"   <garden>   (Home)
+Group    TV                   "TV"   <television>   (Home)
+Group    Elettricita
+Group    Diagnostic
+Group    TermostatoSoggiorno
+
+Switch   tettoia  "Tettoia"  <light>    (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]"     <keyring> (Outside, Diagnostic)  {channel="souliss:t31:105:5-0:lastStatusStored"}
+
+Switch   portoncino         "Portoncino"          <light>         (FamilyRoom)         ["Lighting"]   {autoupdate="false",channel="souliss:t14:105:1-6:pulse"}
+Switch   cancello         "Cancello"          <light>         (FamilyRoom)         ["Lighting"]   {autoupdate="false",channel="souliss:t14:105:1-7:pulse"}
+
+Number   FamilyRoom_Temperature   "Temperatura [%.1f Â°C]"   <temperature>  (FamilyRoom)                  {channel="souliss:t31:105:6-0:measured"}
+Number   FamilyRoom_Humidity      "Umidità [%.1f %%]"       <humidity>      (FamilyRoom)                     {channel="souliss:t53:105:6-7:value"}
+String AggiornamentoNodo6      "Agg [%1$td.%1$tm.%1$tY %1$tk:%1$tM:%1$tS]"     <keyring> (FamilyRoom, Diagnostic)  {channel="souliss:t31:105:6-0:lastStatusStored"}
+
+Number   Consumo      "Consumo [%.1f W]"       <energy>      (FamilyRoom, Elettricita)                     {channel="souliss:t57:105:1-4:value"}
+Number   Fotovoltaico      "Fotovoltaico [%.1f W]"       <energy>      (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]"     <keyring> (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]"        <keyring> (FamilyRoom, Elettricita, Diagnostic)  {channel="souliss:t57:105:4-0:lastStatusStored"}
+                                  
+Switch divano "Divano" <light> (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]"    <keyring> (FamilyRoom, Divano, Diagnostic)  {channel="souliss:t57:105:12-0:lastStatusStored"}
+String divano_healthy  "Salute"        <keyring> (FamilyRoom, Divano, Diagnostic)  {channel="souliss:t57:105:12-0:healthy"}
+
+Number termostatosoggiorno_temperatura  "Temperatura [%.1f Â°C]" <temperature> (TermostatoSoggiorno) {channel="souliss:t31:105:6-0:measured"}
+Number termostatosoggiorno_umidita "Umidità [%.1f %%]" <temperature>   (TermostatoSoggiorno)       {channel="souliss:t53:105:6-7:value" }
+
+Number termostatosoggiorno_umidita "Umidità" <humidity>   (TermostatoSoggiorno)  {channel="souliss:t53:105:6-7:value" }
+
+Number termostatosoggiorno_temperatura  "Temperatura" <temperature> (TermostatoSoggiorno) {channel="souliss:t31:105:6-0:measured"}
+Number termostatosoggiorno_setpoint "Regola Set Point [%.1f Â°c]"    <heating> (TermostatoSoggiorno) {autoupdate="false", channel="souliss:t31:105:6-0:sePpoint"}
+Switch termostatosoggiorno_setasmeasured "Set temp. attuale" <heating> (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" <powerIcon> (TermostatoSoggiorno) {channel="souliss:t31:105:6-0:system"}
+Switch termostatosoggiorno_fire "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]" <keyring> (TermostatoSoggiorno, Diagnostic)  {channel="souliss:t31:105:6-0:lastStatusStored"}
+Number TermostatoSoggiorno_healthy "Salute" <keyring> (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 (file)
index 0000000..621e9aa
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.openhab.addons.bundles</groupId>
+    <artifactId>org.openhab.addons.reactor.bundles</artifactId>
+    <version>3.2.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>org.openhab.binding.souliss</artifactId>
+
+  <name>openHAB Add-ons :: Bundles :: Souliss Binding</name>
+
+</project>
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 (file)
index 0000000..b064c66
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<features name="org.openhab.binding.souliss-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
+       <repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
+
+       <feature name="openhab-binding-souliss" description="Souliss Binding" version="${project.version}">
+               <feature>openhab-runtime-base</feature>
+               <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.souliss/${project.version}</bundle>
+       </feature>
+</features>
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 (file)
index 0000000..a9b37f4
--- /dev/null
@@ -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<ThingTypeUID> 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 (file)
index 0000000..ee618c1
--- /dev/null
@@ -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 (file)
index 0000000..df1ad5e
--- /dev/null
@@ -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 (file)
index 0000000..4e05968
--- /dev/null
@@ -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 (file)
index 0000000..9d74280
--- /dev/null
@@ -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 (file)
index 0000000..1a900f6
--- /dev/null
@@ -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 (file)
index 0000000..2bcaca8
--- /dev/null
@@ -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 (file)
index 0000000..abace17
--- /dev/null
@@ -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 (file)
index 0000000..29c63ac
--- /dev/null
@@ -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<String, Object> 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 (file)
index 0000000..bb6eaa5
--- /dev/null
@@ -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<Class<? extends ThingHandlerService>> 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 (file)
index 0000000..3207231
--- /dev/null
@@ -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 (file)
index 0000000..5b431ba
--- /dev/null
@@ -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 (file)
index 0000000..9ccbeb3
--- /dev/null
@@ -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 (file)
index 0000000..a434ab1
--- /dev/null
@@ -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 (file)
index 0000000..f6c32d9
--- /dev/null
@@ -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 (file)
index 0000000..e0c8583
--- /dev/null
@@ -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 (file)
index 0000000..43bf6b5
--- /dev/null
@@ -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 (file)
index 0000000..34786d1
--- /dev/null
@@ -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 (file)
index 0000000..4556c6c
--- /dev/null
@@ -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 (file)
index 0000000..71e427e
--- /dev/null
@@ -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 (file)
index 0000000..8ba705a
--- /dev/null
@@ -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 (file)
index 0000000..651550a
--- /dev/null
@@ -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 (file)
index 0000000..be83d07
--- /dev/null
@@ -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 (file)
index 0000000..7b22b03
--- /dev/null
@@ -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 (file)
index 0000000..e938c62
--- /dev/null
@@ -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<Temperature> setMeasuredValue = new QuantityType<>("0");
+    QuantityType<Temperature> 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<Temperature> valueOf) {
+        if ((valueOf instanceof QuantityType<?>) && (!setMeasuredValue.equals(valueOf))) {
+            this.updateState(SoulissBindingConstants.T31_VALUE_CHANNEL, valueOf);
+            setMeasuredValue = valueOf;
+        }
+    }
+
+    public void setSetpointValue(QuantityType<Temperature> 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 (file)
index 0000000..401b8ef
--- /dev/null
@@ -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 (file)
index 0000000..cbcfb53
--- /dev/null
@@ -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 (file)
index 0000000..ae9b07b
--- /dev/null
@@ -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 (file)
index 0000000..3e3459b
--- /dev/null
@@ -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 (file)
index 0000000..5caa027
--- /dev/null
@@ -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 (file)
index 0000000..a60da6e
--- /dev/null
@@ -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 (file)
index 0000000..f38f04f
--- /dev/null
@@ -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 (file)
index 0000000..c388e5f
--- /dev/null
@@ -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 (file)
index 0000000..1fea7fc
--- /dev/null
@@ -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 (file)
index 0000000..c4a0332
--- /dev/null
@@ -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 (file)
index 0000000..6565a80
--- /dev/null
@@ -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 (file)
index 0000000..09e0079
--- /dev/null
@@ -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 (file)
index 0000000..b98c74e
--- /dev/null
@@ -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 (file)
index 0000000..249d6a1
--- /dev/null
@@ -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 (file)
index 0000000..e5992cd
--- /dev/null
@@ -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 (file)
index 0000000..97ac801
--- /dev/null
@@ -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 (file)
index 0000000..6c8575d
--- /dev/null
@@ -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 (file)
index 0000000..719e009
--- /dev/null
@@ -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 (file)
index 0000000..40f4b76
--- /dev/null
@@ -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 (file)
index 0000000..db1a626
--- /dev/null
@@ -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 (file)
index 0000000..4fc8a62
--- /dev/null
@@ -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 (file)
index 0000000..6e18831
--- /dev/null
@@ -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 (file)
index 0000000..1832c3e
--- /dev/null
@@ -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<Byte> 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<Byte> 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<Byte> 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<Byte> 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<Byte> macacoFrame, GatewayConfig gwConfig) {
+        ArrayList<Byte> 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<Byte> 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<Byte> 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<Byte> buildVNetFrame(ArrayList<Byte> macacoFrame2, @Nullable String gatewayLanAddress,
+            byte iUserIndex, byte iNodeIndex) {
+        if (gatewayLanAddress != null) {
+            ArrayList<Byte> 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<Byte> 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<Byte> 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<Byte> 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<Byte> 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<Byte> 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<Byte> 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<Byte> 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<Byte> 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<Byte> mACACOframe) {
+        // I copy arrays to avoid concurrent changes
+        ArrayList<Byte> 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 (file)
index 0000000..aef293a
--- /dev/null
@@ -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 (file)
index 0000000..80a2f58
--- /dev/null
@@ -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 (file)
index 0000000..8bc083c
--- /dev/null
@@ -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<PacketStruct> 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<Thing> 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 (file)
index 0000000..bfa1ae8
--- /dev/null
@@ -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<Byte> 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<Byte> 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<Byte> 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<Byte> 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<Byte> 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<Byte> 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<Byte> 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<Byte> 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<Thing> 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<Byte> mac) {
+        int tgtnode = mac.get(3);
+
+        Iterator<Thing> 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<Byte> mac, int slot) {
+        return mac.get(5 + slot);
+    }
+
+    private float getFloatAtSlot(ArrayList<Byte> 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 (file)
index 0000000..4c585f1
--- /dev/null
@@ -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 (file)
index 0000000..f092bbc
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<binding:binding id="souliss" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
+
+       <name>Souliss Binding</name>
+       <description>This is the binding for Souliss. The Arduino based SmartHome..</description>
+
+</binding:binding>
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 (file)
index 0000000..66a4e92
--- /dev/null
@@ -0,0 +1,1286 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="souliss"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+
+       <bridge-type id="gateway">
+               <label>Gateway</label>
+               <description>Represents a Souliss Gateway.</description>
+
+               <representation-property>gatewayLanAddress</representation-property>
+
+               <config-description>
+                       <parameter name="gatewayLanAddress" type="text" required="true" pattern="^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$">
+                               <label>IPV4 Address</label>
+                               <description>LAN Ip address (mandatory)
+                               </description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="gatewayWanAddress" type="text" required="false"
+                               pattern="^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)+([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$">
+                               <label>IPV4 WAN Address</label>
+                               <description>WAN Hostname or Ip in case of external network access
+                               </description>
+                               <advanced>true</advanced>
+                       </parameter>
+                       <parameter name="gatewayPortNumber" type="integer" required="true">
+                               <label>Gateway Port</label>
+                               <description>Default is 230 UDP.
+                               </description>
+                               <advanced>true</advanced>
+                               <default>230</default>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="preferredLocalPortNumber" type="integer" required="true">
+                               <label>Local Port Number</label>
+                               <description>Default port is 23000
+                               </description>
+                               <default>23000</default>
+                               <advanced>true</advanced>
+                               <required>true</required>
+                       </parameter>
+
+                       <parameter name="pingInterval" type="integer" min="1" max="300" unit="s">
+                               <label>Ping Interval</label>
+                               <description>Interval in seconds to check for device presence.</description>
+                               <default>30</default>
+                               <advanced>true</advanced>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="subscriptionInterval" type="integer" min="2" max="240">
+                               <label>Subscription Interval</label>
+                               <description>Interval in seconds to subscribe Souliss Gateway.
+                               </description>
+                               <default>30</default>
+                               <advanced>true</advanced>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="healthyInterval" type="integer" min="1" max="240">
+                               <label>Healthy Interval</label>
+                               <description>Interval in seconds to send nodes healthy.
+                               </description>
+                               <default>60</default>
+                               <advanced>true</advanced>
+                               <required>true</required>
+                       </parameter>
+
+                       <parameter name="sendInterval" type="integer" min="5" max="5000">
+                               <label>Send Interval</label>
+                               <description>Interval in milliseconds to get packet from binding queue and send it to Souliss. First packet is sent
+                                       immediately.
+                               </description>
+                               <default>30</default>
+                               <advanced>true</advanced>
+                               <required>true</required>
+                       </parameter>
+
+                       <parameter name="timeoutToRequeue" type="integer" min="0" max="10000">
+                               <label>Timeout to Requeue</label>
+                               <description>Interval in milliseconds to requeue packet in queue if not yet executed</description>
+                               <required>true</required>
+                               <default>5000</default>
+                               <advanced>true</advanced>
+                       </parameter>
+
+                       <parameter name="timeoutToRemovePacket" type="integer" min="0" max="60000">
+                               <label>Timeout to Remove Packet</label>
+                               <description>Interval in milliseconds to remove packet from queue if not yet executed </description>
+                               <required>true</required>
+                               <default>20000</default>
+                               <advanced>true</advanced>
+                       </parameter>
+
+                       <parameter name="userIndex" type="integer" required="true">
+                               <label>User Index</label>
+                               <description>User Index</description>
+                               <default>70</default>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="nodeIndex" type="integer" required="true">
+                               <label>Node Index</label>
+                               <description>Node Index</description>
+                               <default>120</default>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </bridge-type>
+
+       <thing-type id="t11">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T11</label>
+               <description>Simple Light</description>
+
+               <channels>
+                       <channel id="onOff" typeId="onoff"/>
+                       <channel id="sleep" typeId="sleep"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="sleep" type="integer" min="1" max="1000">
+                               <label>Sleep</label>
+                               <description>Set sleep timer in cycles (conf.parameter: "sleep")</description>
+                               <required>false</required>
+                               <default>5</default>
+                       </parameter>
+                       <parameter name="secureSend" type="boolean">
+                               <label>Secure Send</label>
+                               <description> (conf.parameter: "secureSend") </description>
+                               <required>false</required>
+                               <default>true</default>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t12">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T12</label>
+               <description>Simple Light with Auto Mode</description>
+               <channels>
+                       <channel id="onOff" typeId="onoff"/>
+                       <channel id="autoMode" typeId="autoMode"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="sleep" type="integer" min="1" max="1000">
+                               <label>Sleep</label>
+                               <description>Set sleep timer in cycles</description>
+                               <required>false</required>
+                               <default>5</default>
+                       </parameter>
+                       <parameter name="secureSend" type="boolean">
+                               <label>Secure Send</label>
+                               <description/>
+                               <required>false</required>
+                               <default>true</default>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t13">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T13</label>
+               <description>Digital Input</description>
+
+               <channels>
+                       <channel id="stateOnOff" typeId="stateOnOff"/>
+                       <channel id="stateOpenClose" typeId="stateOpenClose"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t14">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T14</label>
+               <description>Pulse Digital Output</description>
+               <channels>
+                       <channel id="pulse" typeId="pulse"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t16">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T16</label>
+               <description>RGB LED Strip</description>
+
+               <channels>
+                       <channel id="onOff" typeId="onoff"/>
+                       <channel id="whiteMode" typeId="whiteMode"/>
+                       <channel id="rollerBrightness" typeId="rollerBrightness"/>
+                       <channel id="dimmerBrightness" typeId="dimmerBrightness"/>
+                       <channel id="ledColor" typeId="ledColor"/>
+                       <channel id="sleep" typeId="sleep"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="sleep" type="integer" min="1" max="1000">
+                               <label>Sleep</label>
+                               <description>Set sleep timer in cycles</description>
+                               <required>false</required>
+                               <default>5</default>
+                       </parameter>
+
+                       <parameter name="secureSend" type="boolean">
+                               <label>Secure Send</label>
+                               <description/>
+                               <required>false</required>
+                               <default>true</default>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t18">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T18</label>
+               <description>Simple Light with feedback </description>
+
+               <channels>
+                       <channel id="onOff" typeId="onoff"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="sleep" type="integer" min="1" max="1000">
+                               <label>Sleep</label>
+                               <description>Set sleep timer in cycles</description>
+                               <required>false</required>
+                               <default>5</default>
+                       </parameter>
+
+                       <parameter name="secureSend" type="boolean">
+                               <label>Secure Send</label>
+                               <description/>
+                               <required>false</required>
+                               <default>true</default>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t19">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T19</label>
+               <description>Single Color LED Strip</description>
+
+               <channels>
+                       <channel id="onOff" typeId="onoff"/>
+                       <channel id="rollerBrightness" typeId="rollerBrightness"/>
+                       <channel id="dimmerBrightness" typeId="dimmerBrightness"/>
+                       <channel id="sleep" typeId="sleep"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="sleep" type="integer" min="1" max="1000">
+                               <label>Sleep</label>
+                               <description>Set sleep timer in cycles</description>
+                               <required>false</required>
+                               <default>5</default>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t1a">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T1A</label>
+               <description>Digital input pass through</description>
+
+               <channels>
+                       <channel id="one" typeId="stateOpenClose">
+                               <label>1</label>
+                       </channel>
+                       <channel id="two" typeId="stateOpenClose">
+                               <label>2</label>
+                       </channel>
+                       <channel id="three" typeId="stateOpenClose">
+                               <label>3</label>
+                       </channel>
+                       <channel id="four" typeId="stateOpenClose">
+                               <label>4</label>
+                       </channel>
+                       <channel id="five" typeId="stateOpenClose">
+                               <label>5</label>
+                       </channel>
+                       <channel id="six" typeId="stateOpenClose">
+                               <label>6</label>
+                       </channel>
+                       <channel id="seven" typeId="stateOpenClose">
+                               <label>7</label>
+                       </channel>
+                       <channel id="eight" typeId="stateOpenClose">
+                               <label>8</label>
+                       </channel>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t21">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T21</label>
+               <description>Motorized devices with limit switches</description>
+               <channels>
+                       <channel id="rollerShutter" typeId="rollerShutter"/>
+                       <channel id="rollerShutterState" typeId="rollerShutterState"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="secureSend" type="boolean">
+                               <label>Secure Send</label>
+                               <description/>
+                               <required>false</required>
+                               <default>true</default>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t22">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T22</label>
+               <description>Motorized devices with limit switches and middle position</description>
+               <channels>
+                       <channel id="rollerShutter" typeId="rollerShutter"/>
+                       <channel id="rollerShutterState" typeId="rollerShutterState"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="secureSend" type="boolean">
+                               <label>Secure Send</label>
+                               <description/>
+                               <required>false</required>
+                               <default>true</default>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t31">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T31</label>
+               <description>Temperature control with cooling and heating mode</description>
+               <channels>
+                       <channel id="fan" typeId="fan-channel"/>
+                       <channel id="mode" typeId="mode-channel">
+                               <label>Mode</label>
+                       </channel>
+                       <channel id="system" typeId="onoff">
+                               <label>System</label>
+                       </channel>
+                       <channel id="fire" typeId="buttonReadOnly">
+                               <label>Fire</label>
+                       </channel>
+                       <channel id="setPoint" typeId="setpointTemperature"/>
+                       <channel id="setAsMeasured" typeId="button">
+                               <label>As Measured</label>
+                       </channel>
+                       <channel id="measured" typeId="temperature"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t41">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T41</label>
+               <description>Anti-theft integration (Main)</description>
+               <channels>
+                       <channel id="statusAlarm" typeId="buttonReadOnly">
+                               <label>Alarm Status</label>
+                       </channel>
+                       <channel id="onOffAlarm" typeId="onoff">
+                               <label>Alarm ON/OFF</label>
+                       </channel>
+                       <channel id="rearmAlarm" typeId="button">
+                               <label>Rearm</label>
+                       </channel>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="secureSend" type="boolean">
+                               <label>Secure Send</label>
+                               <description/>
+                               <required>false</required>
+                               <default>true</default>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t42">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T42</label>
+               <description>Anti-theft integration (Peer)</description>
+               <channels>
+                       <channel id="statusAlarm" typeId="buttonReadOnly">
+                               <label>Alarm Status</label>
+                       </channel>
+                       <channel id="onOffAlarm" typeId="onoff">
+                               <label>Alarm ON/OFF</label>
+                       </channel>
+                       <channel id="rearmAlarm" typeId="button">
+                               <label>Rearm</label>
+                       </channel>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="secureSend" type="boolean">
+                               <label>Secure Send</label>
+                               <description/>
+                               <required>false</required>
+                               <default>true</default>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+
+       <thing-type id="t51">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T51</label>
+               <description>Floating Point Input</description>
+
+               <channels>
+                       <channel id="value" typeId="float"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t52">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T52</label>
+               <description>Temperature measure (-20, +50) Â°C</description>
+
+               <channels>
+                       <channel id="value" typeId="temperature"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t53">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T53</label>
+               <description>Humidity measure (0, 100) %</description>
+               <channels>
+                       <channel id="value" typeId="humidity"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t54">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T54</label>
+               <description>Light Sensor (0, 40) kLux</description>
+               <channels>
+                       <channel id="value" typeId="lux"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t55">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T55</label>
+               <description>Voltage (0, 400) V</description>
+               <channels>
+                       <channel id="value" typeId="voltage"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t56">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T56</label>
+               <description>Current (0, 25) A</description>
+               <channels>
+                       <channel id="value" typeId="ampere"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t57">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T57</label>
+               <description>Power (0, 6500) W</description>
+               <channels>
+                       <channel id="value" typeId="power"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t58">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T58</label>
+               <description>Pressure measure (0, 1500) hPa</description>
+               <channels>
+                       <channel id="value" typeId="power"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t61">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T61</label>
+               <description>Analog setpoint</description>
+               <channels>
+                       <channel id="value" typeId="float6n"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t62">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T62</label>
+               <description>Temperature measure (-20, +50) Â°C</description>
+               <channels>
+                       <channel id="value" typeId="float6n"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t63">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T63</label>
+               <description>Humidity measure (0, 100) %</description>
+               <channels>
+                       <channel id="value" typeId="float6n"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t64">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T64</label>
+               <description>Light Sensor (0, 40) kLux</description>
+               <channels>
+                       <channel id="value" typeId="float6n"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t65">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T65</label>
+               <description>Voltage (0, 400) V</description>
+               <channels>
+                       <channel id="value" typeId="float6n"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t66">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T66</label>
+               <description>Current (0, 25) A</description>
+               <channels>
+                       <channel id="value" typeId="float6n"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t67">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T67</label>
+               <description>Power (0, 6500) W</description>
+               <channels>
+                       <channel id="value" typeId="float6n"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="t68">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>T68</label>
+               <description>Pressure measure (0, 1500) hPa</description>
+               <channels>
+                       <channel id="value" typeId="float6n"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+                       <channel id="healthy" typeId="healthy"/>
+               </channels>
+               <config-description>
+                       <parameter name="node" type="integer" min="0" max="256">
+                               <label>Node</label>
+                               <description>Node</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="slot" type="integer" min="0" max="256">
+                               <label>Slot</label>
+                               <description>Slot</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <thing-type id="topic">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="gateway"/>
+               </supported-bridge-type-refs>
+               <label>Action Message</label>
+               <description>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.</description>
+               <channels>
+                       <channel id="value" typeId="float6n"/>
+                       <channel id="lastStatusStored" typeId="lastStatusStored"/>
+               </channels>
+               <representation-property>number</representation-property>
+               <config-description>
+                       <parameter name="number" type="text">
+                               <label>Number</label>
+                               <description>Topic Number</description>
+                               <required>true</required>
+                       </parameter>
+                       <parameter name="variant" type="text">
+                               <label>Variant</label>
+                               <description>Topic Variant</description>
+                               <required>true</required>
+                       </parameter>
+               </config-description>
+       </thing-type>
+
+       <!-- CHANNELS -->
+
+       <channel-type id="fan-channel">
+               <item-type>String</item-type>
+               <label>Fan</label>
+               <state readOnly="false">
+                       <options>
+                               <option value="AUTO">Auto</option>
+                               <option value="HIGH">High</option>
+                               <option value="MEDIUM">Medium</option>
+                               <option value="LOW">Low</option>
+                               <option value="FANOFF">Powered off</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="mode-channel">
+               <item-type>String</item-type>
+               <label>Mode</label>
+               <state readOnly="false">
+                       <options>
+                               <option value="COOLING_MODE">Cool</option>
+                               <option value="HEATING_MODE">Heat</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="button">
+               <kind>trigger</kind>
+               <label>Button</label>
+               <description>Button to trigger something</description>
+               <category>Switch</category>
+       </channel-type>
+
+       <channel-type id="buttonReadOnly">
+               <kind>trigger</kind>
+               <label>Button</label>
+               <description>Button to trigger something</description>
+               <category>Switch</category>
+       </channel-type>
+
+
+       <channel-type id="pulse">
+               <kind>trigger</kind>
+               <label>Pulse</label>
+               <description>Set</description>
+               <category>Switch</category>
+       </channel-type>
+
+       <channel-type id="onoff">
+               <item-type>Switch</item-type>
+               <label>On/Off</label>
+               <description>Switch on/off</description>
+       </channel-type>
+
+       <channel-type id="sleep">
+               <item-type>Switch</item-type>
+               <label>Set Timer</label>
+               <description>The output will be timed for nCYCLES of the Node associated timer</description>
+       </channel-type>
+
+       <channel-type id="autoMode">
+               <item-type>Switch</item-type>
+               <label>On/Off Auto Mode</label>
+               <description>Switch on/off</description>
+       </channel-type>
+
+       <channel-type id="stateOnOff">
+               <item-type>Contact</item-type>
+               <label>State On/Off</label>
+               <description>Light on/off</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+
+       <channel-type id="stateOpenClose">
+               <item-type>Contact</item-type>
+               <label>State Open/Closed</label>
+               <description>Contact Open/Closed</description>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="rollerShutter">
+               <item-type>RollerShutter</item-type>
+               <label>Rollershutter</label>
+               <description>Rollershutter Up/Down</description>
+               <category>Blinds</category>
+               <state min="0" max="100" pattern="%d %%"/>
+       </channel-type>
+
+       <channel-type id="rollerShutterState">
+               <item-type>String</item-type>
+               <label>State</label>
+               <description>State of rollershutter/windows</description>
+               <state readOnly="true">
+                       <options>
+                               <option value="opening">Opening</option>
+                               <option value="stop">Stop</option>
+                               <option value="closing">Closing</option>
+                               <option value="limSwitchOpen">Limit Switch Open</option>
+                               <option value="limSwitchClose">Limit Switch Close</option>
+                               <option value="stateOpen">Opened</option>
+                               <option value="stateClose">Closed</option>
+                               <option value="NoLimSwitch">No Limit Switch</option>
+                       </options>
+               </state>
+       </channel-type>
+
+       <channel-type id="lastMessage" advanced="true">
+               <item-type>DateTime</item-type>
+               <label>Last Message</label>
+               <description>Last Message emitted by the module</description>
+               <category>QualityOfService</category>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="lastStatusStored" advanced="false">
+               <item-type>DateTime</item-type>
+               <label>Last Status Stored</label>
+               <description>Last Status Store</description>
+               <category>QualityOfService</category>
+               <state readOnly="true"/>
+       </channel-type>
+
+       <channel-type id="healthy">
+               <item-type>Number</item-type>
+               <label>Healthy</label>
+               <description>Souliss Healthy</description>
+               <tags>
+                       <tag>QualityOfService</tag>
+               </tags>
+               <state readOnly="true" pattern="%.0f"/>
+       </channel-type>
+
+       <channel-type id="float">
+               <item-type>Number</item-type>
+               <label>Value</label>
+               <description>Floating Point Input</description>
+               <tags>
+                       <tag>sensors</tag>
+               </tags>
+               <state readOnly="true" pattern="%.1f "/>
+       </channel-type>
+
+       <channel-type id="float6n">
+               <item-type>Number</item-type>
+               <kind>trigger</kind>
+               <label>Setpoint</label>
+               <description>Floating Point Input</description>
+               <category>Temperature</category>
+               <state pattern="%.1f"/>
+       </channel-type>
+
+       <channel-type id="humidity">
+               <item-type>Number</item-type>
+               <label>Humidity</label>
+               <description>Current humidity in %</description>
+               <category>Temperature</category>
+               <tags>
+                       <tag>sensors</tag>
+               </tags>
+               <state readOnly="true" pattern="%d %%"/>
+       </channel-type>
+
+
+       <channel-type id="temperature">
+               <item-type>Number:Temperature</item-type>
+               <label>Temperature</label>
+               <description>Current temperature</description>
+               <category>Temperature</category>
+               <tags>
+                       <tag>sensors</tag>
+               </tags>
+               <state readOnly="true" pattern="%.1f %unit%"/>
+       </channel-type>
+
+       <channel-type id="setpointTemperature" advanced="true">
+               <item-type>Number:Temperature</item-type>
+               <label>Setpoint Temperature</label>
+               <description>Setpoint temperature</description>
+               <category>Temperature</category>
+               <tags>
+                       <tag>sensors</tag>
+               </tags>
+               <state min="12" max="30" step="0.5" pattern="%.1f %unit%" readOnly="false"/>
+       </channel-type>
+
+       <channel-type id="lux">
+               <item-type>Number</item-type>
+               <label>Illuminance</label>
+               <description>Lux</description>
+               <tags>
+                       <tag>sensors</tag>
+               </tags>
+               <state readOnly="true" pattern="%.1f kLux"/>
+       </channel-type>
+
+       <channel-type id="voltage">
+               <item-type>Number</item-type>
+               <label>Voltage</label>
+               <description>Current Voltage</description>
+               <category>Energy</category>
+               <tags>
+                       <tag>sensors</tag>
+               </tags>
+               <state readOnly="true" pattern="%.1f V"/>
+       </channel-type>
+       <channel-type id="ampere">
+               <item-type>Number</item-type>
+               <label>Current</label>
+               <description>Current Ampere</description>
+               <category>Energy</category>
+               <tags>
+                       <tag>sensors</tag>
+               </tags>
+               <state readOnly="true" pattern="%.1f A"/>
+       </channel-type>
+
+       <channel-type id="power">
+               <item-type>Number</item-type>
+               <label>Power</label>
+               <description>Current Power</description>
+               <category>Energy</category>
+               <tags>
+                       <tag>sensors</tag>
+               </tags>
+               <state readOnly="true" pattern="%d W"/>
+       </channel-type>
+
+       <channel-type id="pressure">
+               <item-type>Number</item-type>
+               <label>Pressure</label>
+               <description>Pressure</description>
+               <tags>
+                       <tag>sensors</tag>
+               </tags>
+               <state readOnly="true" pattern="%.1f hPa"/>
+       </channel-type>
+
+       <channel-type id="ledColor">
+               <item-type>Color</item-type>
+               <label>Color</label>
+               <description>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.</description>
+               <category>ColorLight</category>
+       </channel-type>
+
+       <channel-type id="dimmerBrightness">
+               <item-type>Dimmer</item-type>
+               <label>Dimmer Brightness</label>
+               <description>The brightness can be set in 16 steps for RGBW/White leds and in 64 steps for RGBWW leds</description>
+               <category>DimmableLight</category>
+               <state min="0" max="100" step="1" pattern="%d"/>
+       </channel-type>
+
+       <channel-type id="whiteMode">
+               <item-type>Switch</item-type>
+               <label>White Mode</label>
+               <description>Switch lamp to white mode</description>
+               <category>DimmableLight</category>
+       </channel-type>
+
+       <channel-type id="rollerBrightness">
+               <item-type>RollerShutter</item-type>
+               <label>Roller Brightness</label>
+               <description>Brightness Up/Down</description>
+               <category>DimmableLight</category>
+               <state readOnly="false"/>
+       </channel-type>
+</thing:thing-descriptions>
index 2ac8d20aee5035f188c1ca64329af88f42427457..83ed47c63665b71857a56f68700663eb3ee400ea 100644 (file)
     <module>org.openhab.binding.sonos</module>
     <module>org.openhab.binding.sonyaudio</module>
     <module>org.openhab.binding.sonyprojector</module>
+    <module>org.openhab.binding.souliss</module>
     <module>org.openhab.binding.spotify</module>
     <module>org.openhab.binding.squeezebox</module>
     <module>org.openhab.binding.surepetcare</module>