diff --git a/CODEOWNERS b/CODEOWNERS index 8a543c1dda52b..a66d2aedaa64e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -5,58 +5,6 @@ * @openhab/2-x-add-ons-maintainers # Add-on maintainers: -/addons/binding/org.openhab.binding.bluetooth/ @cdjackson @kaikreuzer -/addons/binding/org.openhab.binding.bluetooth.bluegiga/ @cdjackson @kaikreuzer -/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/ @cdjackson @kaikreuzer -/addons/binding/org.openhab.binding.bluetooth.bluez/ @cdjackson @kaikreuzer -/addons/binding/org.openhab.binding.bluetooth.blukii/ @kaikreuzer -/addons/binding/org.openhab.binding.bluetooth.ruuvitag @ssalonen -/addons/binding/org.openhab.binding.bluetooth.test/ @cdjackson @kaikreuzer -/addons/binding/org.openhab.binding.boschindego/ @jofleck -/addons/binding/org.openhab.binding.chromecast/ @kaikreuzer -/addons/binding/org.openhab.binding.chromecast.test/ @kaikreuzer -/addons/binding/org.openhab.binding.dsmr/ @Hilbrand -/addons/binding/org.openhab.binding.dsmr.test/ @Hilbrand -/addons/binding/org.openhab.binding.enocean/ @fruggy83 -/addons/binding/org.openhab.binding.exec/ @kgoderis -/addons/binding/org.openhab.binding.feed/ @svilenvul -/addons/binding/org.openhab.binding.feed.test/ @svilenvul -/addons/binding/org.openhab.binding.ftpupload/ @paulianttila -/addons/binding/org.openhab.binding.ihc/ @paulianttila -/addons/binding/org.openhab.binding.ihc.test/ @paulianttila -/addons/binding/org.openhab.binding.innogysmarthome/ @ollie-dev -/addons/binding/org.openhab.binding.lgwebos/ @sprehn -/addons/binding/org.openhab.binding.loxone/ @ppieczul -/addons/binding/org.openhab.binding.modbus/ @ssalonen -/addons/binding/org.openhab.binding.modbus.test/ @ssalonen -/addons/binding/org.openhab.binding.netatmo/ @clinique @cweitkamp @lolodomo -/addons/binding/org.openhab.binding.nikohomecontrol/ @mherwege -/addons/binding/org.openhab.binding.onewiregpio/ @aogorek -/addons/binding/org.openhab.binding.onkyo/ @pail23 @paulianttila -/addons/binding/org.openhab.binding.orvibo/ @tavalin -/addons/binding/org.openhab.binding.samsungtv/ @paulianttila -/addons/binding/org.openhab.binding.smartmeter/ @msteigenberger -/addons/binding/org.openhab.binding.smartmeter.test/ @msteigenberger -/addons/binding/org.openhab.binding.systeminfo/ @svilenvul -/addons/binding/org.openhab.binding.systeminfo.test/ @svilenvul -/addons/binding/org.openhab.binding.yamahareceiver/ @davidgraeff @zarusz -/addons/binding/org.openhab.binding.yamahareceiver.test/ @davidgraeff @zarusz -/addons/binding/org.openhab.binding.zoneminder/ @Mr-Eskildsen -/addons/io/org.openhab.io.azureiothub/ @nikotanghe -/addons/io/org.openhab.io.homekit/ @beowulfe -/addons/io/org.openhab.io.hueemulation/ @davidgraeff @digitaldan -/addons/io/org.openhab.io.hueemulation.test/ @davidgraeff @digitaldan -/addons/io/org.openhab.io.imperihome/ @pdegeus -/addons/io/org.openhab.io.javasound/ @kaikreuzer -/addons/io/org.openhab.io.mqttembeddedbroker/ @davidgraeff -/addons/io/org.openhab.io.mqttembeddedbroker.test/ @davidgraeff -/addons/io/org.openhab.io.openhabcloud/ @kaikreuzer -/addons/io/org.openhab.io.sound/ @kaikreuzer -/addons/io/org.openhab.io.transport.feed/ @svilenvul -/addons/io/org.openhab.io.transport.modbus/ @ssalonen -/addons/io/org.openhab.io.transport.modbus.test/ @ssalonen -/addons/voice/org.openhab.voice.kaldi/ @kaikreuzer -/addons/voice/org.openhab.voice.pollytts/ @hillmanr /bundles/org.openhab.binding.airquality/ @kubawolanin /bundles/org.openhab.binding.airvisualnode/ @3cky /bundles/org.openhab.binding.allplay/ @dominicdesun @@ -67,7 +15,15 @@ /bundles/org.openhab.binding.autelis/ @digitaldan /bundles/org.openhab.binding.avmfritz/ @cweitkamp /bundles/org.openhab.binding.bigassfan/ @mhilbush +/bundles/org.openhab.binding.bluetooth/ @cdjackson @kaikreuzer +/bundles/org.openhab.binding.bluetooth.bluegiga/ @cdjackson @kaikreuzer +/bundles/org.openhab.binding.bluetooth.bluez/ @cdjackson @kaikreuzer +/bundles/org.openhab.binding.bluetooth.blukii/ @kaikreuzer +/bundles/org.openhab.binding.bluetooth.ruuvitag @ssalonen +/bundles/org.openhab.binding.boschindego/ @jofleck /bundles/org.openhab.binding.bosesoundtouch/ @marvkis @tratho +/bundles/org.openhab.binding.buienradar/ @gedejong +/bundles/org.openhab.binding.chromecast/ @kaikreuzer /bundles/org.openhab.binding.cm11a/ @BobRak /bundles/org.openhab.binding.coolmasternet/ @projectgus /bundles/org.openhab.binding.daikin/ @caffineehacker @@ -79,14 +35,19 @@ /bundles/org.openhab.binding.dlinksmarthome/ @MikeJMajor /bundles/org.openhab.binding.dmx/ @J-N-K /bundles/org.openhab.binding.dscalarm/ @RSStephens +/bundles/org.openhab.binding.dsmr/ @Hilbrand /bundles/org.openhab.binding.dwdunwetter/ @limdul79 /bundles/org.openhab.binding.elerotransmitterstick/ @vbier +/bundles/org.openhab.binding.enocean/ @fruggy83 /bundles/org.openhab.binding.evohome/ @Nebula83 +/bundles/org.openhab.binding.exec/ @kgoderis +/bundles/org.openhab.binding.feed/ @svilenvul /bundles/org.openhab.binding.feican/ @Hilbrand /bundles/org.openhab.binding.folding/ @fa2k /bundles/org.openhab.binding.freebox/ @lolodomo /bundles/org.openhab.binding.fronius/ @trokohl /bundles/org.openhab.binding.fsinternetradio/ @paphko +/bundles/org.openhab.binding.ftpupload/ @paulianttila /bundles/org.openhab.binding.gardena/ @gerrieg /bundles/org.openhab.binding.globalcache/ @mhilbush /bundles/org.openhab.binding.gpstracker/ @gbicskei @@ -99,6 +60,8 @@ /bundles/org.openhab.binding.hue/ @cweitkamp /bundles/org.openhab.binding.hyperion/ @tavalin /bundles/org.openhab.binding.icloud/ @pgfeller +/bundles/org.openhab.binding.ihc/ @paulianttila +/bundles/org.openhab.binding.innogysmarthome/ @ollie-dev /bundles/org.openhab.binding.ipp/ @peuter /bundles/org.openhab.binding.irtrans/ @kgoderis /bundles/org.openhab.binding.jeelink/ @vbier @@ -111,10 +74,13 @@ /bundles/org.openhab.binding.lametrictime/ @syphr42 /bundles/org.openhab.binding.leapmotion/ @kaikreuzer /bundles/org.openhab.binding.lgtvserial/ @fa2k +/bundles/org.openhab.binding.lgwebos/ @sprehn /bundles/org.openhab.binding.lifx/ @wborn /bundles/org.openhab.binding.lirc/ @kabili207 /bundles/org.openhab.binding.logreader/ @paulianttila +/bundles/org.openhab.binding.loxone/ @ppieczul /bundles/org.openhab.binding.lutron/ @actong +/bundles/org.openhab.binding.mail/ @J-N-K /bundles/org.openhab.binding.max/ @marcelrv /bundles/org.openhab.binding.mcp23017/ @aogorek /bundles/org.openhab.binding.meteoblue/ @9037568 @@ -124,6 +90,7 @@ /bundles/org.openhab.binding.miio/ @marcelrv /bundles/org.openhab.binding.milight/ @davidgraeff /bundles/org.openhab.binding.minecraft/ @ibaton +/bundles/org.openhab.binding.modbus/ @ssalonen /bundles/org.openhab.binding.mqtt/ @davidgraeff /bundles/org.openhab.binding.mqtt.generic/ @davidgraeff /bundles/org.openhab.binding.mqtt.homeassistant/ @davidgraeff @@ -136,14 +103,18 @@ /bundles/org.openhab.binding.network/ @davidgraeff @mettke /bundles/org.openhab.binding.nibeheatpump/ @paulianttila /bundles/org.openhab.binding.nibeuplink/ @alexf2015 +/bundles/org.openhab.binding.nikohomecontrol/ @mherwege /bundles/org.openhab.binding.ntp/ @marcelrv /bundles/org.openhab.binding.nuki/ @mkatter /bundles/org.openhab.binding.oceanic/ @kgoderis /bundles/org.openhab.binding.onebusaway/ @sdwilsh +/bundles/org.openhab.binding.onewiregpio/ @aogorek /bundles/org.openhab.binding.onewire/ @J-N-K +/bundles/org.openhab.binding.onkyo/ @pail23 @paulianttila /bundles/org.openhab.binding.opensprinkler/ @CrackerStealth /bundles/org.openhab.binding.openuv/ @clinique /bundles/org.openhab.binding.openweathermap/ @cweitkamp +/bundles/org.openhab.binding.orvibo/ @tavalin /bundles/org.openhab.binding.pentair/ @jsjames /bundles/org.openhab.binding.phc/ @gnlpfjh /bundles/org.openhab.binding.pioneeravr/ @Stratehm @@ -157,6 +128,7 @@ /bundles/org.openhab.binding.robonect/ @reyem /bundles/org.openhab.binding.rotelra1x/ @fa2k /bundles/org.openhab.binding.russound/ @tmrobert8 +/bundles/org.openhab.binding.samsungtv/ @paulianttila /bundles/org.openhab.binding.satel/ @druciak /bundles/org.openhab.binding.seneye/ @nikotanghe /bundles/org.openhab.binding.sensebox/ @hakan42 @@ -164,6 +136,7 @@ /bundles/org.openhab.binding.silvercrestwifisocket/ @jmvaz /bundles/org.openhab.binding.sleepiq/ @syphr42 /bundles/org.openhab.binding.smaenergymeter/ @monnimeter +/bundles/org.openhab.binding.smartmeter/ @msteigenberger /bundles/org.openhab.binding.solaredge/ @alexf2015 /bundles/org.openhab.binding.solarlog/ @johannrichard /bundles/org.openhab.binding.somfytahoma/ @octa22 @@ -172,6 +145,7 @@ /bundles/org.openhab.binding.spotify/ @Hilbrand /bundles/org.openhab.binding.squeezebox/ @digitaldan @mhilbush /bundles/org.openhab.binding.synopanalyzer/ @clinique +/bundles/org.openhab.binding.systeminfo/ @svilenvul /bundles/org.openhab.binding.tado/ @dfrommi /bundles/org.openhab.binding.tankerkoenig/ @dolic @JueBag /bundles/org.openhab.binding.tellstick/ @jarlebh @@ -188,13 +162,24 @@ /bundles/org.openhab.binding.wemo/ @hmerk /bundles/org.openhab.binding.wifiled/ @rvt @xylo /bundles/org.openhab.binding.windcentrale/ @marcelrv +/bundles/org.openhab.binding.xmltv/ @clinique +/bundles/org.openhab.binding.xmppclient/ @pavel-gololobov +/bundles/org.openhab.binding.yamahareceiver/ @davidgraeff @zarusz /bundles/org.openhab.binding.yeelight/ @claell +/bundles/org.openhab.binding.zoneminder/ @Mr-Eskildsen /bundles/org.openhab.binding.zway/ @pathec /bundles/org.openhab.extensionservice.marketplace/ @kaikreuzer /bundles/org.openhab.extensionservice.marketplace.automation/ @kaikreuzer +/bundles/org.openhab.io.azureiothub/ @nikotanghe +/bundles/org.openhab.io.homekit/ @beowulfe +/bundles/org.openhab.io.hueemulation/ @davidgraeff @digitaldan +/bundles/org.openhab.io.hueemulation.test/ @davidgraeff @digitaldan /bundles/org.openhab.io.imperihome/ @pdegeus /bundles/org.openhab.io.javasound/ @kaikreuzer +/bundles/org.openhab.io.mqttembeddedbroker/ @davidgraeff /bundles/org.openhab.io.neeo/ @tmrobert8 +/bundles/org.openhab.io.openhabcloud/ @kaikreuzer +/bundles/org.openhab.io.transport.modbus/ @ssalonen /bundles/org.openhab.io.webaudio/ @kaikreuzer /bundles/org.openhab.persistence.mapdb/ @mkhl /bundles/org.openhab.transform.exec/ @openhab/2-x-add-ons-maintainers @@ -210,17 +195,19 @@ /bundles/org.openhab.voice.mactts/ @kaikreuzer /bundles/org.openhab.voice.marytts/ @kaikreuzer /bundles/org.openhab.voice.picotts/ @FlorianSW +/bundles/org.openhab.voice.pollytts/ @hillmanr /bundles/org.openhab.voice.voicerss/ @JochenHiller /itests/org.openhab.binding.astro.tests/ @gerrieg /itests/org.openhab.binding.avmfritz.tests/ @cweitkamp /itests/org.openhab.binding.dmx.tests/ @J-N-K +/itests/org.openhab.binding.feed.tests/ @svilenvul /itests/org.openhab.binding.hue.tests/ @cweitkamp /itests/org.openhab.binding.max.tests/ @marcelrv /itests/org.openhab.binding.mqtt.homeassistant.tests/ @davidgraeff /itests/org.openhab.binding.mqtt.homie.tests/ @davidgraeff /itests/org.openhab.binding.nest.tests/ @wborn /itests/org.openhab.binding.ntp.tests/ @marcelrv -/itests/org.openhab.binding.onewire.tests/ @J-N-K +/itests/org.openhab.binding.systeminfo.tests/ @svilenvul /itests/org.openhab.binding.tradfri.tests/ @cweitkamp @kaikreuzer /itests/org.openhab.binding.wemo.tests/ @hmerk /itests/org.openhab.persistence.mapdb.tests/ @mkhl diff --git a/README.md b/README.md index fc024b82b99e7..076cec6bd5049 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,6 @@ You find the following repository structure: ``` . -+-- addons/ Legacy addons that are currently ported to the new buildsystem -| +-- bom Maven buildsystem: Bill of materials | +-- openhab-addons Lists all extensions for other repos to reference them | +-- ... Other boms @@ -39,7 +37,7 @@ You find the following repository structure: | +-- org.openhab.binding.astro | +-- ... | -+-- features/karaf An extension usually has dependencies (at least openHAB core). ++-- features/ An extension usually has dependencies (at least openHAB core). | | In those feature files are the dependencies for the OSGi container declared. | +-- openhab-addons-external/src/main/feature/feature.xml | +-- openhab-addons/src/main/feature/feature.xml @@ -49,7 +47,6 @@ You find the following repository structure: | +-- org.openhab.binding.avmfritz.tests | +-- ... | -+-- poms Maven buildsystem files +-- src/etc Auxilary buildsystem files: The license header for automatic checks for example +-- tools Static code analyser instructions | diff --git a/addons/archetype-settings.xml b/addons/archetype-settings.xml deleted file mode 100644 index 2ec2ddd3aaa8c..0000000000000 --- a/addons/archetype-settings.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - smarthome-snapshots - - - archetype - https://repo.eclipse.org/content/repositories/smarthome-snapshots/ - - - - - - smarthome-snapshots - - diff --git a/addons/binding/create_openhab_binding_skeleton.cmd b/addons/binding/create_openhab_binding_skeleton.cmd deleted file mode 100644 index 1f51cad870e6a..0000000000000 --- a/addons/binding/create_openhab_binding_skeleton.cmd +++ /dev/null @@ -1,39 +0,0 @@ -@echo off - -SETLOCAL -SET ARGC=0 - -FOR %%x IN (%*) DO SET /A ARGC+=1 - -IF %ARGC% NEQ 2 ( - echo Usage: %0 BindingIdInCamelCase Author - exit /B 1 -) - -SET BindingVersion="2.5.0-SNAPSHOT" -SET ArchetypeVersion="0.11.0-SNAPSHOT" - -SET BindingIdInCamelCase=%~1 -SET BindingIdInLowerCase=%BindingIdInCamelCase% -SET Author=%~2 - -call :LoCase BindingIdInLowerCase - -call mvn -s ../archetype-settings.xml archetype:generate -N -DarchetypeGroupId=org.eclipse.smarthome.archetype -DarchetypeArtifactId=org.eclipse.smarthome.archetype.binding -DarchetypeVersion=%ArchetypeVersion% -DgroupId=org.openhab.binding -DartifactId=org.openhab.binding.%BindingIdInLowerCase% -Dpackage=org.openhab.binding.%BindingIdInLowerCase% -Dversion=%BindingVersion% -DbindingId=%BindingIdInLowerCase% -DbindingIdCamelCase=%BindingIdInCamelCase% -DvendorName=openHAB -Dnamespace=org.openhab -Dauthor="%Author%" - -COPY ..\..\src\etc\NOTICE org.openhab.binding.%BindingIdInLowerCase%\ - -(SET BindingIdInLowerCase=) -(SET BindingIdInCamelCase=) -(SET Author=) - -GOTO:EOF - - -:LoCase -:: Subroutine to convert a variable VALUE to all lower case. -:: The argument for this subroutine is the variable NAME. -FOR %%i IN ("A=a" "B=b" "C=c" "D=d" "E=e" "F=f" "G=g" "H=h" "I=i" "J=j" "K=k" "L=l" "M=m" "N=n" "O=o" "P=p" "Q=q" "R=r" "S=s" "T=t" "U=u" "V=v" "W=w" "X=x" "Y=y" "Z=z") DO CALL SET "%1=%%%1:%%~i%%" -GOTO:EOF - -ENDLOCAL diff --git a/addons/binding/create_openhab_binding_skeleton.sh b/addons/binding/create_openhab_binding_skeleton.sh deleted file mode 100755 index 6db16a78aab20..0000000000000 --- a/addons/binding/create_openhab_binding_skeleton.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -[ $# -lt 2 ] && { echo "Usage: $0 "; exit 1; } - -bindingVersion=2.5.0-SNAPSHOT -archetypeVersion=0.11.0-SNAPSHOT - -camelcaseId=$1 -id=`echo $camelcaseId | tr '[:upper:]' '[:lower:]'` - -author=$2 - -mvn -s ../archetype-settings.xml archetype:generate -N \ - -DarchetypeGroupId=org.eclipse.smarthome.archetype \ - -DarchetypeArtifactId=org.eclipse.smarthome.archetype.binding \ - -DarchetypeVersion=$archetypeVersion \ - -DgroupId=org.openhab.binding \ - -DartifactId=org.openhab.binding.$id \ - -Dpackage=org.openhab.binding.$id \ - -Dversion=$bindingVersion \ - -DbindingId=$id \ - -DbindingIdCamelCase=$camelcaseId \ - -DvendorName=openHAB \ - -Dnamespace=org.openhab \ - -Dauthor="$author" - -directory="org.openhab.binding.$id/" - -cp ../../src/etc/NOTICE "$directory" - diff --git a/addons/binding/create_openhab_binding_test_skeleton.cmd b/addons/binding/create_openhab_binding_test_skeleton.cmd deleted file mode 100644 index c4da0292dcef7..0000000000000 --- a/addons/binding/create_openhab_binding_test_skeleton.cmd +++ /dev/null @@ -1,44 +0,0 @@ -@echo off - -SETLOCAL -SET ARGC=0 - -FOR %%x IN (%*) DO SET /A ARGC+=1 - -IF %ARGC% NEQ 2 ( - echo Usage: %0 BindingIdInCamelCase Author - exit /B 1 -) - -SET BindingVersion="2.5.0-SNAPSHOT" -SET ArchetypeVersion="0.11.0-SNAPSHOT" - -SET BindingIdInCamelCase=%1 -SET BindingIdInLowerCase=%BindingIdInCamelCase% -SET Author=%2 - -call :LoCase BindingIdInLowerCase - -If NOT exist "org.openhab.binding.%BindingIdInLowerCase%" ( - echo The binding with the id must exist: org.openhab.binding.%BindingIdInLowerCase% - exit /B 1 -) - -call mvn -s ../archetype-settings.xml archetype:generate -N -DarchetypeGroupId=org.eclipse.smarthome.archetype -DarchetypeArtifactId=org.eclipse.smarthome.archetype.binding.test -DarchetypeVersion=%ArchetypeVersion% -DgroupId=org.openhab.binding -DartifactId=org.openhab.binding.%BindingIdInLowerCase%.test -Dpackage=org.openhab.binding.%BindingIdInLowerCase% -Dversion=%BindingVersion% -DbindingId=%BindingIdInLowerCase% -DbindingIdCamelCase=%BindingIdInCamelCase% -DvendorName=openHAB -Dnamespace=org.openhab -Dauthor="%Author%" - -COPY ..\..\src\etc\NOTICE org.openhab.binding.%BindingIdInLowerCase%.test\ - -(SET BindingIdInLowerCase=) -(SET BindingIdInCamelCase=) -(SET Author=) - -GOTO:EOF - - -:LoCase -:: Subroutine to convert a variable VALUE to all lower case. -:: The argument for this subroutine is the variable NAME. -FOR %%i IN ("A=a" "B=b" "C=c" "D=d" "E=e" "F=f" "G=g" "H=h" "I=i" "J=j" "K=k" "L=l" "M=m" "N=n" "O=o" "P=p" "Q=q" "R=r" "S=s" "T=t" "U=u" "V=v" "W=w" "X=x" "Y=y" "Z=z") DO CALL SET "%1=%%%1:%%~i%%" -GOTO:EOF - -ENDLOCAL diff --git a/addons/binding/create_openhab_binding_test_skeleton.sh b/addons/binding/create_openhab_binding_test_skeleton.sh deleted file mode 100755 index e9f188dce956e..0000000000000 --- a/addons/binding/create_openhab_binding_test_skeleton.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -[ $# -lt 2 ] && { echo "Usage: $0 "; exit 1; } - -bindingVersion=2.5.0-SNAPSHOT -archetypeVersion=0.11.0-SNAPSHOT - -camelcaseId=$1 -id=`echo $camelcaseId | tr '[:upper:]' '[:lower:]'` - -author=$2 - -[ -d org.openhab.binding.$id ] || { echo "The binding with the id must exist: org.openhab.binding.$id"; exit 1; } - -mvn -s ../archetype-settings.xml archetype:generate -N \ - -DarchetypeGroupId=org.eclipse.smarthome.archetype \ - -DarchetypeArtifactId=org.eclipse.smarthome.archetype.binding.test \ - -DarchetypeVersion=$archetypeVersion \ - -DgroupId=org.openhab.binding \ - -DartifactId=org.openhab.binding.$id.test \ - -Dpackage=org.openhab.binding.$id \ - -Dversion=$bindingVersion \ - -DbindingId=$id \ - -DbindingIdCamelCase=$camelcaseId \ - -DvendorName=openHAB \ - -Dnamespace=org.openhab \ - -Dauthor="$author" - -directory="org.openhab.binding.$id.test/" - -cp ../../src/etc/NOTICE "$directory" - diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/.classpath b/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/.classpath deleted file mode 100644 index 64777f25c82a4..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/.project b/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/.project deleted file mode 100644 index 727adac7d54a3..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.openhab.binding.bluetooth.bluegiga.test - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/META-INF/MANIFEST.MF deleted file mode 100644 index 3fea9ec08ca1a..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,21 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Bluetooth BlueGiga Binding Tests -Bundle-SymbolicName: - org.openhab.binding.bluetooth.bluegiga.test;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Fragment-Host: org.openhab.binding.bluetooth.bluegiga -Import-Package: org.openhab.binding.bluetooth, - org.eclipse.smarthome.core.events, - org.eclipse.smarthome.core.thing.util, - org.eclipse.smarthome.test.java, - org.eclipse.smarthome.test.storage, - org.hamcrest;core=split, - org.junit;version="4.0.0", - org.mockito, - org.osgi.framework, - org.osgi.service.device, - org.slf4j -Automatic-Module-Name: org.openhab.binding.bluetooth.bluegiga.test diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/build.properties b/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/build.properties deleted file mode 100644 index 271c737ba0384..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/build.properties +++ /dev/null @@ -1,6 +0,0 @@ -source.. = src/test/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - NOTICE - diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/org.openhab.binding.bluetooth.bluegiga.test.launch b/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/org.openhab.binding.bluetooth.bluegiga.test.launch deleted file mode 100644 index cad834f6b3dbd..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/org.openhab.binding.bluetooth.bluegiga.test.launch +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/pom.xml b/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/pom.xml deleted file mode 100644 index e11783ad6efde..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/pom.xml +++ /dev/null @@ -1,119 +0,0 @@ - - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.bluetooth.bluegiga.test - - eclipse-test-plugin - - Bluetooth BlueGiga Tests - - - - - ${tycho-groupid} - target-platform-configuration - - - - - - eclipse-plugin - org.eclipse.equinox.event - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.ds - 0.0.0 - - - eclipse-plugin - org.openhab.core.config.xml - 0.0.0 - - - eclipse-plugin - org.openhab.core.thing.xml - 0.0.0 - - - eclipse-plugin - org.openhab.core.binding.xml - 0.0.0 - - - - - - - ${tycho-groupid} - tycho-surefire-plugin - - - - eclipse-plugin - ch.qos.logback.classic - 0.0.0 - - - eclipse-plugin - ch.qos.logback.core - 0.0.0 - - - eclipse-plugin - ch.qos.logback.slf4j - 0.0.0 - - - - - org.eclipse.equinox.ds - 1 - true - - - org.eclipse.equinox.event - 2 - true - - - org.openhab.core - 4 - true - - - org.openhab.core.thing - 4 - true - - - org.openhab.core.config.xml - 4 - true - - - org.openhab.core.thing.xml - 4 - true - - - org.openhab.core.binding.xml - 4 - true - - - - - - - - diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/.classpath b/addons/binding/org.openhab.binding.bluetooth.bluegiga/.classpath deleted file mode 100644 index 7f457fa4138d1..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluegiga/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/.project b/addons/binding/org.openhab.binding.bluetooth.bluegiga/.project deleted file mode 100644 index c966d044ca33f..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluegiga/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.bluetooth.bluegiga - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.bluetooth.bluegiga/META-INF/MANIFEST.MF deleted file mode 100644 index 2aac070bc07c4..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluegiga/META-INF/MANIFEST.MF +++ /dev/null @@ -1,30 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: BlueGiga Bluetooth Adapter -Bundle-SymbolicName: org.openhab.binding.bluetooth.bluegiga -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.core.common, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.io.transport.serial, - org.openhab.binding.bluetooth, - org.openhab.binding.bluetooth.bluegiga, - org.openhab.binding.bluetooth.bluegiga.handler, - org.openhab.binding.bluetooth.discovery, - org.openhab.binding.bluetooth.notification, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml -Export-Package: org.openhab.binding.bluetooth.bluegiga, - org.openhab.binding.bluetooth.bluegiga.handler -Automatic-Module-Name: org.openhab.binding.bluetooth.bluegiga diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.bluetooth.bluegiga/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluegiga/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/build.properties b/addons/binding/org.openhab.binding.bluetooth.bluegiga/build.properties deleted file mode 100644 index d3ebb210cc859..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluegiga/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -source..=src/main/java/ -output..=target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/pom.xml b/addons/binding/org.openhab.binding.bluetooth.bluegiga/pom.xml deleted file mode 100644 index 3e67b5c68384a..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluegiga/pom.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.bluetooth.bluegiga - eclipse-plugin - - BlueGiga Bluetooth Adapter - - diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/.classpath b/addons/binding/org.openhab.binding.bluetooth.bluez/.classpath deleted file mode 100644 index fdbb371ce27da..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluez/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/.project b/addons/binding/org.openhab.binding.bluetooth.bluez/.project deleted file mode 100644 index f55733257ef3a..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluez/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.bluetooth.bluez - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/86_64/libjavatinyb.so b/addons/binding/org.openhab.binding.bluetooth.bluez/86_64/libjavatinyb.so deleted file mode 100644 index 308abed357c2a..0000000000000 Binary files a/addons/binding/org.openhab.binding.bluetooth.bluez/86_64/libjavatinyb.so and /dev/null differ diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/86_64/libtinyb.so b/addons/binding/org.openhab.binding.bluetooth.bluez/86_64/libtinyb.so deleted file mode 100644 index 430f3b6c44495..0000000000000 Binary files a/addons/binding/org.openhab.binding.bluetooth.bluez/86_64/libtinyb.so and /dev/null differ diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.bluetooth.bluez/META-INF/MANIFEST.MF deleted file mode 100644 index ab345b621936b..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluez/META-INF/MANIFEST.MF +++ /dev/null @@ -1,38 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: BlueZ Bluetooth Adapter -Bundle-SymbolicName: - org.openhab.binding.bluetooth.bluez;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-ClassPath: ., - lib/tinyb-0.5.1.jar -Bundle-NativeCode: - lib/armv6hf/libjavatinyb.so;lib/armv6hf/libtinyb.so; - processor=arm;osname=linux, - * -Import-Package: org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.core.common, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.core.util, - org.openhab.binding.bluetooth, - org.openhab.binding.bluetooth.bluez, - org.openhab.binding.bluetooth.bluez.handler, - org.openhab.binding.bluetooth.discovery, - org.openhab.binding.bluetooth.notification, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml -Export-Package: org.openhab.binding.bluetooth.bluez, - org.openhab.binding.bluetooth.bluez.handler -Bundle-ActivationPolicy: lazy -Automatic-Module-Name: org.openhab.binding.bluetooth.bluez diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.bluetooth.bluez/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluez/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/build.properties b/addons/binding/org.openhab.binding.bluetooth.bluez/build.properties deleted file mode 100644 index 2846a025d2f8b..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluez/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -source..=src/main/java/ -output..=target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE,\ - lib/ diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/lib/armv6hf/libjavatinyb.so b/addons/binding/org.openhab.binding.bluetooth.bluez/lib/armv6hf/libjavatinyb.so deleted file mode 100644 index b1b5da17fc90b..0000000000000 Binary files a/addons/binding/org.openhab.binding.bluetooth.bluez/lib/armv6hf/libjavatinyb.so and /dev/null differ diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/lib/armv6hf/libtinyb.so b/addons/binding/org.openhab.binding.bluetooth.bluez/lib/armv6hf/libtinyb.so deleted file mode 100644 index bf444a7f101d5..0000000000000 Binary files a/addons/binding/org.openhab.binding.bluetooth.bluez/lib/armv6hf/libtinyb.so and /dev/null differ diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/lib/tinyb-0.5.1.jar b/addons/binding/org.openhab.binding.bluetooth.bluez/lib/tinyb-0.5.1.jar deleted file mode 100644 index dca6d7ad8bd83..0000000000000 Binary files a/addons/binding/org.openhab.binding.bluetooth.bluez/lib/tinyb-0.5.1.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/pom.xml b/addons/binding/org.openhab.binding.bluetooth.bluez/pom.xml deleted file mode 100644 index 4d66bc72cc17e..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.bluez/pom.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.bluetooth.bluez - eclipse-plugin - - BlueZ Bluetooth Adapter - - diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/.classpath b/addons/binding/org.openhab.binding.bluetooth.blukii/.classpath deleted file mode 100644 index 7f457fa4138d1..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.blukii/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/.project b/addons/binding/org.openhab.binding.bluetooth.blukii/.project deleted file mode 100644 index aeb861016a5c5..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.blukii/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.bluetooth.blukii - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.bluetooth.blukii/META-INF/MANIFEST.MF deleted file mode 100644 index e3493116d67a5..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.blukii/META-INF/MANIFEST.MF +++ /dev/null @@ -1,34 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Blukii Binding -Bundle-SymbolicName: org.openhab.binding.bluetooth.blukii -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-ClassPath: . -Import-Package: - javax.measure, - javax.measure.quantity, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.library.unit, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.binding.firmware, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.core.util, - org.openhab.binding.bluetooth, - org.openhab.binding.bluetooth.discovery, - org.openhab.binding.bluetooth.notification, - org.slf4j -Service-Component: OSGI-INF/*.xml -Export-Package: - org.openhab.binding.bluetooth.blukii, - org.openhab.binding.bluetooth.blukii.handler -Bundle-ActivationPolicy: lazy -Automatic-Module-Name: org.openhab.binding.bluetooth.blukii diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.bluetooth.blukii/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.blukii/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/build.properties b/addons/binding/org.openhab.binding.bluetooth.blukii/build.properties deleted file mode 100644 index a6cfff567c9b1..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.blukii/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -source..=src/main/java/ -output..=target/classes -bin.includes=META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/pom.xml b/addons/binding/org.openhab.binding.bluetooth.blukii/pom.xml deleted file mode 100644 index 57dff0b766f26..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.blukii/pom.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.bluetooth.blukii - eclipse-plugin - - Blukii Binding - - diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/.classpath b/addons/binding/org.openhab.binding.bluetooth.ruuvitag/.classpath deleted file mode 100644 index c519ec1b24a4a..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/.project b/addons/binding/org.openhab.binding.bluetooth.ruuvitag/.project deleted file mode 100644 index 474e2dd6c7181..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.bluetooth.ruuvitag - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.bluetooth.ruuvitag/META-INF/MANIFEST.MF deleted file mode 100644 index cdbc19c210087..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/META-INF/MANIFEST.MF +++ /dev/null @@ -1,31 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: RuuviTag Binding -Bundle-SymbolicName: org.openhab.binding.bluetooth.ruuvitag -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-ClassPath: ., - lib/ruuvitag-common-1.0.0.jar -Import-Package: - javax.measure, - javax.measure.quantity, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.library.unit, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.types, - org.openhab.binding.bluetooth, - org.openhab.binding.bluetooth.discovery, - org.openhab.binding.bluetooth.notification, - org.slf4j -Service-Component: OSGI-INF/*.xml -Export-Package: - org.openhab.binding.bluetooth.ruuvitag, - org.openhab.binding.bluetooth.ruuvitag.handler -Bundle-ActivationPolicy: lazy -Automatic-Module-Name: org.openhab.binding.bluetooth.ruuvitag diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.bluetooth.ruuvitag/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/build.properties b/addons/binding/org.openhab.binding.bluetooth.ruuvitag/build.properties deleted file mode 100644 index b939b44256240..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -source..=src/main/java/ -output..=target/classes -bin.includes=META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE,\ - lib/ruuvitag-common-1.0.0.jar diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/pom.xml b/addons/binding/org.openhab.binding.bluetooth.ruuvitag/pom.xml deleted file mode 100644 index 37e504713f59f..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/pom.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.bluetooth.ruuvitag - eclipse-plugin - - RuuviTag Binding - - diff --git a/addons/binding/org.openhab.binding.bluetooth.test/.classpath b/addons/binding/org.openhab.binding.bluetooth.test/.classpath deleted file mode 100644 index 64777f25c82a4..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.test/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.bluetooth.test/.project b/addons/binding/org.openhab.binding.bluetooth.test/.project deleted file mode 100644 index 346bafd98afb3..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.test/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.openhab.binding.bluetooth.test - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.bluetooth.test/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.bluetooth.test/META-INF/MANIFEST.MF deleted file mode 100644 index 899d5603b8568..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,21 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Bluetooth Binding Tests -Bundle-SymbolicName: - org.openhab.binding.bluetooth.test;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Fragment-Host: org.openhab.binding.bluetooth -Import-Package: org.eclipse.smarthome.core.events, - org.eclipse.smarthome.core.thing.util, - org.eclipse.smarthome.test.java, - org.eclipse.smarthome.test.storage, - org.hamcrest;core=split, - org.junit;version="4.0.0", - org.mockito, - org.openhab.binding.bluetooth, - org.osgi.framework, - org.osgi.service.device, - org.slf4j -Automatic-Module-Name: org.openhab.binding.bluetooth.test diff --git a/addons/binding/org.openhab.binding.bluetooth.test/build.properties b/addons/binding/org.openhab.binding.bluetooth.test/build.properties deleted file mode 100644 index 271c737ba0384..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.test/build.properties +++ /dev/null @@ -1,6 +0,0 @@ -source.. = src/test/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - NOTICE - diff --git a/addons/binding/org.openhab.binding.bluetooth.test/org.openhab.binding.bluetooth.test.launch b/addons/binding/org.openhab.binding.bluetooth.test/org.openhab.binding.bluetooth.test.launch deleted file mode 100644 index a7eeac182571f..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.test/org.openhab.binding.bluetooth.test.launch +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.bluetooth.test/pom.xml b/addons/binding/org.openhab.binding.bluetooth.test/pom.xml deleted file mode 100644 index b15315cdf12c2..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth.test/pom.xml +++ /dev/null @@ -1,119 +0,0 @@ - - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.bluetooth.test - - eclipse-test-plugin - - Bluetooth Binding Tests - - - - - ${tycho-groupid} - target-platform-configuration - - - - - - eclipse-plugin - org.eclipse.equinox.event - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.ds - 0.0.0 - - - eclipse-plugin - org.openhab.core.config.xml - 0.0.0 - - - eclipse-plugin - org.openhab.core.thing.xml - 0.0.0 - - - eclipse-plugin - org.openhab.core.binding.xml - 0.0.0 - - - - - - - ${tycho-groupid} - tycho-surefire-plugin - - - - eclipse-plugin - ch.qos.logback.classic - 0.0.0 - - - eclipse-plugin - ch.qos.logback.core - 0.0.0 - - - eclipse-plugin - ch.qos.logback.slf4j - 0.0.0 - - - - - org.eclipse.equinox.ds - 1 - true - - - org.eclipse.equinox.event - 2 - true - - - org.openhab.core - 4 - true - - - org.openhab.core.thing - 4 - true - - - org.openhab.core.config.xml - 4 - true - - - org.openhab.core.thing.xml - 4 - true - - - org.openhab.core.binding.xml - 4 - true - - - - - - - - diff --git a/addons/binding/org.openhab.binding.bluetooth/.classpath b/addons/binding/org.openhab.binding.bluetooth/.classpath deleted file mode 100644 index 7f457fa4138d1..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.bluetooth/.project b/addons/binding/org.openhab.binding.bluetooth/.project deleted file mode 100644 index 01c57782f5ec7..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.bluetooth - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.bluetooth/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.bluetooth/META-INF/MANIFEST.MF deleted file mode 100644 index ce4834d19a04c..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth/META-INF/MANIFEST.MF +++ /dev/null @@ -1,30 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Bluetooth Binding -Bundle-SymbolicName: - org.openhab.binding.bluetooth;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-ClassPath: . -Import-Package: - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.openhab.binding.bluetooth, - org.openhab.binding.bluetooth.discovery, - org.openhab.binding.bluetooth.notification, - org.slf4j -Service-Component: OSGI-INF/*.xml -Export-Package: org.openhab.binding.bluetooth, - org.openhab.binding.bluetooth.discovery, - org.openhab.binding.bluetooth.notification -Bundle-ActivationPolicy: lazy -Automatic-Module-Name: org.openhab.binding.bluetooth diff --git a/addons/binding/org.openhab.binding.bluetooth/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.bluetooth/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.bluetooth/build.properties b/addons/binding/org.openhab.binding.bluetooth/build.properties deleted file mode 100644 index a6cfff567c9b1..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -source..=src/main/java/ -output..=target/classes -bin.includes=META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.bluetooth/pom.xml b/addons/binding/org.openhab.binding.bluetooth/pom.xml deleted file mode 100644 index 7e96b9b1d9f6d..0000000000000 --- a/addons/binding/org.openhab.binding.bluetooth/pom.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.bluetooth - eclipse-plugin - - Bluetooth Binding - - diff --git a/addons/binding/org.openhab.binding.boschindego/.classpath b/addons/binding/org.openhab.binding.boschindego/.classpath deleted file mode 100644 index f32868aa4cb61..0000000000000 --- a/addons/binding/org.openhab.binding.boschindego/.classpath +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.boschindego/.gitignore b/addons/binding/org.openhab.binding.boschindego/.gitignore deleted file mode 100644 index 496ee2ca6a2f0..0000000000000 --- a/addons/binding/org.openhab.binding.boschindego/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.DS_Store \ No newline at end of file diff --git a/addons/binding/org.openhab.binding.boschindego/.project b/addons/binding/org.openhab.binding.boschindego/.project deleted file mode 100644 index 0ba62a861b635..0000000000000 --- a/addons/binding/org.openhab.binding.boschindego/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.boschindego - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.boschindego/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.boschindego/META-INF/MANIFEST.MF deleted file mode 100644 index 20de2a54648a5..0000000000000 --- a/addons/binding/org.openhab.binding.boschindego/META-INF/MANIFEST.MF +++ /dev/null @@ -1,32 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.boschindego -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: - ., - lib/bosch-indego-controller-lib-0.8.jar, - lib/httpclient-4.5.2.jar, - lib/httpcore-4.4.4.jar, - lib/jackson-annotations-2.8.6.jar, - lib/jackson-core-2.8.6.jar, - lib/jackson-databind-2.8.6.jar -Bundle-ManifestVersion: 2 -Bundle-Name: BoschIndego Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.boschindego;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - org.apache.commons.codec, - org.apache.commons.codec.binary, - org.apache.commons.logging, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.boschindego/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.boschindego/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.boschindego/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.boschindego/build.properties b/addons/binding/org.openhab.binding.boschindego/build.properties deleted file mode 100644 index 71f69309a27f0..0000000000000 --- a/addons/binding/org.openhab.binding.boschindego/build.properties +++ /dev/null @@ -1,14 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - lib/bosch-indego-controller-lib-0.8.jar,\ - lib/httpclient-4.5.2.jar,\ - lib/httpcore-4.4.4.jar,\ - lib/jackson-annotations-2.8.6.jar,\ - lib/jackson-core-2.8.6.jar,\ - lib/jackson-databind-2.8.6.jar,\ - NOTICE - diff --git a/addons/binding/org.openhab.binding.boschindego/lib/bosch-indego-controller-lib-0.8.jar b/addons/binding/org.openhab.binding.boschindego/lib/bosch-indego-controller-lib-0.8.jar deleted file mode 100644 index 1c2394f1404e3..0000000000000 Binary files a/addons/binding/org.openhab.binding.boschindego/lib/bosch-indego-controller-lib-0.8.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.boschindego/lib/httpclient-4.5.2.jar b/addons/binding/org.openhab.binding.boschindego/lib/httpclient-4.5.2.jar deleted file mode 100644 index 701609fcc8469..0000000000000 Binary files a/addons/binding/org.openhab.binding.boschindego/lib/httpclient-4.5.2.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.boschindego/lib/httpcore-4.4.4.jar b/addons/binding/org.openhab.binding.boschindego/lib/httpcore-4.4.4.jar deleted file mode 100644 index ac4a8773022b9..0000000000000 Binary files a/addons/binding/org.openhab.binding.boschindego/lib/httpcore-4.4.4.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.boschindego/lib/jackson-annotations-2.8.6.jar b/addons/binding/org.openhab.binding.boschindego/lib/jackson-annotations-2.8.6.jar deleted file mode 100644 index bf07bcb1cf645..0000000000000 Binary files a/addons/binding/org.openhab.binding.boschindego/lib/jackson-annotations-2.8.6.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.boschindego/lib/jackson-core-2.8.6.jar b/addons/binding/org.openhab.binding.boschindego/lib/jackson-core-2.8.6.jar deleted file mode 100644 index 2b10bf3030f99..0000000000000 Binary files a/addons/binding/org.openhab.binding.boschindego/lib/jackson-core-2.8.6.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.boschindego/lib/jackson-databind-2.8.6.jar b/addons/binding/org.openhab.binding.boschindego/lib/jackson-databind-2.8.6.jar deleted file mode 100644 index 2784d1f827605..0000000000000 Binary files a/addons/binding/org.openhab.binding.boschindego/lib/jackson-databind-2.8.6.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.boschindego/pom.xml b/addons/binding/org.openhab.binding.boschindego/pom.xml deleted file mode 100644 index 543b86236efdc..0000000000000 --- a/addons/binding/org.openhab.binding.boschindego/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.boschindego - eclipse-plugin - - BoschIndego Binding - - diff --git a/addons/binding/org.openhab.binding.chromecast.test/.classpath b/addons/binding/org.openhab.binding.chromecast.test/.classpath deleted file mode 100644 index 64777f25c82a4..0000000000000 --- a/addons/binding/org.openhab.binding.chromecast.test/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.chromecast.test/.project b/addons/binding/org.openhab.binding.chromecast.test/.project deleted file mode 100644 index ac3ede2174e88..0000000000000 --- a/addons/binding/org.openhab.binding.chromecast.test/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.openhab.binding.chromecast.test - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.chromecast.test/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.chromecast.test/META-INF/MANIFEST.MF deleted file mode 100644 index 660292638ab07..0000000000000 --- a/addons/binding/org.openhab.binding.chromecast.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,30 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.chromecast.test -Bundle-ActivationPolicy: lazy -Bundle-ManifestVersion: 2 -Bundle-Name: Chromecast Binding Tests -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.chromecast.test;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Fragment-Host: org.openhab.binding.chromecast -Import-Package: - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.events, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.util, - org.eclipse.smarthome.test.java, - org.eclipse.smarthome.test.storage, - org.hamcrest;core=split, - org.hamcrest.core, - org.junit;version="4.0.0", - org.mockito, - org.mockito.invocation, - org.mockito.stubbing, - org.osgi.framework, - org.osgi.service.component, - org.osgi.service.device, - org.slf4j diff --git a/addons/binding/org.openhab.binding.chromecast.test/build.properties b/addons/binding/org.openhab.binding.chromecast.test/build.properties deleted file mode 100644 index 64491e243ba70..0000000000000 --- a/addons/binding/org.openhab.binding.chromecast.test/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -source.. = src/test/java/ -output.. = target/test-classes -bin.includes = META-INF/,\ - .,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.chromecast.test/org.openhab.binding.chromecast.test.launch b/addons/binding/org.openhab.binding.chromecast.test/org.openhab.binding.chromecast.test.launch deleted file mode 100644 index aab0549699ce3..0000000000000 --- a/addons/binding/org.openhab.binding.chromecast.test/org.openhab.binding.chromecast.test.launch +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.chromecast.test/pom.xml b/addons/binding/org.openhab.binding.chromecast.test/pom.xml deleted file mode 100644 index 0dc2f31230cb8..0000000000000 --- a/addons/binding/org.openhab.binding.chromecast.test/pom.xml +++ /dev/null @@ -1,158 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.chromecast.test - eclipse-test-plugin - - Chromecast Binding Tests - - - - - ${tycho-groupid} - target-platform-configuration - - - - - eclipse-plugin - org.eclipse.equinox.ds - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.event - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.http.jetty - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.http.servlet - 0.0.0 - - - eclipse-plugin - org.eclipse.jetty.http - 0.0.0 - - - eclipse-plugin - org.eclipse.jetty.osgi.httpservice - 0.0.0 - - - eclipse-plugin - org.eclipse.jetty.server - 0.0.0 - - - eclipse-plugin - org.eclipse.jetty.servlet - 0.0.0 - - - eclipse-plugin - org.openhab.core.config.core - 0.0.0 - - - eclipse-plugin - org.openhab.core - 0.0.0 - - - eclipse-plugin - org.openhab.core.thing - 0.0.0 - - - eclipse-plugin - org.openhab.core.thing.xml - 0.0.0 - - - - - - - ${tycho-groupid} - tycho-surefire-plugin - - - - org.eclipse.equinox.ds - 1 - true - - - org.eclipse.equinox.event - 2 - true - - - org.eclipse.equinox.http.jetty - 4 - true - - - org.eclipse.equinox.http.servlet - 4 - true - - - org.eclipse.jetty.http - 4 - true - - - org.eclipse.jetty.osgi.httpservice - 4 - true - - - org.eclipse.jetty.server - 4 - true - - - org.eclipse.jetty.servlet - 4 - true - - - org.openhab.core.config.core - 4 - true - - - org.openhab.core - 4 - true - - - org.openhab.core.thing - 4 - true - - - org.openhab.core.thing.xml - 4 - true - - - - - - - diff --git a/addons/binding/org.openhab.binding.chromecast.test/src/test/java/org/openhab/binding/chromecast/internal/handler/ChromecastOSGiTest.java b/addons/binding/org.openhab.binding.chromecast.test/src/test/java/org/openhab/binding/chromecast/internal/handler/ChromecastOSGiTest.java deleted file mode 100644 index e6258afdfefec..0000000000000 --- a/addons/binding/org.openhab.binding.chromecast.test/src/test/java/org/openhab/binding/chromecast/internal/handler/ChromecastOSGiTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.chromecast.internal.handler; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; -import static org.openhab.binding.chromecast.internal.ChromecastBindingConstants.*; - -import org.eclipse.smarthome.config.core.Configuration; -import org.eclipse.smarthome.core.thing.ManagedThingProvider; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingProvider; -import org.eclipse.smarthome.core.thing.binding.ThingHandler; -import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder; -import org.eclipse.smarthome.test.java.JavaOSGiTest; -import org.eclipse.smarthome.test.storage.VolatileStorageService; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * Tests for {@link ChromecastHandler}. - * - * @author François Pelsser, Wouter Born - Initial contribution - */ -public class ChromecastOSGiTest extends JavaOSGiTest { - - private ManagedThingProvider managedThingProvider; - private VolatileStorageService volatileStorageService = new VolatileStorageService(); - - @Before - public void setUp() { - registerService(volatileStorageService); - - managedThingProvider = getService(ThingProvider.class, ManagedThingProvider.class); - assertThat(managedThingProvider, is(notNullValue())); - } - - @After - public void tearDown() { - unregisterService(volatileStorageService); - } - - @Test - public void creationOfChromecastHandler() { - ChromecastHandler handler = getService(ThingHandler.class, ChromecastHandler.class); - assertThat(handler, is(nullValue())); - - Configuration configuration = new Configuration(); - configuration.put(HOST, "hostname"); - - Thing thing = ThingBuilder.create(THING_TYPE_CHROMECAST, "tv").withConfiguration(configuration).build(); - managedThingProvider.add(thing); - - waitForAssert(() -> assertThat(thing.getHandler(), notNullValue())); - assertThat(thing.getConfiguration(), is(notNullValue())); - assertThat(thing.getHandler(), is(instanceOf(ChromecastHandler.class))); - } -} diff --git a/addons/binding/org.openhab.binding.chromecast/.classpath b/addons/binding/org.openhab.binding.chromecast/.classpath deleted file mode 100644 index d87cc26b5634f..0000000000000 --- a/addons/binding/org.openhab.binding.chromecast/.classpath +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.chromecast/.project b/addons/binding/org.openhab.binding.chromecast/.project deleted file mode 100644 index aae99edb3088b..0000000000000 --- a/addons/binding/org.openhab.binding.chromecast/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.chromecast - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.chromecast/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.chromecast/META-INF/MANIFEST.MF deleted file mode 100644 index 8e51de761cde7..0000000000000 --- a/addons/binding/org.openhab.binding.chromecast/META-INF/MANIFEST.MF +++ /dev/null @@ -1,37 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.chromecast -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: - ., - lib/protobuf-java-2.6.0.jar, - lib/api-v2-0.10.6.jar, - lib/jackson-core-asl-1.9.2.jar, - lib/jackson-mapper-asl-1.9.2.jar -Bundle-ManifestVersion: 2 -Bundle-Name: Chromecast Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.chromecast;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - javax.jmdns, - javax.measure.quantity, - org.apache.commons.lang, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.config.discovery.mdns, - org.eclipse.smarthome.core.audio, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.library.unit, - org.eclipse.smarthome.core.net, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.io.net.http, - org.osgi.framework, - org.osgi.service.component, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.chromecast/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.chromecast/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.chromecast/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.chromecast/build.properties b/addons/binding/org.openhab.binding.chromecast/build.properties deleted file mode 100644 index f3b3ec62e305b..0000000000000 --- a/addons/binding/org.openhab.binding.chromecast/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - lib/,\ - NOTICE -source.. = src/main/java/ diff --git a/addons/binding/org.openhab.binding.chromecast/lib/api-v2-0.10.6.jar b/addons/binding/org.openhab.binding.chromecast/lib/api-v2-0.10.6.jar deleted file mode 100644 index 6418219b03010..0000000000000 Binary files a/addons/binding/org.openhab.binding.chromecast/lib/api-v2-0.10.6.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.chromecast/lib/jackson-core-asl-1.9.2.jar b/addons/binding/org.openhab.binding.chromecast/lib/jackson-core-asl-1.9.2.jar deleted file mode 100644 index c50656724bba0..0000000000000 Binary files a/addons/binding/org.openhab.binding.chromecast/lib/jackson-core-asl-1.9.2.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.chromecast/lib/jackson-mapper-asl-1.9.2.jar b/addons/binding/org.openhab.binding.chromecast/lib/jackson-mapper-asl-1.9.2.jar deleted file mode 100644 index 6407dc05911e1..0000000000000 Binary files a/addons/binding/org.openhab.binding.chromecast/lib/jackson-mapper-asl-1.9.2.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.chromecast/lib/protobuf-java-2.6.0.jar b/addons/binding/org.openhab.binding.chromecast/lib/protobuf-java-2.6.0.jar deleted file mode 100644 index 4bfbd4b71abcc..0000000000000 Binary files a/addons/binding/org.openhab.binding.chromecast/lib/protobuf-java-2.6.0.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.chromecast/pom.xml b/addons/binding/org.openhab.binding.chromecast/pom.xml deleted file mode 100644 index 0b0c274de0e39..0000000000000 --- a/addons/binding/org.openhab.binding.chromecast/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.chromecast - eclipse-plugin - - Chromecast Binding - - diff --git a/addons/binding/org.openhab.binding.dsmr.test/.classpath b/addons/binding/org.openhab.binding.dsmr.test/.classpath deleted file mode 100644 index a0838479442e1..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr.test/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/addons/binding/org.openhab.binding.dsmr.test/.project b/addons/binding/org.openhab.binding.dsmr.test/.project deleted file mode 100644 index 62c24d5f1d135..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr.test/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.openhab.binding.dsmr.test - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.dsmr.test/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.dsmr.test/META-INF/MANIFEST.MF deleted file mode 100644 index 30f6f17cf7443..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,38 +0,0 @@ -Automatic-Module-Name: org.openhab.binding.dsmr.test -Manifest-Version: 1.0 -Bundle-ClassPath: . -Bundle-ManifestVersion: 2 -Bundle-Name: DSMR Binding Tests -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.dsmr.test;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Fragment-Host: org.openhab.binding.dsmr -Import-Package: - org.apache.commons.io, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.core.common, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.events, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.util, - org.eclipse.smarthome.io.transport.serial, - org.eclipse.smarthome.test, - org.eclipse.smarthome.test.java, - org.eclipse.smarthome.test.storage, - org.hamcrest;core=split, - org.hamcrest.core, - org.junit, - org.mockito, - org.mockito.hamcrest, - org.mockito.invocation, - org.mockito.stubbing, - org.osgi.framework, - org.osgi.service.component, - org.osgi.service.device, - org.slf4j -Require-Bundle: - org.hamcrest, - org.junit diff --git a/addons/binding/org.openhab.binding.dsmr.test/build.properties b/addons/binding/org.openhab.binding.dsmr.test/build.properties deleted file mode 100644 index ce15f22529e21..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr.test/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -source.. = src/test/java/,src/test/resources/ -output.. = target/test-classes -bin.includes = META-INF/,\ - .,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.dsmr.test/org.openhab.binding.dsmr.test.launch b/addons/binding/org.openhab.binding.dsmr.test/org.openhab.binding.dsmr.test.launch deleted file mode 100644 index 6bac0ee1f82ba..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr.test/org.openhab.binding.dsmr.test.launch +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.dsmr.test/pom.xml b/addons/binding/org.openhab.binding.dsmr.test/pom.xml deleted file mode 100644 index e961c9090d668..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr.test/pom.xml +++ /dev/null @@ -1,125 +0,0 @@ - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.dsmr.test - eclipse-test-plugin - - DSMR Binding Tests - - - - - ${tycho-groupid} - target-platform-configuration - - - - - eclipse-plugin - org.eclipse.equinox.ds - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.event - 0.0.0 - - - eclipse-plugin - org.openhab.core.config.discovery - 0.0.0 - - - eclipse-plugin - org.openhab.core.config.xml - 0.0.0 - - - eclipse-plugin - org.openhab.core.thing.xml - 0.0.0 - - - eclipse-plugin - org.openhab.core.binding.xml - 0.0.0 - - - - - - - ${tycho-groupid} - tycho-surefire-plugin - - - - eclipse-plugin - ch.qos.logback.classic - 0.0.0 - - - eclipse-plugin - ch.qos.logback.core - 0.0.0 - - - eclipse-plugin - ch.qos.logback.slf4j - 0.0.0 - - - - - org.eclipse.equinox.ds - 1 - true - - - org.eclipse.equinox.event - 2 - true - - - org.openhab.core - 4 - true - - - org.openhab.core.thing - 4 - true - - - org.openhab.core.config.discovery - 4 - true - - - org.openhab.core.config.xml - 4 - true - - - org.openhab.core.core.thing - 4 - true - - - org.openhab.core.core.thing.xml - 4 - true - - - - - - - - diff --git a/addons/binding/org.openhab.binding.dsmr/.classpath b/addons/binding/org.openhab.binding.dsmr/.classpath deleted file mode 100644 index 7f457fa4138d1..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.dsmr/.project b/addons/binding/org.openhab.binding.dsmr/.project deleted file mode 100644 index da5034fb07f73..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.dsmr - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/binding/binding.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/binding/binding.xml deleted file mode 100644 index c3c815539778c..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/binding/binding.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - DSMR Binding - This binding integrates Dutch Smart Meters - Marcel Volaart, Hilbrand Bouwkamp - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_en.properties b/addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_en.properties deleted file mode 100644 index 2243333db9bb3..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_en.properties +++ /dev/null @@ -1,26 +0,0 @@ -# Text used in the source code. - -# meter kind names -meterKind.invalid.label = Invalid Meter -meterKind.device.label = Generic DSMR Device -meterKind.main_electricity.label = Main Electricity Meter -meterKind.gas.label = Gas Meter -meterKind.heating.label = Heating Meter -meterKind.cooling.label = Cooling Meter -meterKind.water.label = Water Meter -meterKind.generic.label = Generic Meter -meterKind.gj.label = GJ Meter -meterKind.m3.label = M3 Meter -meterKind.slave_electricity1.label = Slave Electricity Meter -meterKind.slave_electricity2.label = Slave Electricity Meter 2 - -# Connector error messages -error.bridge.nodata = Not receiving data from meter. -error.configuration.invalidmetertype = The thing could not be initialized. Delete and re-add thing if the problem persists. -error.configuration.incomplete = Incomplete configuration. Not all required configuration settings are set. -error.thing.nodata = Not receiving data from meter. -error.connector.dont_exists = Serial port does not exist. -error.connector.in_use = Serial port is already in use. -error.connector.internal_error = Unexpected error, possible bug. Please report. -error.connector.not_compatible = Serial port is not compatible. -error.connector.read_error = Read error. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_electricity.xml b/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_electricity.xml deleted file mode 100644 index 271930c0d85ac..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_electricity.xml +++ /dev/null @@ -1,249 +0,0 @@ - - - - - Number:Energy - - The total amount of electricity used. - - - - Number:Energy - - The total amount of electricity used for tariff 0. - - - - Number:Energy - - The total amount of electricity used for tariff 1. - - - - Number:Energy - - The total amount of electricity used for tariff 2. - - - - Number:Energy - - The total amount of electricity used for tariff 0 (anti fraud). - - - - Number - - The total amount of electricity used for tariff 1 (anti fraud). - - - - Number:Energy - - The total amount of electricity used for tariff 2 (anti fraud). - - - - Number:Energy - - The total amount of electricity produced for tariff 0. - - - - Number:Energy - - The total amount of electricity produced for tariff 1. - - - - Number:Energy - - The total amount of electricity produced for tariff 2. - - - - String - - The current tariff indicator. - - - - Number:Power - - The aggregate active import power. - - - - Number:Power - - The current power delivery. - - - - Number:Power - - The current power production. - - - - Number:ElectricCurrent - - The actual threshold. - - - - Number:Power - - The actual threshold. - - - - Number - - The switch position. - - - - Number - - The number of power failures. - - - - Number - - The number of long power failures. - - - - Number - - Number of log entries. - - - - DateTime - - Timestamp when the power failure ended. There can be multiple log entries. -Each entry has its own channel (emeter_power_failure_log_timestamp*x*, *x* = 0 - 9) - - - - Number:Time - - Duration of the power failure. -Each entry has its own channel (emeter_power_failure_log_duration*x*, *x* = 0 - 9) - - - - Number - - The number of voltage sags L1. - - - - Number - - The number of voltage sags L2. - - - - Number - - The number of voltage sags L3. - - - - Number - - The number of voltage swells L1. - - - - Number - - The number of voltage swells L2. - - - - Number - - The number of voltage swells L3. - - - - Number:ElectricCurrent - - The instant current L1. - - - - Number:ElectricCurrent - - The instant current L2. - - - - Number:ElectricCurrent - - The instant current L3. - - - - Number:Power - - The instant power delivery L1. - - - - Number:Power - - The instant power delivery L2. - - - - Number - - The instant power delivery L3. - - - - Number:Power - - The instant power production L1. - - - - Number:Power - - The instant power production L2. - - - - Number:Power - - The instant power production L3. - - - - Number:ElectricPotential - - The instant voltage L1. - - - - Number:ElectricPotential - - The instant voltage L2. - - - - Number:ElectricPotential - - The instant voltage L3. - - - diff --git a/addons/binding/org.openhab.binding.dsmr/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.dsmr/META-INF/MANIFEST.MF deleted file mode 100644 index 3b4e94a3ca023..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr/META-INF/MANIFEST.MF +++ /dev/null @@ -1,30 +0,0 @@ -Automatic-Module-Name: org.openhab.binding.dsmr -Manifest-Version: 1.0 -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: . -Bundle-ManifestVersion: 2 -Bundle-Name: DSMR Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.dsmr;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - javax.measure, - javax.measure.quantity, - org.apache.commons.lang, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.core.common, - org.eclipse.smarthome.core.i18n, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.library.unit, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.io.transport.serial, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.dsmr/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.dsmr/OSGI-INF/.gitignore deleted file mode 100644 index 6722cd96e785a..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.xml diff --git a/addons/binding/org.openhab.binding.dsmr/README.md b/addons/binding/org.openhab.binding.dsmr/README.md deleted file mode 100644 index 4cb7786517c1e..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr/README.md +++ /dev/null @@ -1,302 +0,0 @@ -# DSMR Binding - -The DSMR-binding is targeted towards Dutch users having a smart meter (Dutch: 'Slimme meter'). -Data of Dutch smart meters can be obtained via the P1-port. -When connecting this port from a serial port the data can be read out. - -This binding reads the P1-port of the Dutch Smart Meters that comply to NTA8130, DSMR v2.1, DSMR v2.2, DSMR v3.0, DSMR v4.0, DSMR v4.04, and DSMR 5.0. -Although DSMR v4.2 is not an official specification, the binding has support for this version. - -If you are not living in the Netherlands but do want to read a meter please have look at the [SmartMeter Binding](https://www.openhab.org/addons/bindings/smartmeter). -Because the Dutch Meter standard is based on the IEC-62056-21 standard it might be desirable to build support for other country metering systems based on that standard in this binding. - -## Serial Port Configuration - -The P1-port is a serial port. To configure the serial port within openHAB see the [general documentation about serial port configuration](/docs/administration/serial.html). - -## Supported Things - -`dsmrBridge`: This is the device that communicated between the binding (serial) and its internal meters. -You always have to have a 'Dutch Smart Meter'-bridge. The bridge contains the serial port configuration. -Specific meters are bound via the bridge to the smart meter. A smart meter consists typically out of minimal 2 meters. -A generic meter and the electricity meter. Each meter is bound to the DSMR protocol the physical meter supports. -For each meter it is possible to set a refresh rate at which the status is updated. -The physical meter might update with a high frequency per second, while it's desired to have only values per minute. - -## Discovery - -Both bridges and meters can be discovered via the discovery process. -If a bridge is manually configured it is possible to auto detect available meters. - -### Configuration - -The configuration for the Bridge consists of the following parameters: - -| Parameter | Description | -|---------------------|-------------------------------------------------------------------------------------------------------------| -| serialPort | The serial port where the P1-port is connected to (e.g. Linux: `/dev/ttyUSB1`, Windows: `COM2`) (mandatory) | -| receivedTimeout | The time out period in which messages are expected to arrive, default is 120 seconds. | -| baudrate | Baudrate when no auto detect. valid values: 4800, 9600, 19200, 38400, 57600, 115200 | -| databits | Data bits when no auto detect. valid values: 5, 6, 7, 8 | -| parity | Parity when no auto detect. valid values: E(ven), N(one), O(dd) | -| stopbits | Stop bits when no auto detect. valid values: 1, 1.5, 2 | - -**Note:** *The manual configuration is only needed if the DSMR-device requires non DSMR-standard Serial Port parameters (i.e. something different then `115200 8N1` or `9600 7E1`)* - -### Troubleshooting - -If there are unexpected configuration issues. -For example a meter could not be found or not all channels expected are available. -Than run the discovery process and look into the log file. -There are extra checks that can give more information about what might be wrong. - -## Meters - -The information in this paragraph in necessary if you choose to configure the meters manually in a `.things` file. - -Supported meters: - -| Meter Thing | Thing type ID | M-Bus channel | Refresh rate | -|-------------------------------------------------|------------------------------|---------------|--------------| -| DSMR V2 / V3 Device | `device_v2_v3` | -1 | 10 seconds | -| DSMR V4 Device | `device_v4` | -1 | 10 seconds | -| DSMR V5 Device | `device_v5` | -1 | 10 seconds | -| ACE4000 GTMM Mk3 Electricity meter | `electricity_ace4000` | 0 | 10 seconds | -| DSMR V2.1 Electricity meter | `electricity_v2_1` | 0 | 10 seconds | -| DSMR V2.2 Electricity meter | `electricity_v2_2` | 0 | 10 seconds | -| DSMR V3 Electricity meter | `electricity_v3_0` | 0 | 10 seconds | -| DSMR V4.0 Electricity meter | `electricity_v4_0` | 0 | 10 seconds | -| DSMR V4.0.4 Electricity meter | `electricity_v4_0_4` | 0 | 10 seconds | -| DSMR V4.2 Electricity meter | `electricity_v4_2` | 0 | 10 seconds | -| DSMR V5 Electricity meter | `electricity_v5_0` | 0 | 10 seconds | -| ACE4000 GTMM Mk3 Gas meter | `gas_ace4000` | 3 | 1 hour | -| DSMR V2.1 Gas meter | `gas_v2_1` | 0 | 24 hours | -| DSMR V2.2 Gas meter | `gas_v2_2` | 0 | 24 hours | -| DSMR V3.0 Gas meter | `gas_v3_0` | *note 1* | 1 hour | -| ACE4000 GTMM Mk3 Cooling meter | `cooling_ace4000` | 6 | 1 hour | -| DSMR V2.2 Cooling meter | `cooling_v2_2` | 0 | 1 hour | -| ACE4000 GTMM Mk3 Heating meter | `heating_ace4000` | 4 | 1 hour | -| DSMR V2.2 Heating meter | `heating_v2_2` | 0 | 1 hour | -| ACE4000 GTMM Mk3 Water meter | `water_ace4000` | 5 | 1 hour | -| DSMR V2.2 Water meter | `water_v2_2` | 0 | 1 hour | -| DSMR V3.0 Water meter | `water_v3_0` | *note 1* | 1 hour | -| ACE4000 GTMM Mk3 1st Slave Electricity meter | `slave_electricity1_ace4000` | 1 | 1 hour | -| ACE4000 GTMM Mk3 2nd Slave Electricity meter | `slave_electricity2_ace4000` | 2 | 1 hour | -| DSMR V4.x Slave Electricity meter | `slave_electricity_v4` | *note 1* | 1 hour | -| DSMR V5 Slave Electricity meter | `slave_electricity_v5` | *note 1* | 5 minutes | -| DSMR V3.0 Generic meter | `generic_v3_0` | *note 1* | 1 hour | -| DSMR V3.0 Giga Joule meter (heating or cooling) | `gj_v3_0` | *note 1* | 1 hour | -| DSMR V4.x Giga Joule meter (heating or cooling) | `gj_v4` | *note 1* | 1 hour | -| DSMR V5 Giga Joule meter (heating or cooling) | `gj_v5_0` | *note 1* | 5 minutes | -| DSMR V4.x m3 meter (gas or water) | `m3_v4` | *note 1* | 1 hour | -| DSMR V5 m3 meter (gas or water) | `m3_v5_0` | *note 1* | 5 minutes | - -*note 1*. The channel of these meters is dependent on the physical installation and corresponds to the M-Bus channel. -You can ask your supplier / installer for this information or you can retrieve it from the logfiles (see *Determine M-Bus channel*). - -#### Configuration - -The configuration for the meters consists of the following parameters: - -| Parameter | Description | -|---------------------|--------------------------------------------------------------------------------------| -| refresh | Time in seconds with which the state of the device is updated. Default is 60 seconds | -| channel | M-Bus channel. See the table above | - - -**Examples** - -``` -Bridge dsmr:dsmrBridge:myDSMRDevice [serialPort="/dev/ttyUSB0"] { - Things: - device_v5 dsmrDeviceV5 [channel=-1] - electricity_v5_0 electricityV5 [channel=0] -} -``` - -### Channels - -#### Item configuration - -Paper UI. Item configuration can be done in the regular way. - -Manual configuration: -The following channels are supported: - -- Y channel is supported -- \- channel is not supported -- O channel is supported only if the device has this functionality - - -| Channel Type ID | Item Type | Description | Ace4000 | DSMR V2.1 | DSMR V2.2 | DSMR V3.0 | DSMR V4.0 | DSMR V4.0.4 | DSMR V4.2 | DSMR V5 | -|--------------------------------------------------|-----------|------------------------------------------------------------------------|---------|-----------|-----------|-----------|-----------|-------------|-----------|---------| -| | | **Channels for the generic device** | | | | | | | | | -| `p1_text_code` | String | Text code from the device | - | Y | Y | Y | Y | Y | Y | - | -| `p1_text_string` | String | Text string from the device | - | Y | Y | Y | Y | Y | Y | Y | -| `p1_version_output` | String | Version information (most times this refers to the DSMR specification) | - | - | - | - | Y | Y | Y | Y | -| `p1_timestamp` | DateTime | Timestamp of the last device reading | - | - | - | - | Y | Y | Y | Y | -| | | **Channels for the cooling meter** | | | | | | | | | -| `cmeter_value_v2` | Number | The total amount of cooling used in the past period (GJ) | Y | - | Y | - | - | - | - | - | -| `cmeter_value_v2_timestamp` | DateTime | Timestamp of the last meter reading | Y | - | Y | - | - | - | - | - | -| `cmeter_equipment_identifier_v2_2` | String | Equipment identifier | - | - | Y | - | - | - | - | - | -| | | **Channels for the main electricity meter** | | | | | | | | | -| `emeter_equipment_identifier_v2_x` | String | Electricity Equipment identifier | - | Y | Y | - | - | - | - | - | -| `emeter_equipment_identifier` | String | Electricity Equipment identifier | - | - | - | Y | Y | Y | Y | Y | -| `emeter_delivery_tariff0` | Number | Total amount of electricity used for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_delivery_tariff1` | Number | Total amount of electricity used for tariff 1 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | -| `emeter_delivery_tariff2` | Number | Total amount of electricity used for tariff 2 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | -| `emeter_production_tariff0` | Number | Total amount of electricity produced for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_production_tariff1` | Number | Total amount of electricity produced for tariff 1 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | -| `emeter_production_tariff2` | Number | Total amount of electricity produced for tariff 2 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | -| `emeter_delivery_tariff0_antifraud` | Number | Total amount of electricity used for tariff 2 [antifraud] (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_delivery_tariff1_antifraud` | Number | Total amount of electricity used for tariff 1 [antifraud] (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_delivery_tariff2_antifraud` | Number | Total amount of electricity used for tariff 2 [antifraud] (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_tariff_indicator` | String | Current tariff indicator | Y | Y | Y | Y | Y | Y | Y | Y | -| `emeter_treshold_a_v2_1` | Number | Actual treshold (A) | - | Y | - | - | - | - | - | - | -| `emeter_treshold_a` | Number | Actual treshold (A) | Y | - | Y | Y | - | - | - | - | -| `emeter_treshold_kwh` | Number | Actual treshold (kW) | - | - | - | - | Y | Y | - | - | -| `emeter_switch_position_v2_1` | Number | Switch position | - | Y | - | - | - | - | - | - | -| `emeter_switch_position` | Number | Switch position | Y | - | Y | Y | Y | Y | - | - | -| `emeter_active_import_power` | Number | Aggregate active import power (W) | Y | - | - | - | - | - | - | - | -| `emeter_actual_delivery` | Number | Current power delivery (kW) | - | Y | Y | Y | Y | Y | Y | Y | -| `emeter_actual_production` | Number | Current power production (kW) | - | - | - | Y | Y | Y | Y | Y | -| `emeter_power_failures` | Number | Number of power failures | - | - | - | - | Y | Y | Y | Y | -| `emeter_long_power_failures` | Number | Number of long power failures | - | - | - | - | Y | Y | Y | Y | -| `emeter_power_failure_log_entries` | Number | Number of entries in the power failure log | - | - | - | - | Y | Y | Y | Y | -| `emeter_power_failure_log_timestamp[x]` *note 2* | Number | Number of entries in the power failure log | - | - | - | - | Y | Y | Y | Y | -| `emeter_power_failure_log_duration[x]` *note 2* | Number | Number of entries in the power failure log | - | - | - | - | Y | Y | Y | Y | -| `emeter_voltage_sags_l1` | Number | Number of voltage sags L1 | - | - | - | - | Y | Y | Y | Y | -| `emeter_voltage_sags_l2` | Number | Number of voltage sags L2 | - | - | - | - | O | O | O | O | -| `emeter_voltage_sags_l3` | Number | Number of voltage sags L3 | - | - | - | - | O | O | O | O | -| `emeter_voltage_swells_l1` | Number | Number of voltage swells L1 | - | - | - | - | Y | Y | Y | Y | -| `emeter_voltage_swells_l2` | Number | Number of voltage swells L2 | - | - | - | - | O | O | O | O | -| `emeter_voltage_swells_l3` | Number | Number of voltage swells L3 | - | - | - | - | O | O | O | O | -| `emeter_instant_current_l1` | Number | Instant Current L1 (A) | - | - | - | - | Y | Y | Y | Y | -| `emeter_instant_current_l2` | Number | Instant Current L2 (A) | - | - | - | - | O | O | O | O | -| `emeter_instant_current_l3` | Number | Instant Current L3 (A) | - | - | - | - | O | O | O | O | -| `emeter_instant_power_delivery_l1` | Number | Instant Power Delivery L1 (kW) | - | - | - | - | Y | Y | Y | Y | -| `emeter_instant_power_delivery_l2` | Number | Instant Power Delivery L2 (kW) | - | - | - | - | O | O | O | O | -| `emeter_instant_power_delivery_l3` | Number | Instant Power Delivery L3 (kW) | - | - | - | - | O | O | O | O | -| `emeter_instant_power_production_l1` | Number | Instant Power Production L1 (kW) | - | - | - | - | Y | Y | Y | Y | -| `emeter_instant_power_production_l2` | Number | Instant Power Production L2 (kW) | - | - | - | - | O | O | O | O | -| `emeter_instant_power_production_l3` | Number | Instant Power Production L3 (kW) | - | - | - | - | O | O | O | O | -| `emeter_instant_voltage_l1` | Number | Instant Voltage L1 (V) | - | - | - | - | - | - | - | Y | -| `emeter_instant_voltage_l2` | Number | Instant Voltage L2 (V) | - | - | - | - | - | - | - | O | -| `emeter_instant_voltage_l3` | Number | Instant Voltage L3 (V) | - | - | - | - | - | - | - | O | -| | | **Channels for the slave electricity meter** | | | | | | | | | -| `meter_device_type` | String | Slave Electricity Meter Device Type | - | - | - | - | Y | Y | Y | Y | -| `meter_equipment_identifier` | String | Slave Electricity Meter ID | - | - | - | - | Y | Y | Y | Y | -| `emeter_delivery_tariff0` | Number | Total amount of slave electricity used for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_delivery_tariff1` | Number | Total amount of slave electricity used for tariff 1 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_delivery_tariff2` | Number | Total amount of slave electricity used for tariff 2 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_production_tariff0` | Number | Total amount of slave electricity produced for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_production_tariff1` | Number | Total amount of slave electricity produced for tariff 1 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_production_tariff2` | Number | Total amount of slave electricity produced for tariff 2 (kWh) | Y | - | - | - | - | - | - | - | -| `emeter_tariff_indicator` | String | Current slave tariff indicator | Y | - | - | - | - | - | - | - | -| `emeter_treshold_a` | Number | Actual slave treshold (A) | Y | - | - | - | - | - | - | - | -| `meter_switch_position` | Number | Slave electricity switch position | Y | - | - | Y | Y | Y | - | - | -| `emeter_active_import_power` | Number | Slave aggregate active import power (W) | Y | - | - | - | - | - | - | - | -| `emeter_value` | Number | Slave electricity usage (kWh) in the past period | - | - | - | - | Y | Y | Y | Y | -| `emeter_value_timestamp` | DateTime | Timestamp of the last reading | - | - | - | - | Y | Y | Y | Y | -| `meter_device_type` | String | Gas Meter Device Type | - | - | - | Y | - | - | - | - | -| `meter_equipment_identifier` | String | Gas Meter ID | Y | - | - | Y | - | - | - | - | -| | | **Channels for the gas meter** | | | | | | | | | -| `gmeter_equipment_identifier_v2` | String | Gas Meter ID | - | Y | Y | - | - | - | - | - | -| `gmeter_24h_delivery_v2` | Number | Gas Delivery past 24 hours | Y | Y | Y | - | - | - | - | - | -| `gmeter_24h_delivery_v2_timestamp` | DateTime | Timestamp of the last reading | Y | Y | Y | - | - | - | - | - | -| `gmeter_24h_delivery_compensated_v2` | Number | Gas Delivery past 24 hours (compensated) | - | Y | Y | - | - | - | - | - | -| `gmeter_24h_delivery_compensated_v2_timestamp` | DateTime | Timestamp of the last reading | - | Y | Y | - | - | - | - | - | -| `gmeter_value_v3` | Number | Gas Delivery past period | - | - | - | Y | - | - | - | - | -| `gmeter_value_v3_timestamp` | DateTime | Timestamp of the last reading | - | - | - | Y | - | - | - | - | -| `gmeter_valve_position_v2_1` | Number | Gas Valve position | - | Y | - | - | - | - | - | - | -| `gmeter_valve_position_v2_2` | Number | Gas Valve position | Y | - | Y | - | - | - | - | - | -| | | **Channels for the generic meter** | | | | | | | | | -| `meter_valve_switch_position` | Number | Gas Valve position | - | - | - | Y | - | - | - | - | -| `meter_device_type` | String | Generic Meter Device Type | - | - | - | Y | - | - | - | - | -| `gmeter_equipment_identifier` | String | Generic Meter ID | - | - | - | Y | - | - | - | - | -| `genmeter_value_v3` | Number | Delivery past period | - | - | - | Y | - | - | - | - | -| `meter_valve_switch_position` | Number | Generic Meter Valve/Switch position | - | - | - | Y | - | - | - | - | -| | | **Channels for the GJ meter (Heating or Cooling)** | | | | | | | | | -| `meter_device_type` | String | GJ Meter Device Type | - | - | - | Y | Y | Y | Y | Y | -| `meter_equipment_identifier` | Number | GJ Meter ID | - | - | - | Y | Y | Y | Y | Y | -| `gjmeter_value_v3` | Number | GJ Delivery past period | - | - | - | Y | - | - | - | - | -| `gjmeter_value_v3_timestamp` | DateTime | Timestamp of the last reading | - | - | - | Y | - | - | - | - | -| `gjmeter_value_v4` | Number | GJ Delivery past period | - | - | - | - | Y | Y | Y | Y | -| `gjmeter_value_v4_timestamp` | DateTime | Timestamp of the last reading | - | - | - | - | Y | Y | Y | Y | -| | | **Channels for the heating meter** | | | | | | | | | -| `meter_valve_switch_position` | Number | GJ Meter Valve position | - | - | - | Y | Y | Y | Y | - | -| `meter_equipment_identifier` | String | Heating Meter ID | Y | - | - | - | - | - | - | - | -| `hmeter_equipment_identifier_v2_2` | String | Heating Meter ID | - | - | Y | - | - | - | - | - | -| `hmeter_value_v2` | Number | Heating Delivery past period | Y | - | Y | - | - | - | - | - | -| `hmeter_value_v2_timestamp` | DateTime | Timestamp of the last reading | Y | - | Y | - | - | - | - | - | -| | | **Channels for the m3 meter (Gas or Water)** | | | | | | | | | -| `meter_device_type` | String | m3 Meter Device Type | - | - | - | - | Y | Y | Y | Y | -| `meter_equipment_identifier` | String | m3 Meter ID | - | - | - | - | Y | Y | Y | Y | -| `m3meter_value` | Number | m3 Delivery past period | - | - | - | - | Y | Y | Y | Y | -| `meter_valve_switch_position` | Number | m3 Meter Valve position | - | - | - | - | Y | Y | Y | - | -| | | **Channels for the water meter** | | | | | | | | | -| `meter_device_type` | String | Water Meter Device Type | - | - | - | Y | - | - | - | - | -| `meter_equipment_identifier` | String | Water Meter ID | Y | - | - | Y | - | - | - | - | -| `wmeter_equipment_identifier_v2_2` | String | Water Meter ID | - | - | Y | - | - | - | - | - | -| `wmeter_value_v2` | Number | Water Delivery past period | Y | - | Y | - | - | - | - | - | -| `wmeter_value_v2_timestamp` | DateTime | Timestamp of the last reading | Y | - | Y | - | - | - | - | - | -| `wmeter_value_v3` | Number | Water Delivery past period | - | - | - | Y | - | - | - | - | -| `meter_valve_switch_position` | Number | Water Meter Valve position | - | - | - | Y | - | - | - | - | - -*note 2*. The power failure log has a dynamic number of entries starting at `0`. -So `emeter_power_failure_log_timestamp0`, `emeter_power_failure_log_duration0` refers to the first entry, -`emeter_power_failure_log_timestamp1`, `emeter_power_failure_log_duration1` refers to the second entry, etc. - -Channel identifier: `dsmr::::` - -- ThingTypeID. See table with supported meters -- BridgeID. The configured id for the bridge -- id. The configured id for the ThingType you want to address -- channel type id. The channel type id - -The following configuration must to be added to a item configuration file. E.g. `things/dsmr.items` - -``` -ItemType "" () {channel=""} -``` - -**Examples** - -``` -Number MeterDeliveryTariff0 "Total electricity delivered to the resident during low tariff period [%.3f kWh]" {channel="dsmr:electricity_v5_0:mysmartmeter:electricityV5:emeter_delivery_tariff1} -``` - -## Full configuration example - -`things/dsmr.things` - -``` -Bridge dsmr:dsmrBridge:mysmartmeter [serialPort="/dev/ttyUSB0"] { - Things: - device_v5 dsmrV5Device [channel=-1] - electricity_v5_0 electricityV5 [channel=0] -} -``` - -`things/dsmr.items` - -``` -String P1Version "P1 Version output" {channel="dsmr:device_v5:mysmartmeter:dsmrV5Device:p1_version_output"} -Number MeterDeliveryTariff0 "Total electricity delivered to the resident during low tariff period [%.3f kWh]" {channel="dsmr:device_v5:mysmartmeter:electricityV5:emeter_delivery_tariff1} -Number MeterDeliveryTariff1 "Total electricity delivered to the resident during high tariff period [%.3f kWh]" {channel="dsmr:device_v5:mysmartmeter:electricityV5:emeter_delivery_tariff2} -``` - -## Determine M-Bus channel - -By manually trigger the discovery process, e.g. via PaperUI, you can use the logging to find out a M-Bus channel. Look for the following logfile line: -` [INFO ] [] - New compatible meter: [Meter type: M3_V5_0, channel: 1, Meter type: ELECTRICITY_V5, channel: 0]` - -Here you find the ThingTypeID (it is stated only in capitals) and the M-Bus channel. The above example would lead to the following Thing definition - -``` -Bridge definition { - Things: - m3_v5_0 mygasmeter [channel=1] - electricity_v5 [channel=0] -} -``` diff --git a/addons/binding/org.openhab.binding.dsmr/build.properties b/addons/binding/org.openhab.binding.dsmr/build.properties deleted file mode 100644 index 0f6636e9de4f6..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.dsmr/pom.xml b/addons/binding/org.openhab.binding.dsmr/pom.xml deleted file mode 100644 index d05eb8c90d726..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.dsmr - eclipse-plugin - - DSMR Binding - - diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java deleted file mode 100644 index ffcd350fa8bd2..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.dsmr.internal; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.thing.ThingTypeUID; - -/** - * This class defines common constants, which are used across the whole binding. - * - * @author M. Volaart - Initial contribution - * @author Hilbrand Bouwkamp - Removed time constants - */ -@NonNullByDefault -public final class DSMRBindingConstants { - /** - * Binding id. - */ - public static final String BINDING_ID = "dsmr"; - - /** - * Key to use to identify serial port. - */ - public static final String DSMR_PORT_NAME = "org.openhab.binding.dsmr"; - - /** - * Bridge device thing - */ - public static final ThingTypeUID THING_TYPE_DSMR_BRIDGE = new ThingTypeUID(BINDING_ID, "dsmrBridge"); - - private DSMRBindingConstants() { - // Constants class - } -} diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java b/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java deleted file mode 100644 index 09d8f2edf630d..0000000000000 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.dsmr.internal.device.p1telegram; - -import java.util.List; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.dsmr.internal.device.cosem.CosemObject; - -/** - * Data class containing a Telegram with CosemObjects and TelegramState and if in lenient mode also the raw telegram - * data. - * - * @author Hilbrand Bouwkamp - Initial contribution - */ -@NonNullByDefault -public class P1Telegram { - - /** - * The TelegramState described the meta data of the P1Telegram - */ - public enum TelegramState { - /** - * OK. Telegram was successful received and CRC16 checksum is verified (CRC16 only for DSMR V4 and up) - */ - OK("P1 telegram received OK"), - /** - * CRC_ERROR. CRC16 checksum failed (only DSMR V4 and up) - */ - CRC_ERROR("CRC checksum failed for received P1 telegram"), - /** - * DATA_CORRUPTION. The P1 telegram has syntax errors. - */ - DATA_CORRUPTION("Received P1 telegram is corrupted"); - - /** - * public accessible state details - */ - public final String stateDetails; - - /** - * Constructs a new TelegramState enum - * - * @param stateDetails String containing the details of this TelegramState - */ - private TelegramState(String stateDetails) { - this.stateDetails = stateDetails; - } - } - - private final List cosemObjects; - private final TelegramState telegramState; - private final String rawTelegram; - - public P1Telegram(List cosemObjects, TelegramState telegramState, String rawTelegram) { - this.cosemObjects = cosemObjects; - this.telegramState = telegramState; - this.rawTelegram = rawTelegram; - } - - /** - * @return The list of CosemObjects - */ - public List getCosemObjects() { - return cosemObjects; - } - - /** - * @return The raw telegram data - */ - public String getRawTelegram() { - return rawTelegram; - } - - /** - * @return The state of the telegram - */ - public TelegramState getTelegramState() { - return telegramState; - } -} diff --git a/addons/binding/org.openhab.binding.enocean/.classpath b/addons/binding/org.openhab.binding.enocean/.classpath deleted file mode 100644 index 7f457fa4138d1..0000000000000 --- a/addons/binding/org.openhab.binding.enocean/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.enocean/.project b/addons/binding/org.openhab.binding.enocean/.project deleted file mode 100644 index b252d42d434fd..0000000000000 --- a/addons/binding/org.openhab.binding.enocean/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.enocean - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.enocean/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.enocean/META-INF/MANIFEST.MF deleted file mode 100644 index a9c67aaca04e4..0000000000000 --- a/addons/binding/org.openhab.binding.enocean/META-INF/MANIFEST.MF +++ /dev/null @@ -1,35 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.enocean -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: . -Bundle-ManifestVersion: 2 -Bundle-Name: EnOcean Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.enocean;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - javax.measure.quantity, - org.apache.commons.io, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.core.status, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.config.discovery.usbserial, - org.eclipse.smarthome.core.items, - org.eclipse.smarthome.core.library, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.library.unit, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.profiles, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.transform, - org.eclipse.smarthome.core.transform.actions, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.core.util, - org.eclipse.smarthome.io.transport.serial, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.enocean/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.enocean/OSGI-INF/.gitignore deleted file mode 100644 index 6722cd96e785a..0000000000000 --- a/addons/binding/org.openhab.binding.enocean/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.xml diff --git a/addons/binding/org.openhab.binding.enocean/README.md b/addons/binding/org.openhab.binding.enocean/README.md deleted file mode 100644 index 441e0ab6a42b1..0000000000000 --- a/addons/binding/org.openhab.binding.enocean/README.md +++ /dev/null @@ -1,354 +0,0 @@ -# EnOcean Binding - -The EnOcean binding connects openHAB to the EnOcean ecosystem. - -The binding uses an EnOcean gateway to retrieve sensor data and control actuators. -For _bidirectional_ actuators it is even possible to update the openHAB item state if the actuator gets modified outside of openHAB. -This binding has been developed on an USB300 gateway and was also tested with an EnOceanPi. -As this binding implements a full EnOcean stack, we have full control over these gateways. -This binding can enable the repeater function (level 1 or 2) of these gateways and retrieve detailed information about them. - -## Concepts/Configuration - -First of all you have to configure an EnOcean transceiver (gateway). -A directly connected USB300 can be auto discovered, an EnOceanPi has to be added manually to openHAB. -Both gateways are represented by an _EnOcean gateway_ in openHAB. -If you want to place the gateway for better reception apart from your openHAB server, you can forward its serial messages over TCP/IP (_ser2net_). In this case you have to define the path to the gateway like this rfc2217://x.x.x.x:3001. -If everything is running fine you should see the _base id_ of your gateway in the properties of your bridge. - -The vast majority of EnOcean messages are sent as broadcast messages without an explicit receiver address. -However each EnOcean device is identified by an unique id, called EnOceanId, which is used as the sender address in these messages. -To receive messages from an EnOcean device you have to determine its EnOceanId and add an appropriate thing to openHAB. - -If the device is an actuator which you want to control with your gateway from openHAB, you also have to create an unique sender id and announce it to the actuator (_teach-in_). -For security reasons you cannot choose a random id, instead each gateway has 127 unique ids build in, from which you can choose. -A SenderId of your gateway is made up its base id and a number between 1 and 127. -This number can be chosen manually or the next free/unused number can be determined by the binding. - -## Supported Things - -This binding is developed on and tested with the following devices - - * USB300 and EnOceanPi gateways - * The following Eltako actuators: - * FSR14 (light switch) - * FSB14 (rollershutter) - * FUD14 (dimmer) - * FSSA-230V (smart plug) - * FWZ12-65A (energy meter) - * FTKE (window / door contact) - * TF-FGB (window handle) - * TF-FKB (window contact) - * TF-AHDSB (outdoor brightness sensor) - * FAFT60 (outdoor temperature & humidity sensor) - * The following Opus actuators: - * GN-A-R12V-SR-4 (light) - * GN-A-R12V-MF-2 (light) - * GN-A-R12V-LZ/UD (dimmer) - * GN-A-R12V-JRG-2 (rollershutter) - * GN-A-U230V-JRG (rollershutter) - * OPUS-FUNK PLUS Jalousieaktor 1fach UP (rollershutter) - * OPUS-Funk PLUS Steckdosenleiste (smart multiple socket) - * NodOn: - * Smart Plug (ASP-2-1-10) - * In Wall Switch (SIN-2-2-00, SIN-2-1-0x) - * Temperature & humidity sensor (STPH-2-1-05) - * Permundo - * PSC234 (smart plug with metering) = Afriso APR234 - * PSC132 (smart switch actor with metering) - * PSC152 (smart blinds control) - * Thermokon SR04 room control - * Hoppe SecuSignal window handles - * Rocker switches (NodOn, Eltako FT55 etc) - -However, because of the standardized EnOcean protocol it is more important which EEP this binding supports. -Hence if your device supports one of the following EEPs the chances are good that your device is also supported by this binding. - -|Thing type | EEP family | EEP Types | Channels¹ | Devices² | Pairing | -|---------------------------------|-------------|---------------|------------------------------|--------------------------------|-----------| -| bridge | - | - | repeaterMode, setBaseId | USB300, EnOceanPi | - | -| pushButton | F6-01 | 0x01 | pushButton | | Manually | -| rockerSwitch | F6-02 | 0x01-02 | rockerswitchA, rockerswitchB | Eltako FT55 | Discovery | -| mechanicalHandle | F6-10 | 0x00-01 | windowHandleState, contact | Hoppe SecuSignal handles, Eltako TF-FGB | Discovery | -| contact | D5-00 | 0x01 | contact | Eltako FTK(E) & TF-FKB | Discovery | -| temperatureSensor | A5-02 | 0x01-30 | temperature | Thermokon SR65 | Discovery | -| temperatureHumiditySensor | A5-04 | 0x01-03 | humidity, temperature | Eltako FTSB | Discovery | -| occupancySensor | A5-07 | 0x01-03 | illumination, batteryVoltage,
motionDetection | NodON PIR-2-1-01 | Discovery | -| lightTemperatureOccupancySensor | A5-08 | 0x01-03 | illumination, temperature,
occupancy, motionDetection | Eltako FABH | Discovery | -| lightSensor | A5-06 | 0x01 | illumination | Eltako TF-AHDSB | Discovery | -| roomOperatingPanel | A5-10 | 0x01-23 | temperature, setPoint, fanSpeedStage,
occupancy | Thermokon SR04 | Discovery | -| automatedMeterSensor | A5-12 | 0x00-03 | counter, currentNumber, instantpower,
totalusage, amrLitre, amrCubicMetre | FWZ12 | Discovery | -| centralCommand | A5-38 | 0x08 | dimmer, generalSwitch | Eltako FUD14, FSR14 | Teach-in | -| rollershutter | A5-3F/D2-05/A5-38 | 0x7F/00/08 | rollershutter | Eltako FSB14, NodOn SIN-2-RS-01| Teach-in/Discovery | -| measurementSwitch | D2-01 | 0x00-0F,11,12 | generalSwitch(/A/B), instantpower,
totalusage, repeaterMode | NodOn In Wall Switch | Discovery | -| classicDevice | F6-02 | 0x01-02 | virtualRockerswitchA, virtualRockerswitchB | - | Teach-in | - -¹ Not all channels are supported by all devices, it depends which specific EEP type is used by the device, all thing types additionally support `rssi`, `repeatCount` and `lastReceived` channels - -² These are just examples of supported devices - -Furthermore following supporting EEP family is available too: A5-11, types 0x03 (rollershutter position status) and 0x04 (extended light status). - -A `rockerSwitch` is used to receive messages from a physical EnOcean Rocker Switch. -A `classicDevice` is used to for older EnOcean devices which react only on rocker switch messages (like Opus GN-A-R12V-SR-4). -As these devices do not send their current status, you have to add additional listener channels for each physical Rocker Switch to your thing. -In this way you can still sync your item status with the physical status of your device whenever it gets modified by a physical rocker switch. -The classic device simulates a physical Rocker Switch. -Per default the classic device uses the channel A of the simulated rocker switch. -If you want to use channel B you have to use virtualRockerswitchB in conjunction with rules to send commands. - -## Pairing - -Most of the EnOcean devices can be automatically created and configured as an openHAB thing through the discovery service. -The EnOcean protocol defines a so called "teach-in" process to announce the abilities and services of an EnOcean device and pair devices. -To pair an EnOcean device with its openHAB thing representation, you have to differentiate between sensors and actuators. - -### Sensors - -To pair a sensor with its thing, you first have to start the discovery scan for this binding in PaperUI. -Then press the "teach-in" button of the sensor. -The sensor sends a teach-in message which contains the information about the EEP and the EnOceanId of the sensor. -If the EEP is known by this binding the thing representation of the device is created. -The corresponding channels are created dynamically, too. - -### Actuators - -If the actuator supports UTE teach-in, the corresponding thing can be created and paired automatically. -First you have to **start the discovery scan for a gateway** in PaperUI. -Then press the teach-in button of the actuator. -If the EEP of the actuator is known, the binding sends an UTE teach-in response with a new SenderId and creates a new thing with its channels. - -If the actuator does not support UTE teach-ins, you have to create, configure and choose the right EEP of the thing manually. -It is important to link the teach-in channel of this thing to a switch item. -Afterwards you have to **activate the pairing mode of the actuator**. -Then switch on the teach-in item to send a teach-in message to the actuator. -If the pairing was successful, you can control the actuator and unlink the teach-in channel now. -The content of this teach-in message is device specific and can be configured through the teach-in channel. - -To pair a classicDevice with an EnOcean device, you first have to activate the pairing mode of the actuator. -Then switch the virtualRockerSwitchA On/Off. - -Each EnOcean gateway supports 127 unique SenderIds. -The SenderId of a thing can be set manually or determined automatically by the binding. -In case of an UTE teach-in the next unused SenderId is taken automatically. -To set this SenderId to a specific one, you have to use the nextSenderId parameter of your gateway. - -## Thing Configuration - -The pairing process of an openHAB thing and an EnOcean device has to be triggered within Paper UI. -Therefore if you do not want to use Paper UI, a mixed mode configuration approach has to be done. -To determine the EEP and EnOceanId of the device and announce a SenderId to it, you first have to pair an openHAB thing with the EnOcean device. -Afterwards you can delete this thing and manage it with its necessary parameters through a configuration file. -If you change the SenderId of your thing, you have to pair again the thing with your device. - -|Thing type | Parameter | Meaning | Possible Values | -|---------------------------------|-------------------|-----------------------------|---| -| bridge | path | Path to the EnOcean Gateway | COM3, /dev/ttyAMA0, rfc2217://x.x.x.x:3001 | -| | nextSenderId | Set SenderId of next created thing.
If omitted, the next unused SenderId is taken | 1-127 | -| pushButton | receivingEEPId | EEP used for receiving msg | F6_01_01 | -| | enoceanId | EnOceanId of device this thing belongs to | hex value as string | -| rockerSwitch | receivingEEPId | | F6_02_01, F6_02_02 | -| | enoceanId | | | -| mechanicalHandle | receivingEEPId | | F6_10_00, F6_10_01, A5_14_09 | -| | enoceanId | | | -| contact | receivingEEPId | | D5_00_01, A5_14_01_ELTAKO | -| | enoceanId | | | -| temperatureSensor | receivingEEPId | | A5_02_01-0B, A5_02_10-1B, A5_02_20, A5_02_30 | -| | enoceanId | | | -| temperatureHumiditySensor | receivingEEPId | | A5_04_01-03 | -| | enoceanId | | | -| occupancySensor | receivingEEPId | | A5_07_01-03 | -| | enoceanId | | | -| lightTemperatureOccupancySensor | receivingEEPId | | A5_08_01-03, A5_08_01_FXBH | -| | enoceanId | | | -| lightSensor | receivingEEPId | | A5_06_01, A5_06_01_ELTAKO | -| | enoceanId | | | -| roomOperatingPanel | receivingEEPId | | A5_10_01-0D, A5_10_10-1F, A5_10_20-23 | -| | enoceanId | | | -| automatedMeterSensor | receivingEEPId | | A5_12_00-03 | -| | enoceanId | | | -| centralCommand | senderIdOffset | SenderId used for sending msg.
If omitted, nextSenderId of bridge is used | 1-127 | -| | enoceanId | | | -| | sendingEEPId | EEP used for sending msg | A5_38_08_01, A5_38_08_02 | -| | broadcastMessages | Send broadcast or addressed msg | true, false | -| | receivingEEPId | | F6_00_00, A5_38_08_02, A5_11_04 | -| | suppressRepeating | Suppress repeating of msg | true, false | -| rollershutter | senderIdOffset | | 1-127 | -| | enoceanId | | | -| | sendingEEPId | | A5_3F_7F_EltakoFSB, A5_38_08_07, D2_05_00 | -| | broadcastMessages | | true, false | -| | receivingEEPId¹ | | A5_3F_7F_EltakoFSB, A5_11_03, D2_05_00 | -| | suppressRepeating | | true, false | -| | pollingInterval | Refresh interval in seconds | Integer | -| measurementSwitch | senderIdOffset | | 1-127 | -| | enoceanId | | | -| | sendingEEPId | | D2_01_00-0F, D2_01_11, D2_01_12,
D2_01_09_PERMUNDO, D2_01_0F_NODON, D2_01_12_NODON | -| | receivingEEPId¹ | | D2_01_00-0F, D2_01_11, D2_01_12,
D2_01_09_PERMUNDO, D2_01_0F_NODON, D2_01_12_NODON,
A5_12_01 | -| | broadcastMessages | | true, false | -| | pollingInterval | | Integer | -| | suppressRepeating | | true, false | -| classicDevice | senderIdOffset | | 1-127 | -| | sendingEEPId | | F6_02_01, F6_02_02 | -| | broadcastMessages | | true, false | -| | receivingEEPId | | F6_02_01, F6_02_02 | -| | suppressRepeating | | true, false | - -¹ multiple values possible, EEPs have to be of different EEP families. -If you want to receive messages of your EnOcean devices you have to set **the enoceanId to the EnOceanId of your device**. - -## Channels - -The channels of a thing are determined automatically based on the chosen EEP. - -|Channel | Item | Description | -|---------------------|--------------------|---------------------------------| -| repeaterMode | String | Set repeater level to 1, 2 or disable | -| setBaseId | String | Changes the BaseId of your gateway. This can only be done 10 times! So use it with care. | -| pushButton | Trigger | Channel type system:rawbutton, emits PRESSED and RELEASED events | -| rockerswitchA/B | Trigger | Channel type system:rawrocker, emits DIR1_PRESSED, DIR1_RELEASED, DIR2_PRESSED, DIR2_RELEASED events | -| windowHandleState | String | Textual representation of handle position (OPEN, CLOSED, TILTED) | -| contact | Contact | State OPEN/CLOSED (tilted handle => OPEN) | -| temperature | Number:Temperature | Temperature in degree Celsius | -| humidity | Number | Relative humidity level in percentages | -| illumination | Number:Illuminance | Illumination in lux | -| occupancy | Switch | Occupancy button pressed (ON) or released (OFF) | -| motionDetection | Switch | On=Motion detected, Off=not | -| setPoint | Number | linear set point | -| fanSpeedStage | String | Fan speed: -1 (Auto), 0, 1, 2, 3 | -| dimmer | Dimmer | Dimmer value in percent | -| generalSwitch(/A/B) | Switch | Switch something (channel A/B) ON/OFF | -| rollershutter | Rollershutter | Shut time (shutTime) in seconds can be configured | -| angle | Number:Angle | The angle for blinds | -| instantpower | Number:Power | Instant power consumption in Watts | -| totalusage | Number:Energy | Used energy in Kilowatt hours | -| teachInCMD | Switch | Sends a teach-in msg, content can configured with parameter teachInMSG | -| virtualSwitchA | Switch | Used to convert switch item commands into rocker switch messages (channel A used)
Time in ms between sending a pressed and release message can be defined with channel parameter duration.
The switch mode (rocker switch: use DIR1 and DIR2, toggle: use just one DIR) can be set with channel parameter switchMode (rockerSwitch, toggleButtonDir1, toggleButtonDir2) | -| virtualRollershutterA | Rollershutter | Used to convert rollershutter item commands into rocker switch messages (channel A used) | -| rockerswitchListenerSwitch | Switch | Used to convert rocker switch messages into switch item state updates | -| rockerswitchListenerRollershutter | Rollershutter | Used to convert rocker switch messages into rollershutter item state updates | -| virtualRockerswitchB | String | Used to send plain rocker switch messages (channel B used) | -| batteryVoltage | Number:ElectricPotential | Battery voltage for things with battery | -| energyStorage | Number:ElectricPotential | Energy storage, don't know what this means... | -| rssi | Number | Received Signal Strength Indication (dBm) of last received message | -| repeatCount | Number | Number of repeaters involved in the transmission of the telegram | -| lastReceived | DateTime | Date and time the last telegram was received | - -Items linked to bi-directional actuators (actuator sends status messages back) should always disable the `autoupdate`. -This is especially true for Eltako rollershutter, as their position is calculated out of the current position and the moving time. - -## Channel Configuration - -Some channels can be configured with parameters. - -| Channel type | Parameter | Meaning | Possible values | -|---------------|----------------|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------| -| rollershutter | shutTime | Time (in seconds) to completely close the rollershutter | | -| dimmer | rampingTime | Duration of dimming | A5-38-08: Ramping Time (in seconds), 0 = default ramping, 1..255 = seconds to 100%; D2-01-01: 0 = switch, 1-3 = timer 1-3, 4 = stop | -| | eltakoDimmer | Flag for Eltako dimmers, because Eltako does interpret this EEP differently | True for Eltako dimmer, false otherwise. Defaults to true for compatibility purpose. | -| | storeValue | Store final value. For Eltako devices, block dimming value. | True or false. Defaults to false. | -| teachInCMD | manufacturerId | Id is used for 4BS teach in with EEP | HEX | -| | teachInMSG | Use this message if teach in type and/or manufacturer id are unknown | HEX | - -Possible declaration in Thing DSL: - -```xtend -Thing centralCommand 11223344 "Light" @ "Living room" [ enoceanId="11223344", senderIdOffset=15, sendingEEPId="A5_38_08_02", receivingEEPId="A5_38_08_02" ] { - Channels: - Type teachInCMD : teachInCMD [ teachInMSG="E0400D80" ] - Type dimmer : dimmer [ rampingTime=0 ] -} -``` - -## Rules and Profiles - -The rockerSwitch things use _system:rawrocker_ channel types. -So they trigger _DIR1[/2]_\__PRESSED_ and DIR1[/2]_\__RELEASED_ events. -These channels can be directly linked to simple items like Switch, Dimmer or Player with the help of _profiles_. -If you want to do more advanced stuff, you have to implement rules which react to these events - -```xtend -rule "Advanced rocker rule" -when - Channel 'enocean:rockerSwitch:gtwy:AABBCC00:rockerswitchA' triggered DIR1_PRESSED -then - // do some advanced stuff -end -``` - -## Example - -```xtend -Bridge enocean:bridge:gtwy "EnOcean Gateway" [ path="/dev/ttyAMA0" ] { - Thing rockerSwitch rs01 "Rocker" @ "Kitchen" [ enoceanId="aabbcc01", receivingEEPId="F6_02_01" ] - Thing mechanicalHandle mh01 "Door handle" @ "Living room" [ enoceanId="aabbcc02", receivingEEPId="F6_10_00" ] - Thing roomOperatingPanel p01 "Panel" @ "Floor" [ enoceanId="aabbcc03", receivingEEPId="A5_10_06" ] - Thing centralCommand cc01 "Light" @ "Kitchen" [ enoceanId="aabbcc04", senderIdOffset=1, sendingEEPId="A5_38_08_01", receivingEEPId="F6_00_00", broadcastMessages=true, suppressRepeating=false ] - Thing centralCommand cc02 "Dimmer" @ "Living room" [ enoceanId="aabbcc05", senderIdOffset=2, sendingEEPId="A5_38_08_02", receivingEEPId="A5_38_08_02", broadcastMessages=true, suppressRepeating=false ] - Thing rollershutter r01 "Rollershutter" @ "Kitchen" [ enoceanId="aabbcc06", senderIdOffset=3, sendingEEPId="A5_3F_7F_EltakoFSB", receivingEEPId="A5_3F_7F_EltakoFSB", broadcastMessages=true, suppressRepeating=false ] {Channels: Type rollershutter:rollershutter [shutTime=25]} - Thing measurementSwitch ms01 "TV Smart Plug" @ "Living room" [ enoceanId="aabbcc07", senderIdOffset=4, sendingEEPId="D2_01_09", broadcastMessages=false, receivingEEPId="D2_01_09","A5_12_01", suppressRepeating=false, pollingInterval=300] - Thing classicDevice cd01 "Garage_Light" @ "Garage" [ - senderIdOffset=5, - sendingEEPId="F6_02_01", - broadcastMessages=true, - receivingEEPId="F6_02_01", - suppressRepeating=false - ] { - Type virtualSwitchA : virtualSwitchA [duration=300, switchMode="rockerSwitch"] - Type rockerswitchListenerSwitch : Listener1 "Schalter links" [enoceanId="aabbcc08", channel="channelA", switchMode="toggleButtonDir1"] - Type rockerswitchListenerSwitch : Listener2 "Schalter rechts" [enoceanId="aabbcc09", channel="channelB", switchMode="toggleButtonDir2"] - } -} -``` - -```xtend -Player Kitchen_Sonos "Sonos" (Kitchen) {channel="sonos:PLAY1:ID:control", channel="enocean:rockerSwitch:gtwy:rs01:rockerswitchA" [profile="system:rawrocker-to-play-pause"]} -Dimmer Kitchen_Hue "Hue" {channel="enocean:rockerSwitch:gtwy:rs01:rockerswitchB" [profile="system:rawrocker-to-dimmer"], channel="hue:0220:0017884f6626:9:brightness"} -Rollershutter Kitchen_Rollershutter "Roller shutter" (Kitchen) {channel="enocean:rollershutter:gtwy:r01:rollershutter", autoupdate="false"} -Switch Garage_Light "Switch" { - channel="enocean:classicDevice:gtwy:cd01:virtualRockerswitchA", - channel="enocean:classicDevice:gtwy:cd01:Listener1", - channel="enocean:classicDevice:gtwy:cd01:Listener2" -} -``` - -## Generic Things - -If an EnOcean device uses an unsupported EEP or _A5-3F-7F_, you have to create a `genericThing`. -Generic things support all channels like switch, number, string etc as generic channels. -However you have to specify how to convert the EnOcean messages of the device into openHAB state updates and how to convert the openHAB commands into EnOcean messages. -These conversion functions can be defined with the help of transformation functions like MAP. - -|Thing type | Parameter | Meaning | Possible Values | -|---------------------------------|-------------------|----------------------------|---| -| genericThing | senderIdOffset | | 1-127 | -| | enoceanId | EnOceanId of device this thing belongs to | hex value as string | -| | sendingEEPId | EEP used for sending msg | F6_FF_FF, A5_FF_FF, D2_FF_FF | -| | receivingEEPId | EEP used for receiving msg | F6_FF_FF, A5_FF_FF, D2_FF_FF | -| | broadcastMessages | | true, false | -| | suppressRepeating | | true, false | - -Supported channels: genericSwitch, genericRollershutter, genericDimmer, genericNumber, genericString, genericColor, genericTeachInCMD. -You have to define the transformationType (e.g. MAP) and transformationFunction (e.g. for MAP: file name of mapping file) for each of these channels. - -For an inbound transformation (EnOcean message => openHAB state) you receive the channel id and the EnOcean data in hex separated by a pipe. -Your transformation function has to return the openHAB State type and value separated by a pipe. -If you want to use a mapping transformation, your mapping file has to look like this for a genericThing using EEP F6_FF_FF: - -``` -ChannelId|EnoceanData(Hex)=openHABState|Value -genericSwitch|70=OnOffType|ON -genericSwitch|50=OnOffType|OFF -genericRollershutter|70=PercentType|0 -genericRollershutter|50=PercentType|100 -``` - -For an outbound transformation (openHAB command => EnOcean message) you receive the channel id and the command separated by a pipe. -Your transformation function has to return the payload of the EnOcean message. -You do not have to worry about CRC and header data. -If you want to use a mapping transformation, your mapping file has to look like this for a genericThing using EEP A5_FF_FF: - -``` -ChannelId|openHABCommand=EnoceanData(Hex) -genericSwitch|ON=01000009 -genericSwitch|OFF=01000008 -``` diff --git a/addons/binding/org.openhab.binding.enocean/build.properties b/addons/binding/org.openhab.binding.enocean/build.properties deleted file mode 100644 index a6cfff567c9b1..0000000000000 --- a/addons/binding/org.openhab.binding.enocean/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -source..=src/main/java/ -output..=target/classes -bin.includes=META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.enocean/pom.xml b/addons/binding/org.openhab.binding.enocean/pom.xml deleted file mode 100644 index f75bb83c66667..0000000000000 --- a/addons/binding/org.openhab.binding.enocean/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - pom - org.openhab.binding - 2.5.0-SNAPSHOT - - - org.openhab.binding.enocean - eclipse-plugin - - EnOcean Binding - - diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java b/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java deleted file mode 100644 index 4555d3d57117c..0000000000000 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.enocean.internal.handler; - -import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.Set; -import java.util.function.Predicate; - -import org.eclipse.smarthome.config.core.Configuration; -import org.eclipse.smarthome.core.thing.Channel; -import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.types.Command; -import org.eclipse.smarthome.core.types.State; -import org.eclipse.smarthome.core.types.UnDefType; -import org.eclipse.smarthome.core.util.HexUtils; -import org.openhab.binding.enocean.internal.config.EnOceanBaseConfig; -import org.openhab.binding.enocean.internal.eep.EEP; -import org.openhab.binding.enocean.internal.eep.EEPFactory; -import org.openhab.binding.enocean.internal.eep.EEPType; -import org.openhab.binding.enocean.internal.messages.ERP1Message; -import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; -import org.openhab.binding.enocean.internal.messages.ESP3Packet; -import org.openhab.binding.enocean.internal.transceiver.ESP3PacketListener; - -/** - * - * @author Daniel Weber - Initial contribution - * This class defines base functionality for receiving eep messages. - */ -public class EnOceanBaseSensorHandler extends EnOceanBaseThingHandler implements ESP3PacketListener { - - // List of all thing types which support receiving of eep messages - public final static Set SUPPORTED_THING_TYPES = new HashSet( - Arrays.asList(THING_TYPE_ROOMOPERATINGPANEL, THING_TYPE_MECHANICALHANDLE, THING_TYPE_CONTACT, - THING_TYPE_TEMPERATURESENSOR, THING_TYPE_TEMPERATUREHUMIDITYSENSOR, THING_TYPE_ROCKERSWITCH, - THING_TYPE_OCCUPANCYSENSOR, THING_TYPE_LIGHTTEMPERATUREOCCUPANCYSENSOR, THING_TYPE_LIGHTSENSOR, - THING_TYPE_PUSHBUTTON, THING_TYPE_AUTOMATEDMETERSENSOR)); - - protected Hashtable receivingEEPTypes = null; - - public EnOceanBaseSensorHandler(Thing thing) { - super(thing); - } - - @Override - void initializeConfig() { - config = getConfigAs(EnOceanBaseConfig.class); - } - - @Override - boolean validateConfig() { - receivingEEPTypes = null; - - try { - if (config.receivingEEPId != null && !config.receivingEEPId.isEmpty()) { - boolean first = true; - receivingEEPTypes = new Hashtable<>(); - - for (String receivingEEP : config.receivingEEPId) { - if (receivingEEP == null) { - continue; - } - - EEPType receivingEEPType = EEPType.getType(receivingEEP); - if (receivingEEPTypes.containsKey(receivingEEPType.getRORG())) { - configurationErrorDescription = "Receiving more than one EEP of the same RORG is not supported"; - return false; - } - - receivingEEPTypes.put(receivingEEPType.getRORG(), receivingEEPType); - updateChannels(receivingEEPType, first); - first = false; - } - } else { - receivingEEPTypes = null; - } - } catch (Exception e) { - configurationErrorDescription = "Receiving EEP is not supported"; - return false; - } - - if (receivingEEPTypes != null) { - if (!validateEnoceanId(config.enoceanId)) { - configurationErrorDescription = "EnOceanId is not a valid EnOceanId"; - return false; - } - - if (!config.enoceanId.equals(EMPTYENOCEANID)) { - getBridgeHandler().addPacketListener(this); - } - } - - return true; - } - - @Override - public long getSenderIdToListenTo() { - return Long.parseLong(config.enoceanId, 16); - } - - @Override - public void handleRemoval() { - - if (getBridgeHandler() != null) { - getBridgeHandler().removePacketListener(this); - } - super.handleRemoval(); - } - - @Override - public void handleCommand(ChannelUID channelUID, Command command) { - // sensor things cannot send any messages, hence they are not allowed to handle any command - // The only possible command would be "Refresh" - } - - protected Predicate channelFilter(EEPType eepType, byte[] senderId) { - return c -> eepType.GetSupportedChannels().containsKey(c.getUID().getId()); - } - - @Override - public void espPacketReceived(ESP3Packet packet) { - - if (receivingEEPTypes == null) { - return; - } - - try { - - ERP1Message msg = (ERP1Message) packet; - EEPType receivingEEPType = receivingEEPTypes.get(msg.getRORG()); - if (receivingEEPType == null) { - return; - } - - EEP eep = EEPFactory.buildEEP(receivingEEPType, (ERP1Message) packet); - logger.debug("ESP Packet payload {} for {} received", HexUtils.bytesToHex(packet.getPayload()), - config.enoceanId); - - if (eep.isValid()) { - - // try to interpret received message for all linked channels - getLinkedChannels().stream().filter(channelFilter(receivingEEPType, msg.getSenderId())) - .forEach(channel -> { - String channelTypeId = channel.getChannelTypeUID().getId(); - String channelId = channel.getUID().getId(); - Configuration channelConfig = channel.getConfiguration(); - switch (channel.getKind()) { - case STATE: - State currentState = getCurrentState(channelId); - State result = eep.convertToState(channelId, channelTypeId, channelConfig, - currentState); - - // if message can be interpreted (result != UnDefType.UNDEF) => update item state - if (result != null && result != UnDefType.UNDEF) { - updateState(channelId, result); - setCurrentState(channelTypeId, result); // update internal state map - } - break; - case TRIGGER: - String lastEvent = lastEvents.get(channelId); - String event = eep.convertToEvent(channelId, channelTypeId, lastEvent, - channelConfig); - if (event != null) { - triggerChannel(channel.getUID(), event); - lastEvents.put(channelId, event); - } - break; - } - }); - } - } catch (Exception e) { - logger.debug(e.getMessage()); - } - } -} diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java b/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java deleted file mode 100644 index 70902566e0fdf..0000000000000 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java +++ /dev/null @@ -1,273 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.enocean.internal.handler; - -import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; - -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.smarthome.core.library.types.OnOffType; -import org.eclipse.smarthome.core.library.types.StopMoveType; -import org.eclipse.smarthome.core.library.types.StringType; -import org.eclipse.smarthome.core.library.types.UpDownType; -import org.eclipse.smarthome.core.thing.Channel; -import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.thing.CommonTriggerEvents; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingStatus; -import org.eclipse.smarthome.core.thing.ThingStatusDetail; -import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.types.Command; -import org.eclipse.smarthome.core.types.RefreshType; -import org.eclipse.smarthome.core.types.State; -import org.eclipse.smarthome.core.util.HexUtils; -import org.openhab.binding.enocean.internal.config.EnOceanActuatorConfig; -import org.openhab.binding.enocean.internal.config.EnOceanChannelRockerSwitchConfigBase.SwitchMode; -import org.openhab.binding.enocean.internal.config.EnOceanChannelRockerSwitchListenerConfig; -import org.openhab.binding.enocean.internal.config.EnOceanChannelVirtualRockerSwitchConfig; -import org.openhab.binding.enocean.internal.eep.EEP; -import org.openhab.binding.enocean.internal.eep.EEPFactory; -import org.openhab.binding.enocean.internal.eep.EEPType; -import org.openhab.binding.enocean.internal.messages.ESP3Packet; - -/** - * - * @author Daniel Weber - Initial contribution - * This class defines base functionality for sending eep messages. This class extends EnOceanBaseSensorHandler - * class as most actuator things send status or response messages, too. - */ -public class EnOceanClassicDeviceHandler extends EnOceanBaseActuatorHandler { - - // List of thing types which support sending of eep messages - public final static Set SUPPORTED_THING_TYPES = new HashSet( - Arrays.asList(THING_TYPE_CLASSICDEVICE)); - - private Hashtable channelConfigById; - private String currentEnOceanId; - private StringType lastTriggerEvent = StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED); - - public EnOceanClassicDeviceHandler(Thing thing) { - super(thing); - - channelConfigById = new Hashtable<>(); - } - - @Override - void initializeConfig() { - super.initializeConfig(); - ((EnOceanActuatorConfig) config).broadcastMessages = true; - ((EnOceanActuatorConfig) config).enoceanId = EMPTYENOCEANID; - } - - @Override - public long getSenderIdToListenTo() { - return Long.parseLong(currentEnOceanId, 16); - } - - @Override - public void channelLinked(@NonNull ChannelUID channelUID) { - super.channelLinked(channelUID); - - // if linked channel is a listening channel => put listener - String id = channelUID.getId(); - Channel channel = getThing().getChannel(id); - addListener(channel); - } - - @Override - public void thingUpdated(Thing thing) { - super.thingUpdated(thing); - - // it seems that there does not exist a channel update callback - // => remove all listeners and add them again - while (!channelConfigById.isEmpty()) { - removeListener(getThing().getChannel(channelConfigById.keys().nextElement())); - } - - getLinkedChannels().forEach(c -> { - if (!addListener(c)) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Wrong channel configuration"); - } - }); - } - - @Override - public void channelUnlinked(@NonNull ChannelUID channelUID) { - super.channelUnlinked(channelUID); - - // if unlinked channel is listening channel => remove listener - String id = channelUID.getId(); - Channel channel = getThing().getChannel(id); - removeListener(channel); - } - - protected boolean addListener(Channel channel) { - if (channel != null && channel.getChannelTypeUID().getId().startsWith("rockerswitchListener")) { - EnOceanChannelRockerSwitchListenerConfig config = channel.getConfiguration() - .as(EnOceanChannelRockerSwitchListenerConfig.class); - - try { - Long enoceanId = Long.parseLong(config.enoceanId, 16); - channelConfigById.put(channel.getUID().getId(), config); - currentEnOceanId = config.enoceanId; - getBridgeHandler().addPacketListener(this); - - return true; - } catch (Exception e) { - } - - return false; - } - return true; - } - - protected void removeListener(Channel channel) { - if (channel != null && channel.getChannelTypeUID().getId().startsWith("rockerswitchListener")) { - String channelId = channel.getUID().getId(); - - if (channelConfigById.containsKey(channelId)) { - currentEnOceanId = channelConfigById.get(channelId).enoceanId; - channelConfigById.remove(channelId); - getBridgeHandler().removePacketListener(this); - } - } - } - - @Override - protected State getCurrentState(String channelId) { - // Always use the same channelId of CHANNEL_VIRTUALSWITCHA - return super.getCurrentState(CHANNEL_VIRTUALSWITCHA); - } - - @Override - protected void setCurrentState(String channelId, State state) { - // Always use the same channelId of CHANNEL_VIRTUALSWITCHA - super.setCurrentState(CHANNEL_VIRTUALSWITCHA, state); - } - - @Override - protected Predicate channelFilter(EEPType eepType, byte[] senderId) { - return c -> c.getChannelTypeUID().getId().startsWith("rockerswitchListener") - && c.getConfiguration().as(EnOceanChannelRockerSwitchListenerConfig.class).enoceanId - .equalsIgnoreCase(HexUtils.bytesToHex(senderId)); - } - - private StringType convertToReleasedCommand(StringType command) { - return command.equals(CommonTriggerEvents.DIR1_PRESSED) ? StringType.valueOf(CommonTriggerEvents.DIR1_RELEASED) - : StringType.valueOf(CommonTriggerEvents.DIR2_RELEASED); - } - - private StringType convertToPressedCommand(Command command, SwitchMode switchMode) { - if (command instanceof StringType) { - return (StringType) command; - } else if (command instanceof OnOffType) { - switch (switchMode) { - case RockerSwitch: - return (command == OnOffType.ON) ? StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED) - : StringType.valueOf(CommonTriggerEvents.DIR2_PRESSED); - case ToggleDir1: - return StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED); - case ToggleDir2: - return StringType.valueOf(CommonTriggerEvents.DIR2_PRESSED); - default: - return null; - } - } else if (command instanceof UpDownType) { - switch (switchMode) { - case RockerSwitch: - return (command == UpDownType.UP) ? StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED) - : StringType.valueOf(CommonTriggerEvents.DIR2_PRESSED); - case ToggleDir1: - return StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED); - case ToggleDir2: - return StringType.valueOf(CommonTriggerEvents.DIR2_PRESSED); - default: - return null; - } - } else if (command instanceof StopMoveType) { - if (command == StopMoveType.STOP) { - return lastTriggerEvent; - } - } - - return null; - } - - @Override - public void handleCommand(@NonNull ChannelUID channelUID, @NonNull Command command) { - - // We must have a valid sendingEEPType and sender id to send commands - if (sendingEEPType == null || senderId == null || command == RefreshType.REFRESH) { - return; - } - - try { - String channelId = channelUID.getId(); - Channel channel = getThing().getChannel(channelId); - // check if the channel is linked otherwise do nothing - if (!getLinkedChannels().contains(channel)) { - return; - } - if (channel.getChannelTypeUID().getId().contains("Listener")) { - return; - } - - EnOceanChannelVirtualRockerSwitchConfig channelConfig = channel.getConfiguration() - .as(EnOceanChannelVirtualRockerSwitchConfig.class); - State currentState = getCurrentState(channelId); - StringType result = convertToPressedCommand(command, channelConfig.getSwitchMode()); - - if (result != null) { - lastTriggerEvent = result; - - EEP eep = EEPFactory.createEEP(sendingEEPType); - ESP3Packet press = eep.setSenderId(senderId).setDestinationId(destinationId) - .convertFromCommand(channelId, channel.getChannelTypeUID().getId(), result, currentState, - channel.getConfiguration()) - .setSuppressRepeating(getConfiguration().suppressRepeating).getERP1Message(); - - getBridgeHandler().sendMessage(press, null); - - if (channelConfig.duration > 0) { - scheduler.schedule(() -> { - ESP3Packet release = eep.convertFromCommand(channelId, channel.getChannelTypeUID().getId(), - convertToReleasedCommand(lastTriggerEvent), currentState, channel.getConfiguration()) - .getERP1Message(); - - getBridgeHandler().sendMessage(release, null); - - }, channelConfig.duration, TimeUnit.MILLISECONDS); - } - } - - } catch (Exception e) { - logger.debug(e.getMessage()); - } - - } - - @Override - public void handleRemoval() { - for (Channel channel : getLinkedChannels()) { - removeListener(channel); - } - currentEnOceanId = EMPTYENOCEANID; - super.handleRemoval(); - } -} diff --git a/addons/binding/org.openhab.binding.exec/.classpath b/addons/binding/org.openhab.binding.exec/.classpath deleted file mode 100644 index 7f457fa4138d1..0000000000000 --- a/addons/binding/org.openhab.binding.exec/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.exec/.project b/addons/binding/org.openhab.binding.exec/.project deleted file mode 100644 index 19db9d6dc58e7..0000000000000 --- a/addons/binding/org.openhab.binding.exec/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.exec - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.exec/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.exec/META-INF/MANIFEST.MF deleted file mode 100644 index 6c7add5f8ed59..0000000000000 --- a/addons/binding/org.openhab.binding.exec/META-INF/MANIFEST.MF +++ /dev/null @@ -1,25 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.exec -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: . -Bundle-ManifestVersion: 2 -Bundle-Name: Exec Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.exec;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - org.apache.commons.exec, - org.apache.commons.lang, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.transform, - org.eclipse.smarthome.core.types, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.exec/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.exec/OSGI-INF/.gitignore deleted file mode 100644 index 6722cd96e785a..0000000000000 --- a/addons/binding/org.openhab.binding.exec/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.xml diff --git a/addons/binding/org.openhab.binding.exec/build.properties b/addons/binding/org.openhab.binding.exec/build.properties deleted file mode 100644 index 2802d321c602c..0000000000000 --- a/addons/binding/org.openhab.binding.exec/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE - diff --git a/addons/binding/org.openhab.binding.exec/pom.xml b/addons/binding/org.openhab.binding.exec/pom.xml deleted file mode 100644 index fad51f6e976f2..0000000000000 --- a/addons/binding/org.openhab.binding.exec/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.exec - eclipse-plugin - - Exec Binding - - diff --git a/addons/binding/org.openhab.binding.feed.test/.classpath b/addons/binding/org.openhab.binding.feed.test/.classpath deleted file mode 100644 index afa41445ca61e..0000000000000 --- a/addons/binding/org.openhab.binding.feed.test/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/addons/binding/org.openhab.binding.feed.test/.project b/addons/binding/org.openhab.binding.feed.test/.project deleted file mode 100644 index 551d28ffe95b9..0000000000000 --- a/addons/binding/org.openhab.binding.feed.test/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.feed.test - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.feed.test/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.feed.test/META-INF/MANIFEST.MF deleted file mode 100644 index cf719f28246ea..0000000000000 --- a/addons/binding/org.openhab.binding.feed.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,32 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.feed.test -Bundle-ManifestVersion: 2 -Bundle-Name: Feed Binding Tests -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.feed.test;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Fragment-Host: org.openhab.binding.feed -Import-Package: - javax.servlet, - javax.servlet.http, - org.apache.commons.io, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.jetty.http, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.events, - org.eclipse.smarthome.core.items, - org.eclipse.smarthome.core.library.items, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.link, - org.eclipse.smarthome.test.java, - org.eclipse.smarthome.test.storage, - org.hamcrest;core=split, - org.junit;version="4.0.0", - org.junit.experimental.categories, - org.junit.matchers, - org.junit.runner, - org.osgi.framework, - org.osgi.service.event, - org.osgi.service.http diff --git a/addons/binding/org.openhab.binding.feed.test/build.properties b/addons/binding/org.openhab.binding.feed.test/build.properties deleted file mode 100644 index 7c4bbc93109d2..0000000000000 --- a/addons/binding/org.openhab.binding.feed.test/build.properties +++ /dev/null @@ -1,6 +0,0 @@ -source.. = src/test/java/,\ - src/test/resources/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.feed.test/org.openhab.binding.feed.test.launch b/addons/binding/org.openhab.binding.feed.test/org.openhab.binding.feed.test.launch deleted file mode 100644 index 9e81e8ffe9753..0000000000000 --- a/addons/binding/org.openhab.binding.feed.test/org.openhab.binding.feed.test.launch +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.feed.test/pom.xml b/addons/binding/org.openhab.binding.feed.test/pom.xml deleted file mode 100644 index 75f5927be94c6..0000000000000 --- a/addons/binding/org.openhab.binding.feed.test/pom.xml +++ /dev/null @@ -1,146 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.feed.test - eclipse-test-plugin - - Feed Binding Tests - - - - org.openhab.io - org.openhab.io.transport.feed - ${project.version} - - - - - - - ${tycho-groupid} - tycho-surefire-plugin - - junit47 - - org.openhab.binding.feed.test.SlowTests - - - 4 - true - - - - - eclipse-plugin - org.eclipse.equinox.ds - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.event - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.util - 0.0.0 - - - eclipse-plugin - org.eclipse.osgi.services - 0.0.0 - - - - eclipse-plugin - ch.qos.logback.classic - 0.0.0 - - - eclipse-plugin - ch.qos.logback.core - 0.0.0 - - - eclipse-plugin - ch.qos.logback.slf4j - 0.0.0 - - - - eclipse-plugin - org.eclipse.equinox.http.jetty - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.http.servlet - 0.0.0 - - - - eclipse-plugin - org.eclipse.smarthome.config.xml - 0.0.0 - - - eclipse-plugin - org.eclipse.smarthome.core.thing - 0.0.0 - - - eclipse-plugin - org.eclipse.smarthome.core.thing.xml - 0.0.0 - - - eclipse-plugin - org.eclipse.smarthome.core.binding.xml - 0.0.0 - - - - - org.eclipse.equinox.ds - 1 - true - - - org.eclipse.equinox.http.servlet - 3 - true - - - org.eclipse.equinox.http.jetty - 3 - true - - - org.eclipse.equinox.event - 2 - true - - - - - -Dorg.osgi.service.http.port=9090 -Dprm.platform.capability.ClientPlatformID=notSimulator - - false - - - notSimulator - - - - - - diff --git a/addons/binding/org.openhab.binding.feed/.classpath b/addons/binding/org.openhab.binding.feed/.classpath deleted file mode 100644 index 3daf8821910d1..0000000000000 --- a/addons/binding/org.openhab.binding.feed/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.feed/.project b/addons/binding/org.openhab.binding.feed/.project deleted file mode 100644 index 997a5c60bdd36..0000000000000 --- a/addons/binding/org.openhab.binding.feed/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.feed - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.feed/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.feed/META-INF/MANIFEST.MF deleted file mode 100644 index 108b9fe9b9901..0000000000000 --- a/addons/binding/org.openhab.binding.feed/META-INF/MANIFEST.MF +++ /dev/null @@ -1,27 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.feed -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: . -Bundle-ManifestVersion: 2 -Bundle-Name: Feed Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.feed;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - com.rometools.rome.feed, - com.rometools.rome.feed.atom, - com.rometools.rome.feed.module, - com.rometools.rome.feed.rss, - com.rometools.rome.feed.synd, - com.rometools.rome.io, - com.rometools.utils, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.types, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.feed/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.feed/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.feed/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.feed/build.properties b/addons/binding/org.openhab.binding.feed/build.properties deleted file mode 100644 index 2802d321c602c..0000000000000 --- a/addons/binding/org.openhab.binding.feed/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE - diff --git a/addons/binding/org.openhab.binding.feed/pom.xml b/addons/binding/org.openhab.binding.feed/pom.xml deleted file mode 100644 index c326f75745545..0000000000000 --- a/addons/binding/org.openhab.binding.feed/pom.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.feed - eclipse-plugin - - Feed Binding - - - - org.openhab.io - org.openhab.io.transport.feed - ${project.version} - - - - diff --git a/addons/binding/org.openhab.binding.ftpupload/.classpath b/addons/binding/org.openhab.binding.ftpupload/.classpath deleted file mode 100644 index c9d8a81b5173f..0000000000000 --- a/addons/binding/org.openhab.binding.ftpupload/.classpath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.ftpupload/.project b/addons/binding/org.openhab.binding.ftpupload/.project deleted file mode 100644 index aad3a6997b495..0000000000000 --- a/addons/binding/org.openhab.binding.ftpupload/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.ftpupload - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.ftpupload/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.ftpupload/META-INF/MANIFEST.MF deleted file mode 100644 index 0f183018ba370..0000000000000 --- a/addons/binding/org.openhab.binding.ftpupload/META-INF/MANIFEST.MF +++ /dev/null @@ -1,28 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.ftpupload -Bundle-ActivationPolicy: lazy -Bundle-ManifestVersion: 2 -Bundle-Name: FTP Upload Binding -Bundle-SymbolicName: org.openhab.binding.ftpupload;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-ClassPath: ., - lib/ftplet-api-1.1.0.jar, - lib/ftpserver-core-1.1.0.jar, - lib/mina-core-2.0.16.jar -Import-Package: - org.apache.commons.lang, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.core.util, - org.eclipse.smarthome.io.net.http, - org.osgi.service.component, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.ftpupload/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.ftpupload/OSGI-INF/.gitignore deleted file mode 100644 index 6722cd96e785a..0000000000000 --- a/addons/binding/org.openhab.binding.ftpupload/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.xml diff --git a/addons/binding/org.openhab.binding.ftpupload/build.properties b/addons/binding/org.openhab.binding.ftpupload/build.properties deleted file mode 100644 index 23960a396f025..0000000000000 --- a/addons/binding/org.openhab.binding.ftpupload/build.properties +++ /dev/null @@ -1,10 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE,\ - lib/ftplet-api-1.1.0.jar,\ - lib/ftpserver-core-1.1.0.jar,\ - lib/mina-core-2.0.16.jar diff --git a/addons/binding/org.openhab.binding.ftpupload/lib/ftplet-api-1.1.0.jar b/addons/binding/org.openhab.binding.ftpupload/lib/ftplet-api-1.1.0.jar deleted file mode 100644 index 36784e9744c2b..0000000000000 Binary files a/addons/binding/org.openhab.binding.ftpupload/lib/ftplet-api-1.1.0.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.ftpupload/lib/ftpserver-core-1.1.0.jar b/addons/binding/org.openhab.binding.ftpupload/lib/ftpserver-core-1.1.0.jar deleted file mode 100644 index 00bf2a16d87a6..0000000000000 Binary files a/addons/binding/org.openhab.binding.ftpupload/lib/ftpserver-core-1.1.0.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.ftpupload/lib/mina-core-2.0.16.jar b/addons/binding/org.openhab.binding.ftpupload/lib/mina-core-2.0.16.jar deleted file mode 100644 index 0e6d9883e6917..0000000000000 Binary files a/addons/binding/org.openhab.binding.ftpupload/lib/mina-core-2.0.16.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.ftpupload/pom.xml b/addons/binding/org.openhab.binding.ftpupload/pom.xml deleted file mode 100644 index caa2631248db4..0000000000000 --- a/addons/binding/org.openhab.binding.ftpupload/pom.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding - org.openhab.binding.ftpupload - 2.5.0-SNAPSHOT - eclipse-plugin - - FTP Upload Binding - - diff --git a/addons/binding/org.openhab.binding.ihc.test/.classpath b/addons/binding/org.openhab.binding.ihc.test/.classpath deleted file mode 100644 index c0a7772eee543..0000000000000 --- a/addons/binding/org.openhab.binding.ihc.test/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.ihc.test/.project b/addons/binding/org.openhab.binding.ihc.test/.project deleted file mode 100644 index c6393fee6cb40..0000000000000 --- a/addons/binding/org.openhab.binding.ihc.test/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.openhab.binding.ihc.test - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.ihc.test/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.ihc.test/META-INF/MANIFEST.MF deleted file mode 100644 index b64dbe0f7ee4b..0000000000000 --- a/addons/binding/org.openhab.binding.ihc.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,23 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: openHAB IHC / ELKO Binding Tests -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.ihc.test;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Fragment-Host: org.openhab.binding.ihc -Import-Package: - org.apache.commons.lang.reflect, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.test, - org.eclipse.smarthome.test.storage, - org.hamcrest;core=split, - org.junit, - org.junit.runner, - org.junit.runners, - org.mockito, - org.mockito.invocation, - org.mockito.stubbing, - org.osgi.service.device, - org.slf4j diff --git a/addons/binding/org.openhab.binding.ihc.test/build.properties b/addons/binding/org.openhab.binding.ihc.test/build.properties deleted file mode 100644 index 4d11718bcbfa4..0000000000000 --- a/addons/binding/org.openhab.binding.ihc.test/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -source.. = src/test/java/ -output.. = target/classes/ -bin.includes = META-INF/,\ - .,\ - src/,\ - NOTICE - diff --git a/addons/binding/org.openhab.binding.ihc.test/org.openhab.binding.ihc.test.launch b/addons/binding/org.openhab.binding.ihc.test/org.openhab.binding.ihc.test.launch deleted file mode 100644 index 84bca0a176be6..0000000000000 --- a/addons/binding/org.openhab.binding.ihc.test/org.openhab.binding.ihc.test.launch +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.ihc.test/pom.xml b/addons/binding/org.openhab.binding.ihc.test/pom.xml deleted file mode 100644 index 2d74b1f9547ad..0000000000000 --- a/addons/binding/org.openhab.binding.ihc.test/pom.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - 4.0.0 - - - pom - org.openhab.binding - 2.5.0-SNAPSHOT - - - org.openhab.binding.ihc.test - eclipse-test-plugin - - IHC / ELKO Binding Tests - - - - - org.eclipse.tycho - tycho-surefire-plugin - ${tycho-version} - - - - eclipse-plugin - org.eclipse.equinox.event - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.ds - 0.0.0 - - - eclipse-plugin - org.eclipse.smarthome.config.xml - 0.0.0 - - - eclipse-plugin - org.eclipse.smarthome.core.thing.xml - 0.0.0 - - - eclipse-plugin - org.eclipse.smarthome.core.binding.xml - 0.0.0 - - - - 4 - true - - - - - - - diff --git a/addons/binding/org.openhab.binding.ihc/.classpath b/addons/binding/org.openhab.binding.ihc/.classpath deleted file mode 100755 index e32acc0cb4fbb..0000000000000 --- a/addons/binding/org.openhab.binding.ihc/.classpath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.ihc/.project b/addons/binding/org.openhab.binding.ihc/.project deleted file mode 100755 index a35c80a7964d9..0000000000000 --- a/addons/binding/org.openhab.binding.ihc/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.ihc - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.ihc/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.ihc/META-INF/MANIFEST.MF deleted file mode 100755 index 8b091a8b2bfbb..0000000000000 --- a/addons/binding/org.openhab.binding.ihc/META-INF/MANIFEST.MF +++ /dev/null @@ -1,26 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: IHC / ELKO Binding -Bundle-SymbolicName: org.openhab.binding.ihc;singleton:=true -Bundle-Vendor: openHAB -Bundle-ClassPath: ., - lib/httpclient-4.5.6.jar, - lib/httpcore-4.4.10.jar, - lib/commons-logging-1.2.jar -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-Version: 2.5.0.qualifier -Import-Package: org.apache.commons.io, - org.apache.commons.lang, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.library, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.profiles, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.slf4j -Service-Component: OSGI-INF/*.xml -Bundle-ActivationPolicy: lazy diff --git a/addons/binding/org.openhab.binding.ihc/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.ihc/OSGI-INF/.gitignore deleted file mode 100644 index 6722cd96e785a..0000000000000 --- a/addons/binding/org.openhab.binding.ihc/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.xml diff --git a/addons/binding/org.openhab.binding.ihc/build.properties b/addons/binding/org.openhab.binding.ihc/build.properties deleted file mode 100755 index c97480f987cbb..0000000000000 --- a/addons/binding/org.openhab.binding.ihc/build.properties +++ /dev/null @@ -1,10 +0,0 @@ -source..=src/main/java/ -output..=target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE,\ - lib/httpclient-4.5.6.jar,\ - lib/httpcore-4.4.10.jar,\ - lib/commons-logging-1.2.jar diff --git a/addons/binding/org.openhab.binding.ihc/lib/commons-logging-1.2.jar b/addons/binding/org.openhab.binding.ihc/lib/commons-logging-1.2.jar deleted file mode 100644 index 93a3b9f6db406..0000000000000 Binary files a/addons/binding/org.openhab.binding.ihc/lib/commons-logging-1.2.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.ihc/lib/httpclient-4.5.6.jar b/addons/binding/org.openhab.binding.ihc/lib/httpclient-4.5.6.jar deleted file mode 100644 index 56231de0cb6da..0000000000000 Binary files a/addons/binding/org.openhab.binding.ihc/lib/httpclient-4.5.6.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.ihc/lib/httpcore-4.4.10.jar b/addons/binding/org.openhab.binding.ihc/lib/httpcore-4.4.10.jar deleted file mode 100644 index dc510f81cb7a8..0000000000000 Binary files a/addons/binding/org.openhab.binding.ihc/lib/httpcore-4.4.10.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.ihc/pom.xml b/addons/binding/org.openhab.binding.ihc/pom.xml deleted file mode 100755 index 8464ed8f89096..0000000000000 --- a/addons/binding/org.openhab.binding.ihc/pom.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.ihc - eclipse-plugin - - IHC / ELKO Binding - - diff --git a/addons/binding/org.openhab.binding.innogysmarthome/.classpath b/addons/binding/org.openhab.binding.innogysmarthome/.classpath deleted file mode 100644 index 708db9a843de1..0000000000000 --- a/addons/binding/org.openhab.binding.innogysmarthome/.classpath +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.innogysmarthome/.project b/addons/binding/org.openhab.binding.innogysmarthome/.project deleted file mode 100644 index 9a8c6619e5360..0000000000000 --- a/addons/binding/org.openhab.binding.innogysmarthome/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.innogysmarthome - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.innogysmarthome/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.innogysmarthome/META-INF/MANIFEST.MF deleted file mode 100644 index 874cbc1b316a7..0000000000000 --- a/addons/binding/org.openhab.binding.innogysmarthome/META-INF/MANIFEST.MF +++ /dev/null @@ -1,46 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.innogysmarthome -Bundle-ActivationPolicy: lazy -Bundle-ManifestVersion: 2 -Bundle-Name: innogy SmartHome Binding -Bundle-SymbolicName: org.openhab.binding.innogysmarthome;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-ClassPath: ., - lib/google-http-client-1.22.0.jar, - lib/google-oauth-client-1.22.0.jar, - lib/google-http-client-jackson2-1.22.0.jar, - lib/httpclient-4.0.1.jar, - lib/httpcore-4.0.1.jar, - lib/jackson-core-2.1.3.jar -Import-Package: - com.google.common.collect, - com.google.gson, - com.google.gson.annotations, - javax.jmdns, - org.apache.commons.io, - org.apache.commons.lang, - org.apache.commons.lang.exception, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.jetty.client, - org.eclipse.jetty.io, - org.eclipse.jetty.util, - org.eclipse.jetty.util.component, - org.eclipse.jetty.util.ssl, - org.eclipse.jetty.websocket.api, - org.eclipse.jetty.websocket.api.annotations, - org.eclipse.jetty.websocket.client, - org.eclipse.jetty.websocket.common, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.config.discovery.mdns, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.innogysmarthome/OSGI-INF/.gitignore deleted file mode 100644 index 6722cd96e785a..0000000000000 --- a/addons/binding/org.openhab.binding.innogysmarthome/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/build.properties b/addons/binding/org.openhab.binding.innogysmarthome/build.properties deleted file mode 100644 index 4db0a6767d630..0000000000000 --- a/addons/binding/org.openhab.binding.innogysmarthome/build.properties +++ /dev/null @@ -1,14 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - lib/google-http-client-1.22.0.jar,\ - lib/google-http-client-jackson2-1.22.0.jar,\ - lib/google-oauth-client-1.22.0.jar,\ - NOTICE,\ - lib/httpclient-4.0.1.jar,\ - lib/httpcore-4.0.1.jar,\ - lib/jackson-core-2.1.3.jar - diff --git a/addons/binding/org.openhab.binding.innogysmarthome/lib/google-http-client-1.22.0.jar b/addons/binding/org.openhab.binding.innogysmarthome/lib/google-http-client-1.22.0.jar deleted file mode 100644 index a31a9b1e6e388..0000000000000 Binary files a/addons/binding/org.openhab.binding.innogysmarthome/lib/google-http-client-1.22.0.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.innogysmarthome/lib/google-http-client-jackson2-1.22.0.jar b/addons/binding/org.openhab.binding.innogysmarthome/lib/google-http-client-jackson2-1.22.0.jar deleted file mode 100644 index 64f0c1594ee54..0000000000000 Binary files a/addons/binding/org.openhab.binding.innogysmarthome/lib/google-http-client-jackson2-1.22.0.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.innogysmarthome/lib/google-oauth-client-1.22.0.jar b/addons/binding/org.openhab.binding.innogysmarthome/lib/google-oauth-client-1.22.0.jar deleted file mode 100644 index 93d284db25381..0000000000000 Binary files a/addons/binding/org.openhab.binding.innogysmarthome/lib/google-oauth-client-1.22.0.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.innogysmarthome/lib/httpclient-4.0.1.jar b/addons/binding/org.openhab.binding.innogysmarthome/lib/httpclient-4.0.1.jar deleted file mode 100644 index ad32285abc5bb..0000000000000 Binary files a/addons/binding/org.openhab.binding.innogysmarthome/lib/httpclient-4.0.1.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.innogysmarthome/lib/httpcore-4.0.1.jar b/addons/binding/org.openhab.binding.innogysmarthome/lib/httpcore-4.0.1.jar deleted file mode 100644 index 4aef35e2ffa17..0000000000000 Binary files a/addons/binding/org.openhab.binding.innogysmarthome/lib/httpcore-4.0.1.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.innogysmarthome/lib/jackson-core-2.1.3.jar b/addons/binding/org.openhab.binding.innogysmarthome/lib/jackson-core-2.1.3.jar deleted file mode 100644 index f47619f41fb07..0000000000000 Binary files a/addons/binding/org.openhab.binding.innogysmarthome/lib/jackson-core-2.1.3.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.innogysmarthome/pom.xml b/addons/binding/org.openhab.binding.innogysmarthome/pom.xml deleted file mode 100644 index 56c197817baba..0000000000000 --- a/addons/binding/org.openhab.binding.innogysmarthome/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.innogysmarthome - eclipse-plugin - - innogy SmartHome Binding - - diff --git a/addons/binding/org.openhab.binding.lgwebos/.classpath b/addons/binding/org.openhab.binding.lgwebos/.classpath deleted file mode 100644 index 55216c87f8a9b..0000000000000 --- a/addons/binding/org.openhab.binding.lgwebos/.classpath +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.lgwebos/.project b/addons/binding/org.openhab.binding.lgwebos/.project deleted file mode 100644 index fbc41a0904b09..0000000000000 --- a/addons/binding/org.openhab.binding.lgwebos/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.lgwebos - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.lgwebos/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.lgwebos/META-INF/MANIFEST.MF deleted file mode 100644 index ff143402c5b12..0000000000000 --- a/addons/binding/org.openhab.binding.lgwebos/META-INF/MANIFEST.MF +++ /dev/null @@ -1,36 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.lgwebos -Bundle-ClassPath: ., - lib/java-websocket-1.3.2.jar, - lib/json-20140107.jar, - lib/xpp3-1.1.4c.jar, - lib/Connect-SDK-Java-Core-1.0.jar -Bundle-ManifestVersion: 2 -Bundle-Name: LG webOS Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.lgwebos;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Export-Package: - org.openhab.binding.lgwebos.action -Import-Package: - javax.jmdns, - org.apache.commons.lang, - org.apache.http.client.methods, - org.apache.http.conn.util, - org.apache.http.message, - org.apache.http.protocol, - org.eclipse.jdt.annotation;resolution:=optional, - org.openhab.core.automation.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.net, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.openhab.binding.lgwebos.action, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.lgwebos/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.lgwebos/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.lgwebos/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.lgwebos/build.properties b/addons/binding/org.openhab.binding.lgwebos/build.properties deleted file mode 100644 index d8903fae3f3e8..0000000000000 --- a/addons/binding/org.openhab.binding.lgwebos/build.properties +++ /dev/null @@ -1,12 +0,0 @@ -source.. = src/main/java/,\ - src/main/resources/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - lib/json-20140107.jar,\ - lib/xpp3-1.1.4c.jar,\ - lib/java-websocket-1.3.2.jar,\ - lib/Connect-SDK-Java-Core-1.0.jar,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.lgwebos/lib/java-websocket-1.3.2.jar b/addons/binding/org.openhab.binding.lgwebos/lib/java-websocket-1.3.2.jar deleted file mode 100644 index dcf5858ac7d1d..0000000000000 Binary files a/addons/binding/org.openhab.binding.lgwebos/lib/java-websocket-1.3.2.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.lgwebos/lib/json-20140107.jar b/addons/binding/org.openhab.binding.lgwebos/lib/json-20140107.jar deleted file mode 100644 index 40a325dbadf46..0000000000000 Binary files a/addons/binding/org.openhab.binding.lgwebos/lib/json-20140107.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.lgwebos/lib/xpp3-1.1.4c.jar b/addons/binding/org.openhab.binding.lgwebos/lib/xpp3-1.1.4c.jar deleted file mode 100644 index 451ac82af4a30..0000000000000 Binary files a/addons/binding/org.openhab.binding.lgwebos/lib/xpp3-1.1.4c.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.lgwebos/pom.xml b/addons/binding/org.openhab.binding.lgwebos/pom.xml deleted file mode 100644 index 699eaf18f1697..0000000000000 --- a/addons/binding/org.openhab.binding.lgwebos/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.lgwebos - eclipse-plugin - - LG webOS Binding - - diff --git a/addons/binding/org.openhab.binding.loxone/.classpath b/addons/binding/org.openhab.binding.loxone/.classpath deleted file mode 100644 index 04f0184a01454..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.loxone/.project b/addons/binding/org.openhab.binding.loxone/.project deleted file mode 100644 index ef9dd408f15ab..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.loxone - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.loxone/ESH-INF/binding/binding.xml b/addons/binding/org.openhab.binding.loxone/ESH-INF/binding/binding.xml deleted file mode 100644 index da9176f98cd3f..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/ESH-INF/binding/binding.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - Loxone Binding - This is the binding for Loxone Miniserver - Pawel Pieczul - - diff --git a/addons/binding/org.openhab.binding.loxone/ESH-INF/thing/thing-types.xml b/addons/binding/org.openhab.binding.loxone/ESH-INF/thing/thing-types.xml deleted file mode 100644 index 1e852f0f6a28a..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/ESH-INF/thing/thing-types.xml +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - - IP gateway for Loxone Smarthome system - - - - - - Connection to Miniserver - - - - - Authentication and encryption parameters - - - - - Various timeout parameters - true - - - - - Various sizing parameters - true - - - - - network-address - Host address or IP of the Loxone Miniserver - - - - Web interface port of the Loxone Miniserver - 80 - - - - - Method used to authorize user in the Miniserver - 0 - - - - - - true - - - - User name on the Loxone Miniserver - - - - password - User password on the Loxone Miniserver. In token-based authentication this password will be cleared after token is acquired. - - - - Token acquired by the binding from the Miniserver. Applicable only in token-based authentication mode. - true - - - - - Time between binding initialization and first connection attempt (seconds, 0-120) - 1 - - - - Time between sending two consecutive keep-alive messages (seconds, 1-600) - 240 - - - - Time between failed websocket connect attempts (seconds, 0-600) - 10 - - - - Time to wait for a response from Miniserver to a request sent from the binding (seconds, 0-60) - 4 - - - - Time in seconds between user login error as a result of wrong name/password or no authority and next connection attempt (seconds, 0-3600) - 60 - - - - Time between connection close (as a result of some communication error) and next connection attempt (seconds, 0-3600) - 30 - - - - Websocket client's maximum binary message size in kB - 3072 - - - - Websocket client's maximum text message size in kB - 512 - - - - - - - Switch - - Loxone's virtual input of switch type and push button controls. - - - - Switch - - Loxone's digital information controls (InfoOnlyDigital, read-only). - - - - - Rollershutter - - Loxone's Jalousies (rollershutters, blinds). - - - - String - - Loxone's state information controls (TextState, read-only). - - - - - Number - - Loxone's time counter (TimedSwitch, read-only). - - - - - Number - - Loxone's state information controls (InfoOnlyAnalog, read-only). - - - - - Number - - Loxone's light controllers (LightController). - - - - - - - - - - - - - - - - - - Number - - Loxone's radio button controls (Radio). - - - - - Dimmer - - Loxone's dimmer control - - - - diff --git a/addons/binding/org.openhab.binding.loxone/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.loxone/META-INF/MANIFEST.MF deleted file mode 100644 index 3077be81871d6..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/META-INF/MANIFEST.MF +++ /dev/null @@ -1,44 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.loxone -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: . -Bundle-ManifestVersion: 2 -Bundle-Name: Loxone Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.loxone;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - com.google.gson, - com.google.gson.annotations, - org.apache.commons.codec, - org.apache.commons.codec.binary, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.jetty.http, - org.eclipse.jetty.util, - org.eclipse.jetty.util.component, - org.eclipse.jetty.websocket.api, - org.eclipse.jetty.websocket.api.annotations, - org.eclipse.jetty.websocket.api.extensions, - org.eclipse.jetty.websocket.client, - org.eclipse.jetty.websocket.common, - org.eclipse.jetty.websocket.common.extensions, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.config.discovery.upnp, - org.eclipse.smarthome.core.common, - org.eclipse.smarthome.core.id, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.link, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.io.transport.upnp, - org.jupnp, - org.jupnp.model.meta, - org.jupnp.model.types, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.loxone/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.loxone/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.loxone/README.md b/addons/binding/org.openhab.binding.loxone/README.md deleted file mode 100644 index 166d5dbf6de9c..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/README.md +++ /dev/null @@ -1,263 +0,0 @@ -# Loxone Binding - -This binding integrates [Loxone Miniserver](https://www.loxone.com/enen/products/miniserver-extensions/) with [openHAB](http://www.openhab.org/). -Miniserver is represented as a [Thing](https://www.openhab.org/docs/configuration/things.html). Miniserver controls, that are visible in the Loxone [UI](https://www.loxone.com/enen/kb/user-interface-configuration/), are exposed as openHAB channels. - -## Features - -The following features are currently supported: - -* [Discovery](https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol) of Miniservers available on the local network -* Creation of channels for Loxone controls that are exposed in the Loxone [UI](https://www.loxone.com/enen/kb/user-interface-configuration/) -* Tagging of channels and [items](https://www.openhab.org/docs/configuration/items.html) with tags that can be recognized by [Alexa](https://en.wikipedia.org/wiki/Amazon_Alexa) openHAB [skill](https://www.amazon.com/openHAB-Foundation/dp/B01MTY7Z5L), so voice can be used to command Loxone controls -* Management of a Websocket connection to the Miniserver and updating Thing status accordingly -* Updates of openHAB channel's state in runtime according to control's state changes on the Miniserver -* Passing channel commands to the Miniserver's controls -* Hash-based and token-based authentication methods -* Command encryption and response decryption - -## Things - -This binding supports [Loxone Miniservers](https://www.loxone.com/enen/products/miniserver-extensions/) for accessing controls that are configured in their UI. - -The Thing UID of automatically discovered Miniservers is: `loxone:miniserver:`, where `` is a serial number of the Miniserver (effectively this is the MAC address of its network interface). - -### Discovery - -[Loxone Miniservers](https://www.loxone.com/enen/products/miniserver-extensions/) are automatically discovered by the binding and put in the Inbox. [Discovery](https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol) is performed using [UPnP](https://en.wikipedia.org/wiki/Universal_Plug_and_Play) protocol. - -Before a Miniserver Thing can go online, it must be configured with a user name and a password of an account available on the Miniserver. -Please set them manually in Thing configuration after you add a new Miniserver Thing from your Inbox. - -### Manual configuration - -As an alternative to the automatic discovery process, Miniservers can be configured manually, through an entry in [.things file](https://www.openhab.org/docs/configuration/things.html#defining-things-using-files). -The entry should have the following syntax: - -`loxone:miniserver: [ user="", password="", host="", port=, ... ]` - -Where: - -* `` is a unique ID for your Miniserver (you can but do not have to use Miniserver's MAC address here) -* `` and `` are the credentials used to log into the Miniserver -* `` is a host name or IP of the Miniserver -* `` is a port of web services on the Miniserver (please notice that port, as a number, is not surrounded by quotation marks, while the other values described above are) -* `...` are optional advanced parameters - please refer to _Advanced parameters_ section at the end of this instruction for a list of available options - -Example 1 - minimal required configuration: - - `loxone:miniserver:504F2414780F [ user="kryten", password="jmc2017", host="loxone.local", port=80 ]` - -Example 2 - additionally keep alive period is set to 2 minutes and Websocket maximum binary message size to 8MB: - - `loxone:miniserver:504F2414780F [ user="kryten", password="jmc2017", host="192.168.0.210", port=80, keepAlivePeriod=120, maxBinMsgSize=8192 ]` - -### Thing Offline Reasons - -There can be following reasons why Miniserver status is `OFFLINE`: - -* __Configuration Error__ - * _Unknown host_ - * Miniserver host/ip address can't be resolved. No connection attempt will be made. - * _User authentication error_ - * Invalid user name or password or user not authorized to connect to the Miniserver. Binding will make another attempt to connect after some time. - * _Too many failed login attempts - stopped trying_ - * Miniserver locked out user for too many failed login attempts. In this case binding will stop trying to connect to the Miniserver. A new connection will be attempted only when user corrects user name or password in the configuration parameters. - * _Enter password to generate a new token_ - * Authentication using stored token failed - either token is wrong or it. A password must be reentered in the binding settings to acquire a new token. - * _Internal error_ - * Probably a code defect, collect debug data and submit an issue. Binding will try to reconnect, but with unknown chance for success. - * _Other_ - * An exception occured and its details will be displayed -* __Communication Error__ - * _Error communicating with Miniserver_ - * I/O error occurred during established communication with the Miniserver, most likely due to network connectivity issues, Miniserver going offline or Loxone Config is uploading a new configuration. A reconnect attempt will be made soon. Please consult detailed message against one of the following: - * _"Text message size &lsqbXX&rsqb exceeds maximum size &lsqbYY&rsqb"_ - adjust text message size in advanced parameters to be above XX value - * _"Binary message size &lsqbXX&rsqb exceeds maximum size &lsqbYY&rsqb"_ - adjust binary message size in advanced parameters to be above XX value - * _User authentication timeout_ - * Authentication procedure took too long time and Miniserver closed connection. It should not occur under normal conditions and may indicate performance issue on binding's OS side. - * _Timeout due to no activity_ - * Miniserver closed connection because there was no activity from binding. It should not occur under normal conditions, as it is prevented by sending keep-alive messages from the binding to the Miniserver. By default Miniserver's timeout is 5 minutes and period between binding's keep-alive messages is 4 minutes. If you see this error, try changing the keep-alive period in binding's configuration to a smaller value. - * _Other_ - * An exception occured and its details will be displayed - -### Security - -The binding supports the following authentication methods, which are selected automatically based on the firmware version. They can be also chosen manually in the advanced settings. - -| Method | Miniserver Firmware | Authentication | Encryption | Requirements | -|-------------|---------------------|--------------------------------------------------------------------------------|------------|-------------------------------------------------------| -| Hash-based | 8.x | HMAC-SHA1 hash on user and password | None | None | -| Token-based | 9.x | Token acquired on the first connection and used later instead of the password. | AES-256 | JRE must have unrestricted security policy configured | - -For the token-based authentication, the password is required only for the first login and acquiring the token. After the token is acquired, the password is cleared in the binding configuration. - -The acquired token will remain active for several weeks following the last succesful authentication with this token. If the connection is not established used during that period and the token expires, a user password has to be re-entered in the binding settings to acquire a new token. - -In case a websocket connection to the Miniserver remains active for the whole duration of the token's life span, the binding will refresh the token one day before token expiration, without the need of providing the password. - - -A method to enable unrestricted security policy depends on the JRE version and vendor, some examples can be found [here](https://www.petefreitag.com/item/844.cfm) and [here](https://stackoverflow.com/questions/41580489/how-to-install-unlimited-strength-jurisdiction-policy-files). - -## Channels - -This binding creates channels for controls that are [used in Loxone's user interface](https://www.loxone.com/enen/kb/user-interface-configuration/). -Currently supported controls are presented in the table below. - -| [Loxone API Control](https://www.loxone.com/enen/kb/api/) | Loxone Block-Functions | [Item Types](https://www.openhab.org/docs/concepts/items.html) | Supported Commands | -|-----------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------| -| Dimmer | [Dimmer](https://www.loxone.com/enen/kb/dimmer/) | `Dimmer` | `OnOffType.*`
`Percent` | -| InfoOnlyAnalog | Analog [virtual inputs](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) (virtual state) | `Number` | none (read-only value) | -| InfoOnlyDigital | Digital [virtual inputs](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) (virtual state) | `String` | none (read-only value) | -| Jalousie | Blinds, [Automatic Blinds](https://www.loxone.com/enen/kb/automatic-blinds/), Automatic Blinds Integrated | `Rollershutter` | `UpDown.*`
`StopMove.*`
`Percent` | -| LightController | [Lighting controller V1 (obsolete)](https://www.loxone.com/enen/kb/lighting-controller/), [Hotel lighting controller](https://www.loxone.com/enen/kb/hotel-lighting-controller/)
Additionally, for each configured output of a lighting controller, a new independent control (with own channel/item) will be created. | `Number` | `Decimal` (select lighting scene)
`UpDownType.*` (swipe through scenes)
`OnOffType.*` (select all off or all on scene) | -| LightControllerV2 | [Lighting controller](https://www.loxone.com/enen/kb/lighting-controller-v2/)
Additionally, for each configured output and for each mood of a lighting controller, a new independent control (with own channel/item) will be created. | `Number` | `Decimal` (select mood)
`UpDownType.*` (swipe through moods) | -| LightControllerV2 Mood | A mood defined for a [Lighting controller](https://www.loxone.com/enen/kb/lighting-controller-v2/). Each mood will have own channel and can be operated independently in order to allow mixing of moods. | `Switch` | `OnOffType.*` (mixes mood in or out of the controller) | -| Pushbutton | [Virtual inputs](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) of pushbutton type | `Switch` | `OnOffType.ON` (generates Pulse command) | -| Radio | [Radio button 8x and 16x](https://www.loxone.com/enen/kb/radio-buttons/) | `Number` | `Decimal` (select output number 1-8/16 or 0 for all outputs off)
`OnOffType.OFF` (all outputs off) | -| Switch | [Virtual inputs](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) of switch type
[Push-button](https://www.loxone.com/enen/kb/push-button/) | `Switch` | `OnOffType.*` | -| TextState | [State](https://www.loxone.com/enen/kb/state/) | `String` | none (read-only value) | -| TimedSwitch | [Stairwell light switch](https://www.loxone.com/enen/kb/stairwell-light-switch/) or [Multifunction switch](https://www.loxone.com/enen/kb/multifunction-switch/) | `Switch`

`Number` | `OnOffType.*` (ON sends pulse to Loxone)

Read-only countdown value to off | - -If your control is supported, but binding does not recognize it, please check if it is exposed in Loxone UI using [Loxone Config](https://www.loxone.com/enen/kb-cat/loxone-config/) application. - -Channel ID is defined in the following way: - -* `loxone:miniserver::` - -Channel label is defined in the following way: - -* For controls that belong to a room: ` / ` -* For controls without a room: `` - -## Advanced Parameters - -This section describes the optional advanced parameters that can be configured for a Miniserver. They can be set using UI (e.g. PaperUI) or in a .things file. -If a parameter is not explicitly defined, binding will use its default value. - -To define a parameter value in a .things file, please refer to it by parameter's ID, for example: - - keepAlivePeriod=120 - -### Security - -| ID | Name | Values | Default | Description | -|--------------|-----------------------|-------------------------------------------------|--------------|-------------------------------------------------------| -| `authMethod` | Authentication method | 0: Automatic
1: Hash-based
2: Token-based | 0: Automatic | A method used to authenticate user in the Miniserver. | - -### Timeouts - -Timeout values control various parts of Websocket connection management. -They can be tuned, when abnormal behavior of the binding is observed, which can be attributed to timing. -
- -| ID | Name | Range | Default | Description | -|-------------------|-----------------------------------------------|----------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `firstConDelay` | First connection delay | 0-120 s | 1 s | Time in seconds between binding initialization with all necessary parameters and first connection attempt. | -| `keepAlivePeriod` | Period between connection keep-alive messages | 1-600 s | 240 s | Time in seconds between sending two consecutive keep-alive messages, in order to inform Miniserver about active connection and prevent it from disconnecting. Miniserver default connection timeout is 5 minutes, so default is set to 4 minutes. | -| `connectErrDelay` | Connect error delay | 0-600 s | 10 s | Time in seconds between failed Websocket connect attempt and another attempt to connect. Websocket connection is established before authentication and data transfer. It can usually fail due to unreachable Miniserver. | -| `responseTimeout` | Response timeout | 0-60 s | 4 s | Time to wait for a response from Miniserver to a request sent from the binding. A request can be any of: websocket connect request, credentials hashing key request, configuration request, enabling of state updates (until initial states are received). If this time passed without the expected reaction from the Miniserver, the connection will be closed. A new connection attempt may be made, depending on the situation. | -| `userErrorDelay` | Authentication error delay | 0-3600 s | 60 s | Time in seconds between user authentication error and another connection attempt. User authentication error can be a result of a wrong name or password, or no authority granted to the user on the Miniserver. If this time is too short, Miniserver will eventually lock out the user for a longer period of time due to too many failed login attempts. This time should allow the administrator to fix the authentication issue without being locked out. Connection retry is required, because very rarely Miniserver seems to reject correct credentials, which are successful on a subsequent identical attempt. | -| `comErrorDelay` | Communication error delay | 0-3600 s | 30 s | Time in seconds between an active connection closes, as a result of a communication error, and next connection attempt. This relates to all types of network communication issues, which can occur and cease to exist randomly to the binding. It is desired that the binding monitors the situation and brings things back to online as soon as Miniserver is accessible. | - -### Sizes - -| ID | Name | Range | Default | Description | -|------------------|----------------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `maxBinMsgSize` | Maximum binary message size (kB) | 0-100 MB | 3072 (3 MB) | For Websocket client, a maximum size of a binary message that can be received from the Miniserver. If you get communication errors with a message indicating there are too long binary messages received, you may need to adjust this parameter. | -| `maxTextMsgSize` | Maximum text message size (kB) | 0-100 MB | 512 KB | For Websocket client, a maximum size of a text message that can be received from the Miniserver. If you get communication errors with a message indicating there are too long text messages received, you may need to adjust this parameter. | - -## Limitations - -* As there is no push button item type in openHAB, Loxone's push button is an openHAB's switch, which always generates a short pulse on changing its state to on. If you use simple UI mode and framework generates items for you, switches for push buttons will still be toggle switches. To change it to the push button style, you have to create item manually with `autoupdate=false` parameter. An example of such item definition is given in the _Items_ section above. - -## Automatic Configuration Example - -The simplest and quickest way of configuring a Loxone Miniserver with openHAB is to use automatic configuration features: - -* Make sure your Miniserver is up and running and on the same network segment as openHAB server. -* Add Loxone binding from the available `Add-ons`. -* In `Configuration/System` page, set `Item Linking` to `Simple Mode` (don't forget to save your choice). -* Add your Miniserver Thing from the `Inbox`, after automatic discovery is performed by the framework during binding initialization. -* Configure your Miniserver by editing Miniserver Thing in `Configuration/Things` page and providing user name and password. -* Miniserver Thing should go online. Channels and Items will be automatically created and configured. -* On the `Control` page, you can test Miniserver Items and interact with them. -* As the user interface, you may use [HABPanel](https://www.openhab.org/docs/configuration/habpanel.html), where all Miniserver's items are ready for picking up, using entirely the graphical user interface. - -## Manual Configuration Example - -A more advanced setup requires manual creation and editing of openHAB configuration files, according to the instructions provided in [configuration user guide](https://www.openhab.org/docs/configuration/). -In this example we will manually configure: - -* A Miniserver with serial number 504F2414780F, available at IP 192.168.0.220 and with web services port 80 -* A Miniserver's user named "kryten" and password "jmc2017" -* Items for: - * Temperature of the Miniserver - a Virtual Analog State functional block - * State of a garage door - a Virtual Digital State funtional block (ON=door open, OFF=door closed) - * Kitchen lights switch - a Switch Subcontrol at the AI1 output of a Lighting Controller functional block (with a tag recognizable by Alexa service) - * Pushbutton to switch all lights off - a Virtual Input of Pushbutton type functional block (pushbutton realized by adding `autoupdate="false"` parameter) - * Kitchen blinds - a Jalousie functional block - * Lighting scene - a Lighting Controller functional block - * Output valve selection for garden watering - 8x Radio Button functional block, where only one valve can be open at a time - * A text displaying current alarm's state - a State functional block - -### things/loxone.things: - -``` -loxone:miniserver:504F2414780F [ user="kryten", password="jmc2017", host="192.168.0.220", port=80 -``` - -### items/loxone.items: - -``` -// Type ID Label Icon Tags Settings - -Number Miniserver_Temp "Miniserver temperature: [%.1f °C]" {channel="loxone:miniserver:504F2414780F:0F2F2133-017D-3C82-FFFF203EB0C34B9E"} -Switch Garage_Door "Garage door [MAP(garagedoor.map):%s]" {channel="loxone:miniserver:504F2414780F:0F2F2133-017D-3C82-FFFF203EB0C34B9E"} -Switch Kitchen_Lights "Kitchen Lights" ["lighting"] {channel="loxone:miniserver:504F2414780F:0EC5E0CF-0255-6ABD-FFFF402FB0C24B9E_AI1"} -Switch Stair_Lights "Stair Lights" ["lighting"] {channel="loxone:miniserver:504F2414780F:0EC5E0CF-0255-31BD-FFFF402FB0C24B9E"} -Number Stair_Lights-1 "Stair Lights Deactivation Delay" ["lighting"] {channel="loxone:miniserver:504F2414780F:0EC5E0CF-0255-31BD-FFFF402FB0C24B9E-1"} -Switch Reset_Lights "Switch all lights off" ["lighting"] {channel="loxone:miniserver:504F2414780F:0F2F2133-01AD-3282-FFFF201EB0C24B9E",autoupdate="false"} -Rollershutter Kitchen_Blinds "Kitchen blinds" {channel="loxone:miniserver:504F2414780F:0F2E2123-014D-3232-FFEF204EB3C24B9E"} -Dimmer Kitchen_Dimmer "Kitchen dimmer" ["lighting"] {channel="loxone:miniserver:504F2414780F:0F2E2123-014D-3232-FFEF207EB3C24B9E"} -Number Light_Scene "Lighting scene" {channel="loxone:miniserver:504F2414780F:0FC4E0DF-0255-6ABD-FFFE403FB0C34B9E"} -Number Mood_Selector "Lighting mood" {channel="loxone:miniserver:504F2414780F:0FC4E0DF-0255-6ABD-FFFE203EA0C34B9E"} -Switch Mood_Enter_Home "Entering home" {channel="loxone:miniserver:504F2414780F:0FC4E0DF-0255-6ABD-FFFE203EA0C34B9E-M1"} -Switch Mood_Read_Book "Reading book" {channel="loxone:miniserver:504F2414780F:0FC4E0DF-0255-6ABD-FFFE203EA0C34B9E-M2"} -Switch Mood_Evening "Evening setup" {channel="loxone:miniserver:504F2414780F:0FC4E0DF-0255-6ABD-FFFE203EA0C34B9E-M3"} -Number Garden_Valve "Garden watering section" {channel="loxone:miniserver:504F2414780F:0FC5E0DF-0355-6AAD-FFFE403FB0C34B9E"} -String Alarm_State "Alarm state [%s]" {channel="loxone:miniserver:504F2414780F:0F2E2134-017D-3E82-FFFF433FB4A34B9E"} -``` - -### sitemaps/loxone.sitemap: - -``` -sitemap loxone label="Loxone Example Menu" -{ - Frame label="Demo Controls" { - Text item=Miniserver_Temp - Text item=Garage_Door - Switch item=Kitchen_Lights - Switch item=Reset_Lights - Switch item=Kitchen_Blinds - Slider item=Kitchen_Dimmer switchSupport - Switch item=Stairs_Light - Text item=Stairs_Light-1 - Selection item=Light_Scene mappings=[0="All off", 1="My scene 1", 2="My scene 2", 9="All on"] - Selection item=Mood_Selector - Switch item=Mood_Enter_Home - Switch item=Mood_Read_Book - Switch item=Mood_Evening - Setpoint item=Garden_Valve minValue=0 maxValue=8 step=1 - Text item=Alarm_State - } -} -``` - -### transform/garagedoor.map: - -```java -OFF=Closed -ON=Open --=Unknown -``` diff --git a/addons/binding/org.openhab.binding.loxone/build.properties b/addons/binding/org.openhab.binding.loxone/build.properties deleted file mode 100644 index 2802d321c602c..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE - diff --git a/addons/binding/org.openhab.binding.loxone/pom.xml b/addons/binding/org.openhab.binding.loxone/pom.xml deleted file mode 100644 index 3d839836fc098..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.loxone - eclipse-plugin - - Loxone Binding - - diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LoxoneBindingConstants.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LoxoneBindingConstants.java deleted file mode 100644 index a3fbb1cf91dd9..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LoxoneBindingConstants.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.thing.ThingTypeUID; - -/** - * Common constants used across the whole binding. - * - * @author Pawel Pieczul - Initial contribution - */ -@NonNullByDefault -public class LoxoneBindingConstants { - - public static final String BINDING_ID = "loxone"; - - // List of all Thing Type UIDs - public static final ThingTypeUID THING_TYPE_MINISERVER = new ThingTypeUID(BINDING_ID, "miniserver"); - - // Channel Type IDs - read/write - public static final String MINISERVER_CHANNEL_TYPE_SWITCH = "switchTypeId"; - public static final String MINISERVER_CHANNEL_TYPE_LIGHT_CTRL = "lightCtrlTypeId"; - public static final String MINISERVER_CHANNEL_TYPE_RADIO_BUTTON = "radioButtonTypeId"; - public static final String MINISERVER_CHANNEL_TYPE_ROLLERSHUTTER = "rollerShutterTypeId"; - public static final String MINISERVER_CHANNEL_TYPE_DIMMER = "dimmerTypeId"; - - // Channel Type IDs - read only - public static final String MINISERVER_CHANNEL_TYPE_RO_TEXT = "roTextTypeId"; - public static final String MINISERVER_CHANNEL_TYPE_RO_SWITCH = "roSwitchTypeId"; - public static final String MINISERVER_CHANNEL_TYPE_RO_ANALOG = "roAnalogTypeId"; - public static final String MINISERVER_CHANNEL_TYPE_RO_NUMBER = "roNumberTypeId"; - - // Miniserver properties and parameters - public static final String MINISERVER_PARAM_HOST = "host"; - public static final String MINISERVER_PARAM_PORT = "port"; - public static final String MINISERVER_PROPERTY_MINISERVER_NAME = "name"; - public static final String MINISERVER_PROPERTY_PROJECT_NAME = "project"; - public static final String MINISERVER_PROPERTY_CLOUD_ADDRESS = "cloudAddress"; - - // Location as configured on the Miniserver - it may be different to the Thing location property, which is user - // defined and influences the grouping of items in the UI - public static final String MINISERVER_PROPERTY_PHYSICAL_LOCATION = "physicalLocation"; -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LoxoneDynamicStateDescriptionProvider.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LoxoneDynamicStateDescriptionProvider.java deleted file mode 100644 index b2e1387411a04..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LoxoneDynamicStateDescriptionProvider.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal; - -import java.util.Locale; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.thing.Channel; -import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.thing.type.DynamicStateDescriptionProvider; -import org.eclipse.smarthome.core.types.StateDescription; -import org.osgi.service.component.annotations.Component; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Dynamic channel state description provider. - * Overrides the state description for the controls, which receive its configuration in the runtime. - * - * @author Pawel Pieczul - Initial contribution - */ -@Component(service = { DynamicStateDescriptionProvider.class, LoxoneDynamicStateDescriptionProvider.class }) -@NonNullByDefault -public class LoxoneDynamicStateDescriptionProvider implements DynamicStateDescriptionProvider { - - private Map descriptions = new ConcurrentHashMap<>(); - private Logger logger = LoggerFactory.getLogger(LoxoneDynamicStateDescriptionProvider.class); - - /** - * Set a state description for a channel. This description will be used when preparing the channel state by - * the framework for presentation. A previous description, if existed, will be replaced. - * - * @param channelUID - * channel UID - * @param description - * state description for the channel - */ - public void setDescription(ChannelUID channelUID, StateDescription description) { - logger.debug("Adding state description for channel {}", channelUID); - descriptions.put(channelUID, description); - } - - /** - * Clear all registered state descriptions - */ - public void removeAllDescriptions() { - logger.debug("Removing all state descriptions"); - descriptions.clear(); - } - - @Override - public @Nullable StateDescription getStateDescription(Channel channel, - @Nullable StateDescription originalStateDescription, @Nullable Locale locale) { - StateDescription description = descriptions.get(channel.getUID()); - logger.trace("Providing state description for channel {}", channel.getUID()); - return description; - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LoxoneHandlerFactory.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LoxoneHandlerFactory.java deleted file mode 100644 index e52e34a45aaed..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LoxoneHandlerFactory.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal; - -import java.util.Collections; -import java.util.Set; - -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; -import org.eclipse.smarthome.core.thing.binding.ThingHandler; -import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; -import org.openhab.binding.loxone.internal.handler.LoxoneMiniserverHandler; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Reference; - -/** - * Factory responsible for creating Loxone things (Miniservers) and their handlers ({@link LoxoneMiniserverHandler} - * - * @author Pawel Pieczul - Initial contribution - */ -@Component(service = ThingHandlerFactory.class, configurationPid = "binding.loxone") -public class LoxoneHandlerFactory extends BaseThingHandlerFactory { - - public static final Set SUPPORTED_THING_TYPES_UIDS = Collections - .singleton(LoxoneBindingConstants.THING_TYPE_MINISERVER); - - private LoxoneDynamicStateDescriptionProvider dynamicStateDescriptionProvider; - - @Override - public boolean supportsThingType(ThingTypeUID thingTypeUID) { - return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); - } - - @Override - protected ThingHandler createHandler(Thing thing) { - ThingTypeUID uid = thing.getThingTypeUID(); - if (uid.equals(LoxoneBindingConstants.THING_TYPE_MINISERVER)) { - LoxoneMiniserverHandler handler = new LoxoneMiniserverHandler(thing, dynamicStateDescriptionProvider); - return handler; - } - return null; - } - - @Reference - protected void setDynamicStateDescriptionProvider(LoxoneDynamicStateDescriptionProvider provider) { - this.dynamicStateDescriptionProvider = provider; - } - - protected void unsetDynamicStateDescriptionProvider(LoxoneDynamicStateDescriptionProvider provider) { - this.dynamicStateDescriptionProvider = null; - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/config/LoxoneMiniserverConfig.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/config/LoxoneMiniserverConfig.java deleted file mode 100644 index 003bebd00c794..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/config/LoxoneMiniserverConfig.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.config; - -import org.openhab.binding.loxone.internal.handler.LoxoneMiniserverHandler; - -/** - * Configuration of a Loxone Miniserver ({@link LoxoneMiniserverHandler}) - * - * @author Pawel Pieczul - Initial contribution - * - */ -public class LoxoneMiniserverConfig { - /** - * Host address or IP of the Miniserver - */ - public String host; - /** - * Port of web service of the Miniserver - */ - public int port; - /** - * User name used to log into the Miniserver - */ - public String user; - /** - * Password used to log into the Miniserver - */ - public String password; - /** - * Authentication token acquired from the Miniserver - */ - public String authToken; - /** - * Time in seconds between binding initialization and first connection attempt - */ - public int firstConDelay; - /** - * Time in seconds between sending two consecutive keep-alive messages - */ - public int keepAlivePeriod; - /** - * Time in seconds between failed websocket connect attempts - */ - public int connectErrDelay; - /** - * Time to wait for Miniserver response to a request sent from the binding - */ - public int responseTimeout; - /** - * Time in seconds between user login error as a result of wrong name/password or no authority and next connection - * attempt - */ - public int userErrorDelay; - /** - * Time in seconds between connection close (as a result of some communication error) and next connection attempt - */ - public int comErrorDelay; - /** - * Websocket client's max binary message size in kB - */ - public int maxBinMsgSize; - /** - * Websocket client's max text message size in kB - */ - public int maxTextMsgSize; - public int authMethod; -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxCategory.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxCategory.java deleted file mode 100644 index 89d8bf353d43b..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxCategory.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -/** - * Category of Loxone Miniserver's {@link LxControl} object. - * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxCategory extends LxContainer { - - /** - * Various categories that Loxone Miniserver's control can belong to. - * - * @author Pawel Pieczul - initial contribution - */ - public enum CategoryType { - /** - * Category for lights - */ - LIGHTS, - /** - * Category for shading / rollershutter / blinds - */ - SHADING, - /** - * Unknown category - */ - UNDEFINED - } - - private CategoryType type; - - /** - * Create a {@link LxCategory} object - * - * @param uuid - * UUID of this category object on the Miniserver - * @param name - * name of the category - * @param type - * type of the category, as retrieved from the Miniserver - */ - public LxCategory(LxUuid uuid, String name, String type) { - super(uuid, name); - setType(type); - } - - /** - * Obtain the type of this category - * - * @return - * type of category - */ - public CategoryType getType() { - return type; - } - - /** - * Set the type of this category - * - * @param type - * new type to set as received from Miniserver - */ - void setType(String type) { - String tl = type.toLowerCase(); - if (tl.equals("lights")) { - this.type = CategoryType.LIGHTS; - } else if (tl.equals("shading")) { - this.type = CategoryType.SHADING; - } else { - this.type = CategoryType.UNDEFINED; - } - getUuid().setUpdate(true); - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxContainer.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxContainer.java deleted file mode 100644 index 2c6c21636766b..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxContainer.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.util.HashSet; -import java.util.Set; - -/** - * Container on Loxone Miniserver that groups {@link LxControl} objects. - *

- * Examples of containers are rooms and categories. - * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxContainer { - private LxUuid uuid; - private String name; - private Set controls = new HashSet<>(); - - /** - * Create a new container with given uuid and name - * - * @param uuid - * UUID of the container as received from the Miniserver - * @param name - * name of the container as received from the Miniserver - */ - LxContainer(LxUuid uuid, String name) { - this.uuid = uuid; - this.name = name; - } - - /** - * Obtain container's current name - * - * @return - * container's current name - */ - public String getName() { - return name; - } - - /** - * Obtain container's UUID (assigned by Loxone Miniserver) - * - * @return - * container's UUID - */ - public LxUuid getUuid() { - return uuid; - } - - /** - * Update container's name - * - * @param name - * a new name of the container - */ - void setName(String name) { - this.name = name; - uuid.setUpdate(true); - } - - /** - * Add a new control to this container or mark existing control's and container's UUIDs as updated. - * - * @param control - * control to be added or updated - */ - void addOrUpdateControl(LxControl control) { - uuid.setUpdate(true); - for (LxControl c : controls) { - if (control.equals(c)) { - c.getUuid().setUpdate(true); - return; - } - } - controls.add(control); - control.getUuid().setUpdate(true); - } - - /** - * Removes a control from the container - * - * @param control - * control object to remove from the container - * @return - * true if control object existed in the container and was removed - */ - boolean removeControl(LxControl control) { - return controls.remove(control); - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControl.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControl.java deleted file mode 100644 index 8bb1d70da03be..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControl.java +++ /dev/null @@ -1,308 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; - -/** - * A control of Loxone Miniserver. - *

- * It represents a control object on the Miniserver. Controls can represent an input, functional block or an output of - * the Miniserver, that is marked as visible in the Loxone UI. Controls can belong to a {@link LxContainer} room and a - * {@link LxCategory} category. - * - * @author Pawel Pieczul - initial contribution - * - */ -public abstract class LxControl { - - /** - * This class is used to instantiate a particular control object by the {@link LxControlFactory} - * - * @author Pawel Pieczul - initial contribution - * - */ - abstract static class LxControlInstance { - /** - * Creates an instance of a particular control class. - * - * @param client - * websocket client to facilitate communication with Miniserver - * @param uuid - * UUID of this control - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * Room that this control belongs to - * @param category - * Category that this control belongs to - * @return - * a newly created control object - */ - abstract LxControl create(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, - LxCategory category); - - /** - * Return a type name for this control. - * - * @return - * type name (as used on the Miniserver) - */ - abstract String getType(); - } - - private String name; - private LxContainer room; - private LxCategory category; - private Map states = new HashMap<>(); - - LxUuid uuid; - LxWsClient socketClient; - Logger logger = LoggerFactory.getLogger(LxControl.class); - Map subControls = new HashMap<>(); - - /** - * Create a Miniserver's control object. - * - * @param client - * websocket client to facilitate communication with Miniserver - * @param uuid - * UUID of this control - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * Room that this control belongs to - * @param category - * Category that this control belongs to - */ - LxControl(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - logger.trace("Creating new LxControl: {}", json.type); - socketClient = client; - this.uuid = uuid; - update(json, room, category); - } - - /** - * Gets value of a state object of given name, if exists - * - * @param name - * name of state object - * @return - * state object's value - */ - Double getStateValue(String name) { - LxControlState state = getState(name); - if (state != null) { - return state.getValue(); - } - return null; - } - - /** - * Gets text value of a state object of given name, if exists - * - * @param name - * name of state object - * @return - * state object's text value - */ - String getStateTextValue(String name) { - LxControlState state = getState(name); - if (state != null) { - return state.getTextValue(); - } - return null; - } - - /** - * Adds a listener for a particular state, if the state exists for the control. - * - * @param stateName - * name of the state to add listener for - * @param listener - * listener to listen for state changes - * @return - * state that listener was added to or null if no such state - */ - public LxControlState addStateListener(String stateName, LxControlStateListener listener) { - LxControlState state = getState(stateName); - if (state != null) { - state.addListener(listener); - } else { - logger.debug("Attempt to add listener for not existing state {}", stateName); - } - return state; - } - - public Map getSubControls() { - return subControls; - } - - public Map getStates() { - return states; - } - - /** - * Call when control is no more needed - unlink it from containers - */ - public void dispose() { - if (room != null) { - room.removeControl(this); - } - if (category != null) { - category.removeControl(this); - } - for (LxControl control : subControls.values()) { - control.dispose(); - } - } - - /** - * Obtain control's name - * - * @return - * Human readable name of control - */ - public String getName() { - return name; - } - - /** - * Obtain UUID of this control - * - * @return - * UUID - */ - public LxUuid getUuid() { - return uuid; - } - - /** - * Obtain room that this control belongs to - * - * @return - * Control's room or null if no room - */ - public LxContainer getRoom() { - return room; - } - - /** - * Obtain category of this control - * - * @return - * Control's category or null if no category - */ - public LxCategory getCategory() { - return category; - } - - /** - * Compare UUID's of two controls - - * - * @param object - * Object to compare with - * @return - * true if UUID of two objects are equal - */ - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object == null) { - return false; - } - if (object.getClass() != getClass()) { - return false; - } - LxControl c = (LxControl) object; - return Objects.equals(c.getUuid(), getUuid()); - } - - /** - * Hash code of the control is equal to its UUID's hash code - */ - @Override - public int hashCode() { - return getUuid().hashCode(); - } - - /** - * Update Miniserver's control in runtime. - * - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * New room that this control belongs to - * @param category - * New category that this control belongs to - */ - void update(LxJsonControl json, LxContainer room, LxCategory category) { - logger.trace("Updating LxControl: {}", json.type); - - this.name = json.name; - this.room = room; - this.category = category; - uuid.setUpdate(true); - if (room != null) { - room.addOrUpdateControl(this); - } - if (category != null) { - category.addOrUpdateControl(this); - } - - // retrieve all states from the configuration - if (json.states != null) { - logger.trace("Reading states for LxControl: {}", json.type); - - for (Map.Entry jsonState : json.states.entrySet()) { - JsonElement element = jsonState.getValue(); - if (element instanceof JsonArray) { - // temperature state of intelligent home controller object is the only - // one that has state represented as an array, as this is not implemented - // yet, we will skip this state - continue; - } - String value = element.getAsString(); - if (value != null) { - LxUuid id = new LxUuid(value); - String name = jsonState.getKey().toLowerCase(); - LxControlState state = states.get(name); - if (state == null) { - logger.trace("New state for LxControl {}: {}", json.type, name); - state = new LxControlState(id, name, this); - } else { - logger.trace("Existing state for LxControl {}: {}", json.type, name); - state.getUuid().setUpdate(true); - state.setName(name); - } - states.put(name, state); - } - } - } - } - - private LxControlState getState(String name) { - return states.get(name); - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlAbstractController.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlAbstractController.java deleted file mode 100644 index b3ab61b5cf2bd..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlAbstractController.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.util.ArrayList; -import java.util.List; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -/** - * A class that represents controllers, which contain sub-controls, which are also {@link LxControl} - * - * @author Pawel Pieczul - initial contribution - * - */ -abstract class LxControlAbstractController extends LxControl { - /** - * Create controller object. - * - * @param client - * communication client used to send commands to the Miniserver - * @param uuid - * controller's UUID - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * room to which controller belongs - * @param category - * category to which controller belongs - */ - LxControlAbstractController(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, - LxCategory category) { - super(client, uuid, json, room, category); - } - - /** - * Update Miniserver's controller in runtime. - * - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * New room that this control belongs to - * @param category - * New category that this control belongs to - */ - @Override - void update(LxJsonControl json, LxContainer room, LxCategory category) { - super.update(json, room, category); - - for (LxControl control : subControls.values()) { - control.uuid.setUpdate(false); - } - if (json.subControls != null) { - for (LxJsonControl subControl : json.subControls.values()) { - // recursively create a subcontrol as a new control - subControl.room = json.room; - subControl.cat = json.cat; - LxUuid uuid = new LxUuid(subControl.uuidAction); - if (subControls.containsKey(uuid)) { - subControls.get(uuid).update(subControl, room, category); - } else { - LxControl control = LxControlFactory.createControl(socketClient, uuid, subControl, room, category); - if (control != null) { - subControls.put(control.uuid, control); - } - } - } - } - List toRemove = new ArrayList<>(); - for (LxControl control : subControls.values()) { - if (!control.uuid.getUpdate()) { - toRemove.add(control.uuid); - } - } - for (LxUuid id : toRemove) { - subControls.remove(id); - } - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlDimmer.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlDimmer.java deleted file mode 100644 index 233c781e8fd67..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlDimmer.java +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.io.IOException; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -/** - * A dimmer type of control on Loxone Miniserver. - *

- * According to Loxone API documentation, a dimmer control is: - *

    - *
  • a virtual input of dimmer type - *
- * - * @author Stephan Brunner - * - */ -public class LxControlDimmer extends LxControl { - - static class Factory extends LxControlInstance { - @Override - LxControl create(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - return new LxControlDimmer(client, uuid, json, room, category); - } - - @Override - String getType() { - return TYPE_NAME; - } - } - - /** - * A name by which Miniserver refers to dimmer controls - */ - private static final String TYPE_NAME = "dimmer"; - /** - * States - */ - private static final String STATE_POSITION = "position"; - private static final String STATE_MIN = "min"; - private static final String STATE_MAX = "max"; - - /** - * Command string used to set the dimmer ON - */ - private static final String CMD_ON = "On"; - /** - * Command string used to set the dimmer to OFF - */ - private static final String CMD_OFF = "Off"; - - /** - * Create dimmer control object. - * - * @param client - * communication client used to send commands to the Miniserver - * @param uuid - * dimmer's UUID - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * room to which dimmer belongs - * @param category - * category to which dimmer belongs - */ - LxControlDimmer(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - super(client, uuid, json, room, category); - } - - /** - * Sets the current position of the dimmer - * - * @param position - * position to move to (0-100, 0 - full off, 100 - full on) - * @throws IOException - * error communicating with the Miniserver - */ - public void setPosition(Double position) throws IOException { - Double loxonePosition = mapOHToLoxone(position); - if (loxonePosition != null) { - socketClient.sendAction(uuid, loxonePosition.toString()); - } - } - - /** - * Sets the dimmer to on - * - * @throws IOException - * error communicating with the Miniserver - */ - public void on() throws IOException { - socketClient.sendAction(uuid, CMD_ON); - } - - /** - * Sets the dimmer to off - * - * @throws IOException - * error communicating with the Miniserver - */ - public void off() throws IOException { - socketClient.sendAction(uuid, CMD_OFF); - } - - /** - * Get current value of the dimmer state. - * - * @return - * 0 - full off, 100 - full on - */ - public Double getPosition() { - return mapLoxoneToOH(getStateValue(STATE_POSITION)); - } - - private Double getMax() { - return getStateValue(STATE_MAX); - } - - private Double getMin() { - return getStateValue(STATE_MIN); - } - - private Double mapLoxoneToOH(Double loxoneValue) { - if (loxoneValue != null) { - // 0 means turn dimmer off, any value above zero should be mapped from min-max range - if (Double.compare(loxoneValue, 0.0) == 0) { - return 0.0; - } - Double max = getMax(); - Double min = getMin(); - if (max != null && min != null) { - return (loxoneValue - min) * ((max - min) / 100); - } - } - return null; - } - - private Double mapOHToLoxone(Double ohValue) { - if (ohValue != null) { - // 0 means turn dimmer off, any value above zero should be mapped to min-max range - if (Double.compare(ohValue, 0.0) == 0) { - return 0.0; - } - Double max = getMax(); - Double min = getMin(); - if (max != null && min != null) { - double value = min + (ohValue / ((max - min) / 100)); - return value; // no rounding to integer value is needed as loxone is accepting floating point values - } - } - return null; - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlFactory.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlFactory.java deleted file mode 100644 index cc43095e93230..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlFactory.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.util.HashMap; -import java.util.Map; - -import org.openhab.binding.loxone.internal.core.LxControl.LxControlInstance; -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -/** - * A factory of controls of Loxone Miniserver. - * It creates various types of control objects based on control type received from Miniserver. - * - * @author Pawel Pieczul - * - */ -class LxControlFactory { - static { - controls = new HashMap<>(); - add(new LxControlDimmer.Factory()); - add(new LxControlInfoOnlyAnalog.Factory()); - add(new LxControlInfoOnlyDigital.Factory()); - add(new LxControlJalousie.Factory()); - add(new LxControlLightController.Factory()); - add(new LxControlLightControllerV2.Factory()); - add(new LxControlPushbutton.Factory()); - add(new LxControlRadio.Factory()); - add(new LxControlSwitch.Factory()); - add(new LxControlTextState.Factory()); - add(new LxControlTimedSwitch.Factory()); - } - - private static Map controls; - - /** - * Create a {@link LxControl} object for a control received from the Miniserver - * - * @param client - * websocket client to facilitate communication with Miniserver - * @param uuid - * UUID of the control to be created - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * Room that this control belongs to - * @param category - * Category that this control belongs to - * @return - * created control object or null if error - */ - static LxControl createControl(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, - LxCategory category) { - if (json == null || json.type == null || json.name == null) { - return null; - } - String type = json.type.toLowerCase(); - LxControlInstance control = controls.get(type); - if (control != null) { - return control.create(client, uuid, json, room, category); - } - return null; - } - - private static void add(LxControlInstance control) { - controls.put(control.getType(), control); - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlInfoOnlyAnalog.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlInfoOnlyAnalog.java deleted file mode 100644 index 7f1a77810e71b..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlInfoOnlyAnalog.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -/** - * An InfoOnlyAnalog type of control on Loxone Miniserver. - *

- * According to Loxone API documentation, this control covers analog virtual states only. This control does not send any - * commands to the Miniserver. It can be used to read a formatted representation of an analog virtual state. - * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxControlInfoOnlyAnalog extends LxControl { - - static class Factory extends LxControlInstance { - @Override - LxControl create(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - return new LxControlInfoOnlyAnalog(client, uuid, json, room, category); - } - - @Override - String getType() { - return TYPE_NAME; - } - } - - /** - * A name by which Miniserver refers to analog virtual state controls - */ - private static final String TYPE_NAME = "infoonlyanalog"; - /** - * InfoOnlyAnalog state with current value - */ - private static final String STATE_VALUE = "value"; - /** - * InfoOnlyAnalog state with error value - */ - @SuppressWarnings("unused") - private static final String STATE_ERROR = "error"; - - private String format; - - /** - * Create InfoOnlyAnalog control object. - * - * @param client - * communication client used to send commands to the Miniserver - * @param uuid - * control's UUID - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * room to which control belongs - * @param category - * category to which control belongs - */ - LxControlInfoOnlyAnalog(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - super(client, uuid, json, room, category); - } - - /** - * Update Miniserver's control in runtime. - * - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * New room that this control belongs to - * @param category - * New category that this control belongs to - */ - @Override - void update(LxJsonControl json, LxContainer room, LxCategory category) { - super.update(json, room, category); - if (json.details != null && json.details.format != null) { - format = json.details.format; - } else { - format = "%.1f"; - } - } - - /** - * Obtain current value of an analog virtual state, expressed in a format configured on the Miniserver - * - * @return - * string for the value of the state or null if current value is not compatible with this control - */ - public String getFormattedValue() { - Double value = getStateValue(STATE_VALUE); - if (value != null) { - return String.format(format, value); - } - return null; - } - - /** - * Obtain format string used to convert control's value into text - * - * @return - * string with format - */ - public String getFormatString() { - return format; - } - - /** - * Obtain current value of an analog virtual state, expressed as a number - * - * @return - * value of the state or null if current value is not compatible with this control - */ - public Double getValue() { - return getStateValue(STATE_VALUE); - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlInfoOnlyDigital.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlInfoOnlyDigital.java deleted file mode 100644 index f63801580d095..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlInfoOnlyDigital.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -/** - * An InfoOnlyDigital type of control on Loxone Miniserver. - *

- * According to Loxone API documentation, this control covers digital virtual states only. This control does not send - * any commands to the Miniserver. It can be used to read a formatted representation of a digital virtual state. - * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxControlInfoOnlyDigital extends LxControl { - - static class Factory extends LxControlInstance { - @Override - LxControl create(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - return new LxControlInfoOnlyDigital(client, uuid, json, room, category); - } - - @Override - String getType() { - return TYPE_NAME; - } - } - - /** - * A name by which Miniserver refers to digital virtual state controls - */ - private static final String TYPE_NAME = "infoonlydigital"; - /** - * InfoOnlyDigital has one state that can be on/off - */ - private static final String STATE_ACTIVE = "active"; - - private String textOn; - private String textOff; - - /** - * Create InfoOnlyDigital control object. - * - * @param client - * communication client used to send commands to the Miniserver - * @param uuid - * control's UUID - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * room to which control belongs - * @param category - * category to which control belongs - */ - LxControlInfoOnlyDigital(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, - LxCategory category) { - super(client, uuid, json, room, category); - } - - /** - * Update Miniserver's control in runtime. - * - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * New room that this control belongs to - * @param category - * New category that this control belongs to - */ - @Override - void update(LxJsonControl json, LxContainer room, LxCategory category) { - super.update(json, room, category); - if (json.details != null && json.details.text != null) { - textOn = json.details.text.on; - textOff = json.details.text.off; - } - } - - /** - * Obtain current value of the virtual state, expressed in a format configured on the Miniserver - * - * @return - * string for on/off value of the state or null if current value is not available - */ - public String getFormattedValue() { - Double value = getStateValue(STATE_ACTIVE); - if (value != null) { - if (value == 0) { - return textOff; - } else if (value == 1) { - return textOn; - } - } - return null; - } - - /** - * Obtain current value of a digital virtual state - * - * @return - * 1 for ON, 0 for OFF and -1 if current value is not available - */ - public Double getValue() { - return getStateValue(STATE_ACTIVE); - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlJalousie.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlJalousie.java deleted file mode 100644 index 574205e4253a3..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlJalousie.java +++ /dev/null @@ -1,235 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.io.IOException; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -/** - * A jalousie type of control on Loxone Miniserver. - *

- * According to Loxone API documentation, a jalousie control covers: - *

    - *
  • Blinds - *
  • Automatic blinds - *
  • Automatic blinds integrated - *
- * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxControlJalousie extends LxControl implements LxControlStateListener { - - static class Factory extends LxControlInstance { - @Override - LxControl create(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - return new LxControlJalousie(client, uuid, json, room, category); - } - - @Override - String getType() { - return TYPE_NAME; - } - } - - /** - * A name by which Miniserver refers to jalousie controls - */ - private static final String TYPE_NAME = "jalousie"; - - /** - * Jalousie is moving up - */ - private static final String STATE_UP = "up"; - /** - * Jalousie is moving down - */ - private static final String STATE_DOWN = "down"; - /** - * The position of the Jalousie, a number from 0 to 1 - * Jalousie upper position = 0 - * Jalousie lower position = 1 - */ - private static final String STATE_POSITION = "position"; - /** - * The shade position of the Jalousie (blinds), a number from 0 to 1 - * Blinds are not shaded = 0 - * Blinds are shaded = 1 - */ - @SuppressWarnings("unused") - private static final String STATE_SHADE_POSITION = "shadeposition"; - /** - * Only used by ones with Autopilot, this represents the safety shutdown - */ - @SuppressWarnings("unused") - private static final String STATE_SAFETY_ACTIVE = "safetyactive"; - /** - * Only used by ones with Autopilot - */ - @SuppressWarnings("unused") - private static final String STATE_AUTO_ALLOWED = "autoallowed"; - /** - * Only used by ones with Autopilot - */ - @SuppressWarnings("unused") - private static final String STATE_AUTO_ACTIVE = "autoactive"; - /** - * Only used by ones with Autopilot, this represents the output QI in Loxone Config - */ - @SuppressWarnings("unused") - private static final String STATE_LOCKED = "locked"; - - /** - * Command string used to set control's state to Down - */ - @SuppressWarnings("unused") - private static final String CMD_DOWN = "Down"; - /** - * Command string used to set control's state to Up - */ - @SuppressWarnings("unused") - private static final String CMD_UP = "Up"; - /** - * Command string used to set control's state to Full Down - */ - private static final String CMD_FULL_DOWN = "FullDown"; - /** - * Command string used to set control's state to Full Up - */ - private static final String CMD_FULL_UP = "FullUp"; - /** - * Command string used to stop rollershutter - */ - private static final String CMD_STOP = "Stop"; - - private Double targetPosition; - - /** - * Create jalousie control object. - * - * @param client - * communication client used to send commands to the Miniserver - * @param uuid - * jalousie's UUID - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * room to which jalousie belongs - * @param category - * category to which jalousie belongs - */ - LxControlJalousie(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - super(client, uuid, json, room, category); - addStateListener(STATE_POSITION, this); - } - - /** - * Set rollershutter (jalousie) to full up position. - *

- * Sends a command to operate the rollershutter. - * - * @throws IOException - * when something went wrong with communication - */ - public void fullUp() throws IOException { - socketClient.sendAction(uuid, CMD_FULL_UP); - } - - /** - * Set rollershutter (jalousie) to full down position. - *

- * Sends a command to operate the rollershutter. - * - * @throws IOException - * when something went wrong with communication - */ - public void fullDown() throws IOException { - socketClient.sendAction(uuid, CMD_FULL_DOWN); - } - - /** - * Stop movement of the rollershutter (jalousie) - *

- * Sends a command to operate the rollershutter. - * - * @throws IOException - * when something went wrong with communication - */ - public void stop() throws IOException { - socketClient.sendAction(uuid, CMD_STOP); - } - - /** - * Move the rollershutter (jalousie) to a desired position. - *

- * The jalousie will start moving in the desired direction based on the current position. It will stop moving once - * there is a state update event received with value above/below (depending on direction) or equal to the set - * position. - * - * @param position - * end position to move jalousie to, floating point number from 0..1 (0-fully closed to 1-fully open) - * @throws IOException - * when something went wrong with communication - */ - public void moveToPosition(Double position) throws IOException { - Double currentPosition = getPosition(); - if (currentPosition != null && currentPosition >= 0 && currentPosition <= 1) { - if (currentPosition > position) { - logger.debug("Moving jalousie up from {} to {}", currentPosition, position); - targetPosition = position; - fullUp(); - } else if (currentPosition < position) { - logger.debug("Moving jalousie down from {} to {}", currentPosition, position); - targetPosition = position; - fullDown(); - } - } - } - - /** - * Get current position of the rollershutter (jalousie) - * - * @return - * a floating point number from range 0-fully closed to 1-fully open or null if position not available - */ - public Double getPosition() { - return getStateValue(STATE_POSITION); - } - - /** - * Monitor jalousie position against desired target position and stop it if target position is reached. - */ - @Override - public void onStateChange(LxControlState state) { - // check position changes - if (STATE_POSITION.equals(state.getName()) && targetPosition != null && targetPosition > 0 - && targetPosition < 1) { - // see in which direction jalousie is moving - Double currentPosition = state.getValue(); - Double upValue = getStateValue(STATE_UP); - Double downValue = getStateValue(STATE_DOWN); - if (currentPosition != null && upValue != null && downValue != null) { - if (((upValue == 1) && (currentPosition <= targetPosition)) - || ((downValue == 1) && (currentPosition >= targetPosition))) { - targetPosition = null; - try { - stop(); - } catch (IOException e) { - logger.debug("Error stopping jalousie when meeting target position."); - } - } - } - } - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlLightController.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlLightController.java deleted file mode 100644 index aea49b30c0f5a..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlLightController.java +++ /dev/null @@ -1,233 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.io.IOException; -import java.util.Map; -import java.util.TreeMap; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -/** - * A Light Controller type of control on Loxone Miniserver. - *

- * According to Loxone API documentation, a light controller is one of following functional blocks: - *

    - *
  • Lighting Controller - *
  • Hotel Lighting Controller - *
- * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxControlLightController extends LxControlAbstractController implements LxControlStateListener { - - static class Factory extends LxControlInstance { - @Override - LxControl create(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - return new LxControlLightController(client, uuid, json, room, category); - } - - @Override - String getType() { - return TYPE_NAME; - } - } - - /** - * Number of scenes supported by the Miniserver. Indexing starts with 0 to NUM_OF_SCENES-1. - */ - public static final int NUM_OF_SCENES = 10; - - /** - * A name by which Miniserver refers to light controller controls - */ - private static final String TYPE_NAME = "lightcontroller"; - - /** - * Current active scene number (0-9) - */ - private static final String STATE_ACTIVE_SCENE = "activescene"; - /** - * List of available scenes (public state, so user can monitor scene list updates) - */ - public static final String STATE_SCENE_LIST = "scenelist"; - /** - * Command string used to set control's state to ON - */ - private static final String CMD_ON = "On"; - /** - * Command string used to set control's state to OFF - */ - private static final String CMD_OFF = "Off"; - /** - * Command string used to go to the next scene - */ - private static final String CMD_NEXT_SCENE = "plus"; - /** - * Command string used to go to the previous scene - */ - private static final String CMD_PREVIOUS_SCENE = "minus"; - private static final int SCENE_ALL_ON = 9; - - private Map sceneNames = new TreeMap<>(); - private Integer movementScene; - - /** - * Create lighting controller object. - * - * @param client - * communication client used to send commands to the Miniserver - * @param uuid - * controller's UUID - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * room to which controller belongs - * @param category - * category to which controller belongs - */ - LxControlLightController(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, - LxCategory category) { - super(client, uuid, json, room, category); - // sub-controls of this control have been created when update() method was called by super class constructor - addStateListener(STATE_SCENE_LIST, this); - } - - /** - * Update Miniserver's control in runtime. - * - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * New room that this control belongs to - * @param category - * New category that this control belongs to - */ - @Override - void update(LxJsonControl json, LxContainer room, LxCategory category) { - super.update(json, room, category); - if (json.details != null) { - this.movementScene = json.details.movementScene; - } - } - - /** - * Set all outputs to ON. - * - * @throws IOException - * when something went wrong with communication - */ - public void allOn() throws IOException { - socketClient.sendAction(uuid, CMD_ON); - } - - /** - * Set all outputs to OFF. - * - * @throws IOException - * when something went wrong with communication - */ - public void allOff() throws IOException { - socketClient.sendAction(uuid, CMD_OFF); - } - - /** - * Select next lighting scene. - * - * @throws IOException - * when something went wrong with communication - */ - public void nextScene() throws IOException { - socketClient.sendAction(uuid, CMD_NEXT_SCENE); - } - - /** - * Select previous lighting scene. - * - * @throws IOException - * when something went wrong with communication - */ - public void previousScene() throws IOException { - socketClient.sendAction(uuid, CMD_PREVIOUS_SCENE); - } - - /** - * Set provided scene. - * - * @param scene - * scene number to set (0-9) - * @throws IOException - * when something went wrong with communication - */ - public void setScene(int scene) throws IOException { - if (scene == SCENE_ALL_ON) { - allOn(); - } else if (scene >= 0 && scene < NUM_OF_SCENES) { - socketClient.sendAction(uuid, Long.toString(scene)); - } - } - - /** - * Get current active scene - * - * @return - * number of the active scene (0-9, 0-all off, 9-all on) or null if error - */ - public Integer getCurrentScene() { - Double value = getStateValue(STATE_ACTIVE_SCENE); - if (value != null) { - return value.intValue(); - } - return null; - } - - /** - * Get scene designated as 'movement' - * - * @return - * number of the movement scene (0-9, 0-all off, 9-all on) or null if undefined - */ - public Integer getMovementScene() { - return movementScene; - } - - /** - * Return an array with names of all scenes, where index is scene number - * - * @return - * an array with scene names indexed by scene number - */ - public Map getSceneNames() { - return sceneNames; - } - - /** - * Get scene names from new state value received from the Miniserver - */ - @Override - public void onStateChange(LxControlState state) { - String scenesText = state.getTextValue(); - if (scenesText != null) { - sceneNames.clear(); - String[] scenes = scenesText.split(","); - for (String line : scenes) { - line = line.replaceAll("\"", ""); - String[] params = line.split("="); - if (params.length == 2) { - sceneNames.put(params[0], params[1]); - } - } - } - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlLightControllerV2.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlLightControllerV2.java deleted file mode 100644 index 29c62c19da8ee..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlLightControllerV2.java +++ /dev/null @@ -1,298 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -import com.google.gson.JsonSyntaxException; - -/** - * A Light Controller V2 type of control on Loxone Miniserver. - *

- * This control has been introduced in Loxone Config 9 in 2017 and it makes the {@link LxControlLightController} - * obsolete. Both controls will exist for some time together. - *

- * Light controller V2 can have N outputs named AQ1...AQN that can function as Switch, Dimmer, RGB, Lumitech or Smart - * Actuator functional blocks. Individual controls will be created for these outputs so they can be operated directly - * and independently from the controller. - *

- * Controller can also have M moods configured. Each mood defines own subset of outputs and their settings, which will - * be engaged when the mood is active. A dedicated switch control object will be created for each mood. - * This effectively will allow for mixing various moods by individually enabling/disabling them. - *

- * It seems there is no imposed limitation for the number of outputs and moods. - * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxControlLightControllerV2 extends LxControlAbstractController implements LxControlStateListener { - - static class Factory extends LxControlInstance { - @Override - LxControl create(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - return new LxControlLightControllerV2(client, uuid, json, room, category); - } - - @Override - String getType() { - return TYPE_NAME; - } - } - - /** - * A name by which Miniserver refers to light controller v2 controls - */ - private static final String TYPE_NAME = "lightcontrollerv2"; - - /** - * State with list of active moods - */ - public static final String STATE_ACTIVE_MOODS_LIST = "activemoods"; - /** - * State with list of available moods - */ - public static final String STATE_MOODS_LIST = "moodlist"; - - /** - * Command string used to set a given mood - */ - private static final String CMD_CHANGE_TO_MOOD = "changeTo"; - /** - * Command string used to change to the next mood - */ - private static final String CMD_NEXT_MOOD = "plus"; - /** - * Command string used to change to the previous mood - */ - private static final String CMD_PREVIOUS_MOOD = "minus"; - /** - * Command string used to add mood to the active moods (mix it in) - */ - private static final String CMD_ADD_MOOD = "addMood"; - /** - * Command string used to remove mood from the active moods (mix it out) - */ - private static final String CMD_REMOVE_MOOD = "removeMood"; - - // Following commands are not supported: - // moveFavoriteMood, moveAdditionalMood, moveMood, addToFavoriteMood, removeFromFavoriteMood, learn, delete - - private Map moodList = new HashMap<>(); - private List activeMoods = new ArrayList<>(); - private Integer minMoodId; - private Integer maxMoodId; - - /** - * Create lighting controller v2 object. - * - * @param client - * communication client used to send commands to the Miniserver - * @param uuid - * controller's UUID - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * room to which controller belongs - * @param category - * category to which controller belongs - */ - LxControlLightControllerV2(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, - LxCategory category) { - super(client, uuid, json, room, category); - // sub-controls of this control have been created when update() method was called by the super class constructor - addStateListener(STATE_MOODS_LIST, this); - addStateListener(STATE_ACTIVE_MOODS_LIST, this); - } - - /** - * Set a mood, deactivate other moods - * - * @param moodId - * ID of the mood to set - * @throws IOException - * when something went wrong with communication - */ - public void setMood(Integer moodId) throws IOException { - if (isMoodOk(moodId)) { - socketClient.sendAction(uuid, CMD_CHANGE_TO_MOOD + "/" + moodId); - } - } - - /** - * Select next mood. - * - * @throws IOException - * when something went wrong with communication - */ - public void nextMood() throws IOException { - socketClient.sendAction(uuid, CMD_NEXT_MOOD); - } - - /** - * Select previous mood. - * - * @throws IOException - * when something went wrong with communication - */ - public void previousMood() throws IOException { - socketClient.sendAction(uuid, CMD_PREVIOUS_MOOD); - } - - /** - * Mix a mood into currently active moods. - * - * @param moodId - * ID of the mood to add - * @throws IOException - * when something went wrong with communication - */ - public void addMood(Integer moodId) throws IOException { - if (isMoodOk(moodId)) { - socketClient.sendAction(uuid, CMD_ADD_MOOD + "/" + moodId); - } - } - - /** - * Mix a mood out of currently active moods. - * - * @param moodId - * ID of the mood to remove - * @throws IOException - * when something went wrong with communication - */ - public void removeMood(Integer moodId) throws IOException { - if (isMoodOk(moodId)) { - socketClient.sendAction(uuid, CMD_REMOVE_MOOD + "/" + moodId); - } - } - - /** - * Get IDs of currently active moods. - * - * @return - * list of IDs of active moods or null if active moods list is not available - */ - public List getActiveMoods() { - return activeMoods; - } - - /** - * Get all configured moods. - * - * @return - * Map with mood ID as key and mood control object as value or null if moods are not configured - */ - public Map getMoods() { - return moodList; - } - - /** - * Get minimum value a mood ID can have for the current list of moods. - * - * @return - * minimum value of a mood ID for the current list of moods or null if not defined - */ - public Integer getMinMoodId() { - return minMoodId; - } - - /** - * Get maximum value a mood ID can have for the current list of moods. - * - * @return - * maximum value of a mood ID for the current list of moods or null if not defined - */ - public Integer getMaxMoodId() { - return maxMoodId; - } - - /** - * Get configured and active moods from a new state value received from the Miniserver - * - * @param state - * state update from the Miniserver - */ - @Override - public void onStateChange(LxControlState state) { - String stateName = state.getName(); - String text = state.getTextValue(); - logger.debug("Received state {} update to {}", stateName, text); - try { - if (STATE_MOODS_LIST.equals(stateName)) { - LxJsonMood[] array = socketClient.getGson().fromJson(text, LxJsonMood[].class); - moodList.clear(); - minMoodId = null; - maxMoodId = null; - LxJsonControl json = new LxJsonApp3().new LxJsonControl(); - for (LxJsonMood mood : array) { - if (mood.id != null && mood.name != null) { - logger.debug("Adding mood {} (name={}, isUsed={}, t5={}, static={}", mood.id, mood.name, - mood.isUsed, mood.isT5Controlled, mood.isStatic); - json.name = mood.name; - // mood-UUID = -M - LxUuid moodUuid = new LxUuid(getUuid().toString() + "-M" + mood.id); - LxControlMood control = new LxControlMood(socketClient, moodUuid, json, getRoom(), - getCategory(), mood.id, mood.isStatic, this); - moodList.put(moodUuid, control); - if (minMoodId == null || minMoodId > mood.id) { - minMoodId = mood.id; - } - if (maxMoodId == null || maxMoodId < mood.id) { - maxMoodId = mood.id; - } - } - } - } else if (STATE_ACTIVE_MOODS_LIST.equals(stateName)) { - // this state can be received before list of moods, but it contains a valid list of IDs - Integer[] array = socketClient.getGson().fromJson(text, Integer[].class); - activeMoods = Arrays.asList(array); - } - } catch (JsonSyntaxException e) { - logger.debug("Error parsing state {}: {}", stateName, e.getMessage()); - } - } - - /** - * Check if mood ID is within allowed range - * - * @param moodId - * mood ID to check - * @return - * true if mood ID is within allowed range or range is not configured - */ - private boolean isMoodOk(Integer moodId) { - if ((minMoodId != null && minMoodId > moodId) || (maxMoodId != null && maxMoodId < moodId)) { - return false; - } - return true; - } - - /** - * Check if mood is currently active. - * - * @param moodId - * mood ID to check - * @return - * true if mood is currently active - */ - boolean isMoodActive(Integer moodId) { - return activeMoods.contains(moodId); - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlMood.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlMood.java deleted file mode 100644 index 6a5a3be20a168..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlMood.java +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.io.IOException; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -/** - * This class represents a mood belonging to a {@link LxControlMood} object. - * A mood is effectively a switch. When the switch is set to ON, mood is active and mixed into a set of active - * moods. - * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxControlMood extends LxControlSwitch { - private Integer moodId; - private Boolean isStatic; - private LxControlLightControllerV2 controller; - - /** - * Create a control representing a single mood of a light controller V2. - * - * @param client - * communication client used to send commands to the Miniserver - * @param uuid - * controller's UUID - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * room to which mood belongs - * @param category - * category to which mood belongs - * @param moodId - * mood ID withing the controller (received from the Miniserver) - * @param isStatic - * true if this mood is static and can't be deleted or modified in any way - * @param controller - * light controller that this mood belongs to - */ - LxControlMood(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category, - Integer moodId, Boolean isStatic, LxControlLightControllerV2 controller) { - super(client, uuid, json, room, category); - this.moodId = moodId; - this.isStatic = isStatic; - this.controller = controller; - } - - /** - * Get an ID of this mood. ID indentifies the mood within a light controller. - * It is equal to the mood ID received from the Miniserver. - * - * @return - * mood ID - */ - public Integer getId() { - return moodId; - } - - /** - * Returns if mood is statically-defined 'all off' mood. - * Setting 'all off' mood on the Miniserver switches all outputs off, but it can't be mixed with other moods. - * Attempt to mix it results in no change in the active moods list. - * - * @return - * true if this is static all outputs off mood - */ - public boolean isAllOffMood() { - // currently the API does not give a hint how to figure out the 'all off' mood - // empirically this is the only mood that is not editable by the user and has a static flag set on - // we will assume that the only static mood is 'all off' mood - return isStatic == null ? false : isStatic; - } - - /** - * Get controller's UUID that the mood belongs to. - * - * @return UUID of the lighting controller - */ - public LxUuid getControllerUuid() { - return LxControlMood.this.getUuid(); - } - - /** - * Mix the mood into active moods. - * - * @throws IOException - * when something went wrong with communication - */ - @Override - public void on() throws IOException { - controller.addMood(moodId); - } - - /** - * Mix the mood out of active moods. - * - * @throws IOException - * when something went wrong with communication - */ - @Override - public void off() throws IOException { - controller.removeMood(moodId); - } - - /** - * Return whether the mood is active of not. - * - * @return - * 1 if mood is active and 0 otherwise - */ - @Override - public Double getState() { - if (controller.isMoodActive(moodId)) { - return 1.0; - } - return 0.0; - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlPushbutton.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlPushbutton.java deleted file mode 100644 index 1316f58fbd8a0..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlPushbutton.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.io.IOException; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -/** - * A pushbutton type of control on Loxone Miniserver. - *

- * According to Loxone API documentation, a pushbutton control covers virtual input of type pushbutton - * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxControlPushbutton extends LxControlSwitch { - - static class Factory extends LxControlInstance { - @Override - LxControl create(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - return new LxControlPushbutton(client, uuid, json, room, category); - } - - @Override - String getType() { - return TYPE_NAME; - } - } - - /** - * A name by which Miniserver refers to pushbutton controls - */ - private static final String TYPE_NAME = "pushbutton"; - /** - * Command string used to set control's state to ON and OFF (tap) - */ - private static final String CMD_PULSE = "Pulse"; - - /** - * Create pushbutton control object. - * - * @param client - * communication client used to send commands to the Miniserver - * @param uuid - * switch's UUID - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * room to which switch belongs - * @param category - * category to which switch belongs - */ - LxControlPushbutton(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - super(client, uuid, json, room, category); - } - - /** - * Set pushbutton to ON and to OFF (tap it). - *

- * Sends a command to operate the pushbutton. - * - * @throws IOException - * when something went wrong with communication - */ - public void pulse() throws IOException { - socketClient.sendAction(uuid, CMD_PULSE); - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlRadio.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlRadio.java deleted file mode 100644 index 49e7a6d17dbfc..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlRadio.java +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.io.IOException; -import java.util.Map; -import java.util.TreeMap; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -/** - * A radio-button type of control on Loxone Miniserver. - * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxControlRadio extends LxControl { - - static class Factory extends LxControlInstance { - @Override - LxControl create(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - return new LxControlRadio(client, uuid, json, room, category); - } - - @Override - String getType() { - return TYPE_NAME; - } - } - - /** - * Number of outputs a radio controller may have - */ - public static final int MAX_RADIO_OUTPUTS = 16; - - /** - * A name by which Miniserver refers to radio-button controls - */ - private static final String TYPE_NAME = "radio"; - - /** - * Radio-button has one state that is a number representing current active output - */ - private static final String STATE_ACTIVE_OUTPUT = "activeoutput"; - - /** - * Command string used to set radio button to all outputs off - */ - private static final String CMD_RESET = "reset"; - private Map outputs; - - /** - * Create radio-button control object. - * - * @param client - * communication client used to send commands to the Miniserver - * @param uuid - * radio-button's UUID - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * room to which radio-button belongs - * @param category - * category to which control belongs - */ - LxControlRadio(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - super(client, uuid, json, room, category); - } - - /** - * Update Miniserver's control in runtime. - * - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * New room that this control belongs to - * @param category - * New category that this control belongs to - */ - @Override - void update(LxJsonControl json, LxContainer room, LxCategory category) { - super.update(json, room, category); - if (json.details.outputs != null) { - outputs = new TreeMap<>(json.details.outputs); - } else { - outputs = new TreeMap<>(); - } - if (json.details != null && json.details.allOff != null) { - outputs.put("0", json.details.allOff); - } - } - - /** - * Set radio-button control's active output - *

- * Sends a command to operate the radio-button control. - * - * @param output - * output number to activate - * @throws IOException - * when something went wrong with communication - */ - public void setOutput(int output) throws IOException { - if (output == 0) { - socketClient.sendAction(uuid, CMD_RESET); - } else if (output >= 1 && output <= MAX_RADIO_OUTPUTS) { - socketClient.sendAction(uuid, Long.toString(output)); - } - } - - /** - * Get current active output of a radio-button control - * - * @return - * active output number 1-8 (or 1-16), or 0 if all outputs are off, or null if error occured - */ - public Integer getActiveOutput() { - Double value = getStateValue(STATE_ACTIVE_OUTPUT); - if (value != null) { - return value.intValue(); - } - return null; - } - - /** - * Get mapping between output numbers and output names - * - * @return - * map where key is output number 1-8 (or 1-16) or 0 for no active outputs and value is corresponding name - * (never returns null) - */ - public Map getOutputs() { - return outputs; - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlState.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlState.java deleted file mode 100644 index 842ffe938c300..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlState.java +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.util.ArrayList; -import java.util.List; - -/** - * A state of a Loxone control ({@link LxControl}) - *

- * Each control object may have a number of states defined, that describe the overall condition of the control. - * List of states is read from LoxApp3.json configuration file. - *

- * Each state is identified by its own UUID and a name of the state. Names are proprietary to a particular type of the - * control and as such are defined in {@link LxControl} child classes implementation (e.g. {@link LxControlSwitch} - * Objects of this class are used to bind state updates received from the Miniserver to a control object. - * - * @author Pawel Pieczul - initial contribution - * - */ -class LxControlState { - private LxUuid uuid; - private String name; - private Double value; - private String textValue; - private LxControl control; - private List listeners = new ArrayList<>(); - - /** - * Create a control state object. - * - * @param uuid - * UUID of the state - * @param name - * name of the state - * @param control - * control to which this state belongs - */ - LxControlState(LxUuid uuid, String name, LxControl control) { - this.uuid = uuid; - this.name = name; - this.control = control; - uuid.setUpdate(true); - } - - /** - * Sets current value of the control's state - * - * @param value - * current state's value to set - * @param textValue - * current state's text value to set - */ - void setValue(Double value, String textValue) { - boolean changed = false; - - uuid.setUpdate(true); - - if (value != null && !value.equals(this.value)) { - this.value = value; - changed = true; - } - - if (textValue != null && !textValue.equals(this.textValue)) { - this.textValue = textValue; - changed = true; - } - - if (changed) { - for (LxControlStateListener listener : listeners) { - listener.onStateChange(this); - } - } - } - - /** - * Sets current text value of the control's state - * - * @param value - * new text message value - */ - void setValue(String value) { - uuid.setUpdate(true); - } - - /** - * Gets current value of the control's state - * - * @return - * current state's value - */ - Double getValue() { - return value; - } - - /** - * Gets current value of the control's state - * - * @return - * current state's value - */ - String getTextValue() { - return textValue; - } - - /** - * Gets control to which state belongs - * - * @return - * state's control object - */ - LxControl getControl() { - return control; - } - - /** - * Gets state's name. - *

- * State's name is proprietary per control type. - * - * @return - * state's name - */ - String getName() { - return name; - } - - /** - * Sets state's name - * - * @param name - * state's name - */ - void setName(String name) { - this.name = name; - uuid.setUpdate(true); - } - - /** - * Gets UUID of the state - * - * @return - * state's UUID - */ - LxUuid getUuid() { - return uuid; - } - - /** - * Adds a listener to state changes - * - * @param listener - * an object implementing state change listener interface - */ - void addListener(LxControlStateListener listener) { - listeners.add(listener); - } - - /** - * Removes a listener of state changes - * - * @param listener - * listener object to remove - */ - void removeListener(LxControlStateListener listener) { - listeners.remove(listener); - } - -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlStateListener.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlStateListener.java deleted file mode 100644 index 76286846fa00d..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlStateListener.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -/** - * This is an interface to listen to control's state changes - * - * @author Pawel Pieczul - initial contribution - * - */ -interface LxControlStateListener { - /** - * This method will be called by registered listener, when control's state is changed - * - * @param state - * changed state - */ - void onStateChange(LxControlState state); -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlSwitch.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlSwitch.java deleted file mode 100644 index f1994cc30b13e..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlSwitch.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.io.IOException; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -/** - * A switch type of control on Loxone Miniserver. - *

- * According to Loxone API documentation, a switch control is: - *

    - *
  • a virtual input of switch type - *
  • a push button function block - *
- * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxControlSwitch extends LxControl { - - static class Factory extends LxControlInstance { - @Override - LxControl create(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - return new LxControlSwitch(client, uuid, json, room, category); - } - - @Override - String getType() { - return TYPE_NAME; - } - } - - /** - * A name by which Miniserver refers to switch controls - */ - static final String TYPE_NAME = "switch"; - - /** - * Switch has one state that can be on/off - */ - private static final String STATE_ACTIVE = "active"; - - /** - * Command string used to set control's state to ON - */ - private static final String CMD_ON = "On"; - /** - * Command string used to set control's state to OFF - */ - private static final String CMD_OFF = "Off"; - - /** - * Create switch control object. - * - * @param client - * communication client used to send commands to the Miniserver - * @param uuid - * switch's UUID - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * room to which switch belongs - * @param category - * category to which switch belongs - */ - LxControlSwitch(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - super(client, uuid, json, room, category); - } - - /** - * Set switch to ON. - *

- * Sends a command to operate the switch. - * - * @throws IOException - * when something went wrong with communication - */ - public void on() throws IOException { - socketClient.sendAction(uuid, CMD_ON); - } - - /** - * Set switch to OFF. - *

- * Sends a command to operate the switch. - * - * @throws IOException - * when something went wrong with communication - */ - public void off() throws IOException { - socketClient.sendAction(uuid, CMD_OFF); - } - - /** - * Get current value of the switch'es state. - * - * @return - * 0 - switch off, 1 - switch on - */ - public Double getState() { - return getStateValue(STATE_ACTIVE); - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlTextState.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlTextState.java deleted file mode 100644 index d833aa6af790f..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlTextState.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -/** - * A Text State type of control on Loxone Miniserver. - *

- * According to Loxone API documentation, a text state represents a State functional block on the Miniserver - * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxControlTextState extends LxControl { - - static class Factory extends LxControlInstance { - @Override - LxControl create(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - return new LxControlTextState(client, uuid, json, room, category); - } - - @Override - String getType() { - return TYPE_NAME; - } - } - - /** - * A name by which Miniserver refers to text state controls - */ - private static final String TYPE_NAME = "textstate"; - - /** - * A state which will receive an update of possible Text State values) - */ - private static final String STATE_TEXT_AND_ICON = "textandicon"; - - /** - * Create text state object. - * - * @param client - * communication client used to send commands to the Miniserver - * @param uuid - * controller's UUID - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * room to which controller belongs - * @param category - * category to which controller belongs - */ - LxControlTextState(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - super(client, uuid, json, room, category); - } - - /** - * Return current text value of the state - * - * @return - * string with current value - */ - public String getText() { - return getStateTextValue(STATE_TEXT_AND_ICON); - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlTimedSwitch.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlTimedSwitch.java deleted file mode 100644 index 569a5e28f2e4c..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxControlTimedSwitch.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import org.openhab.binding.loxone.internal.core.LxJsonApp3.LxJsonControl; - -/** - * A timed switch type of control on Loxone Miniserver. - *

- * According to Loxone API documentation, a switch control is: - *

    - *
  • a virtual input of switch type - *
  • a push button function block - *
- * - * @author Stephan Brunner - * - */ -public class LxControlTimedSwitch extends LxControlPushbutton { - - static class Factory extends LxControlInstance { - @Override - LxControl create(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - return new LxControlTimedSwitch(client, uuid, json, room, category); - } - - @Override - String getType() { - return TYPE_NAME; - } - } - - /** - * A name by which Miniserver refers to timed switch controls - */ - private static final String TYPE_NAME = "timedswitch"; - - /** - * deactivationDelay - countdown until the output is deactivated. - * 0 = the output is turned off - * -1 = the output is permanently on - * otherwise it will count down from deactivationDelayTotal - */ - private static final String STATE_DEACTIVATION_DELAY = "deactivationdelay"; - - /** - * Create timed switch control object. - * - * @param client - * communication client used to send commands to the Miniserver - * @param uuid - * switch's UUID - * @param json - * JSON describing the control as received from the Miniserver - * @param room - * room to which switch belongs - * @param category - * category to which switch belongs - */ - LxControlTimedSwitch(LxWsClient client, LxUuid uuid, LxJsonControl json, LxContainer room, LxCategory category) { - super(client, uuid, json, room, category); - } - - /** - * Get current value of the timed switch'es state. - * - * @return - * 0 - switch off, 1 - switch on - */ - @Override - public Double getState() { - /** - * 0 = the output is turned off - * -1 = the output is permanently on - * otherwise it will count down from deactivationDelayTotal - **/ - Double value = getStateValue(STATE_DEACTIVATION_DELAY); - if (value != null) { - if (value == -1 || value > 0) { // mapping - return 1d; - } else if (value == 0) { - return 0d; - } - } - return null; - } - - /** - * Get the time remaining to the switch off, in seconds - * - * @return deactivation delay in seconds - * Loxone also returns floating point values for the delay e.g. 9.99 seconds - */ - public Double getDeactivationDelay() { - return getStateValue(STATE_DEACTIVATION_DELAY); - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxJsonApp3.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxJsonApp3.java deleted file mode 100644 index 8beb376d08c8e..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxJsonApp3.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.util.Map; - -import com.google.gson.JsonElement; - -/** - * A structure of JSON file http://miniserver/data/LoxAPP3.json used for parsing it with Gson library. - * - * @author Pawel Pieczul - initial contribution - * - */ -class LxJsonApp3 { - - LxJsonInfo msInfo; - Map controls; - Map rooms; - Map cats; - - class LxJsonInfo { - String serialNr; - String location; - String roomTitle; - String catTitle; - String msName; - String projectName; - String remoteUrl; - String swVersion; - String macAddress; - } - - class LxJsonRoom { - String uuid; - String name; - } - - class LxJsonCat { - String uuid; - String name; - String type; - } - - class LxJsonControl { - - class LxJsonDetails { - class LxJsonText { - String off; - String on; - } - - LxJsonText text; - String format; - int movementScene; - String allOff; - Map outputs; - } - - String uuidAction; - String name; - String type; - String room; - String cat; - LxJsonDetails details; - Map states; - Map subControls; - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxJsonMood.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxJsonMood.java deleted file mode 100644 index b4df57234e417..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxJsonMood.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import com.google.gson.annotations.SerializedName; - -/** - * A JSON structure of a mood of {@link LxControlLightControllerV2}. This structure is an item of a JSON array received - * in moodList state update of this controller in the runtime. - *

- * This structure is used for parsing with Gson library. - * - * @author Pawel Pieczul - initial contribution - * - */ -class LxJsonMood { - /** - * The userfriendly name for this mood - */ - String name; - - /** - * An ID that uniquely identifies this mood (e.g. inside activeMoods) - */ - Integer id; - - /** - * Bitmask that tells if the mood is used for a specific purpose in the logic. - * If it’s not used, it can be removed without affecting the logic on the Miniserver. - * 0: not used - * 1: this mood is activated by a movement event - * 2: a T5 or other inputs activate/deactivate this mood - */ - @SerializedName("used") - Integer isUsed; - - /** - * Whether or not this mood can be controlled with a t5 input - */ - @SerializedName("t5") - Boolean isT5Controlled; - - /** - * If a mood is marked as static it cannot be deleted or modified in any way. - * But it can be moved within and between favorite and additional lists. - */ - @SerializedName("static") - Boolean isStatic; -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxJsonResponse.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxJsonResponse.java deleted file mode 100644 index 96c4c8b08770a..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxJsonResponse.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import com.google.gson.JsonElement; -import com.google.gson.annotations.SerializedName; - -/** - * A JSON response to a call to send a command to Miniserver's control at - * http://miniserver/jdev/sps/io/{uuid}/{command}. - *

- * This structure is used for parsing with Gson library. - * - * @author Pawel Pieczul - initial contribution - * - */ -class LxJsonResponse { - - class LxJsonKeySalt { - String key; - String salt; - } - - class LxJsonCfgApi { - String snr; - String version; - } - - class LxJsonToken { - String token; - String key; - Integer validUntil; - Integer tokenRights; - Boolean unsecurePass; - } - - static class LxJsonSubResponse { - String control; - JsonElement value; - @SerializedName(value = "Code", alternate = { "code" }) - int code; - } - - @SerializedName("LL") - LxJsonSubResponse subResponse; -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxOfflineReason.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxOfflineReason.java deleted file mode 100644 index f89032cb46280..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxOfflineReason.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -/** - * Reasons why Miniserver may be not reachable - * - * @author Pawel Pieczul - initial contribution - * - */ -public enum LxOfflineReason { - /** - * No reason at all - should be reachable - */ - NONE, - /** - * User name or password incorrect or user not authorized - */ - UNAUTHORIZED, - /** - * Too many failed login attempts and server's temporary ban of the user - */ - TOO_MANY_FAILED_LOGIN_ATTEMPTS, - /** - * Communication error with the Miniserv - */ - COMMUNICATION_ERROR, - /** - * Timeout of user authentication procedure - */ - AUTHENTICATION_TIMEOUT, - /** - * No activity from Miniserver's client - */ - IDLE_TIMEOUT, - /** - * Internal error, sign of something wrong with the program - */ - INTERNAL_ERROR, - /** - * Connection attempt failed (before authentication) - */ - CONNECT_FAILED, - /** - * Repeat connection immediately (for example to retry with different authorization scheme) - */ - REPEAT_CONNECTION; - - /** - * Converts Miniserver status code to offline reason - * - * @param code - * status code received in message response from the Miniserver - * @return - * converted offline reason - */ - static LxOfflineReason getReason(int code) { - switch (code) { - case 420: - return LxOfflineReason.AUTHENTICATION_TIMEOUT; - case 401: - return LxOfflineReason.UNAUTHORIZED; - case 4003: - return LxOfflineReason.TOO_MANY_FAILED_LOGIN_ATTEMPTS; - case 1001: - return LxOfflineReason.IDLE_TIMEOUT; - case 200: - return LxOfflineReason.NONE; - default: - return COMMUNICATION_ERROR; - } - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxServer.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxServer.java deleted file mode 100644 index 89fa680ddafa1..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxServer.java +++ /dev/null @@ -1,783 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import org.openhab.binding.loxone.internal.core.LxServerEvent.EventType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Loxone Miniserver representaton. - *

- * A Miniserver is identified by host address, user name and password used for logging into it. - * They are provided to the constructor of this class. Upon creation of the object, you need to call - * {@link #start()} method to initiate communication with the Miniserver, obtain its runtime configuration and - * process live updates of controls' state changes. - *

- * Runtime configuration consists of following items: - *

    - *
  • server parameters: serial number, location, project name, server name - *
  • 'room' and 'category' proprietary display names - *
  • a list of rooms - *
  • a list of control categories - *
  • a list of controls, each may be assigned to a room and a category - *
  • a list of states for each of the controls - *
- *

- * Once server is populated with runtime configuration, its controls may be used to perform operations. - *

- * If server is not needed anymore, a {@link #stop()} method should be called to close open connections and stop - * processing thread. - * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxServer { - - class Configuration { - Object get(String name) { - // this interface is going to be refactored, there can never be more than one listener - for (LxServerListener listener : listeners) { - return listener.getSetting(name); - } - return null; - } - - void setSettings(Map properties) { - listeners.forEach(listener -> listener.setSettings(properties)); - } - } - - // Configuration - private final InetAddress host; - private final int port; - private final String user; - private final String password; - private final Configuration configuration = new Configuration(); - - private String miniserverName = ""; - private String projectName = ""; - private String location = ""; - private String serial = ""; - private String cloudAddress = ""; - private String swVersion = ""; - private String macAddress = ""; - - private int firstConDelay = 1; - private int connectErrDelay = 10; - private int userErrorDelay = 60; - private int comErrorDelay = 30; - - // Data structures - private final Map controls = new HashMap<>(); - private final Map rooms = new HashMap<>(); - private final Map categories = new HashMap<>(); - // Map of state UUID to a map of control UUID and state objects - // State with a unique UUID can be configured in many controls and each control can even have a different name of - // the state. It must be ensured that updates received for this state UUID are passed to all controls that have this - // state UUID configured. - private final Map> states = new HashMap<>(); - private final List listeners = new ArrayList<>(); - - // Services - private LxWsClient socketClient; - private Thread monitorThread; - private final Lock threadLock = new ReentrantLock(); - private final BlockingQueue queue = new LinkedBlockingQueue<>(); - private final Logger logger = LoggerFactory.getLogger(LxServer.class); - private final int debugId; - private static final AtomicInteger STATIC_DEBUG_ID = new AtomicInteger(1); - - /** - * Creates a new instance of Loxone Miniserver with provided host address and credentials. - * - * @param securityType - * type of authentication/encryption method to use - * - * @param host - * host address of the Miniserver - * @param port - * web service port of the Miniserver - * @param user - * user name used for logging in - * @param password - * password used for logging in - */ - public LxServer(LxWsSecurityType securityType, InetAddress host, int port, String user, String password) { - this.host = host; - this.port = port; - this.user = user; - this.password = password; - - debugId = STATIC_DEBUG_ID.getAndIncrement(); - socketClient = new LxWsClient(debugId, queue, configuration, securityType, host, port, user, password); - } - - /** - * Initiate communication with the Miniserver. - * Starts thread that handles communication. - */ - public void start() { - logger.debug("[{}] Server start", debugId); - threadLock.lock(); - try { - if (monitorThread == null) { - monitorThread = new LxServerThread(this); - monitorThread.start(); - } - } finally { - threadLock.unlock(); - } - } - - /** - * Stop server thread, close communication with Miniserver. - */ - public void stop() { - threadLock.lock(); - try { - if (monitorThread != null) { - logger.debug("[{}] Server stop", debugId); - LxServerEvent event = new LxServerEvent(EventType.CLIENT_CLOSING, LxOfflineReason.NONE, null); - try { - queue.put(event); - } catch (InterruptedException e) { - monitorThread.interrupt(); - } - } else { - logger.debug("[{}] Server stop - no thread", debugId); - } - } finally { - threadLock.unlock(); - } - } - - /** - * Update server's configuration. - *

- * Only timeout parameters can be updated in runtime without breaking connection to the Miniserver. - * If other parameters must be changed, server should be stopped and a new instance created. - * - * @param firstConDelay - * Time in seconds between binding initialization and first connection attempt - * @param keepAlivePeriod - * Time in seconds between sending two consecutive keep-alive messages - * @param connectErrDelay - * Time in seconds between failed websocket connect attempts - * @param connectTimeout - * Time to wait for websocket connect response from the Miniserver - * @param userErrorDelay - * Time in seconds between user login error as a result of wrong name/password or no authority and next - * connection attempt - * @param comErrorDelay - * Time in seconds between connection close (as a result of some communication error) and next connection - * attempt - * @param maxBinMsgSize - * maximum binary message size of websocket client (in kB) - * @param maxTextMsgSize - * maximum text message size of websocket client (in kB) - */ - public void update(int firstConDelay, int keepAlivePeriod, int connectErrDelay, int connectTimeout, - int userErrorDelay, int comErrorDelay, int maxBinMsgSize, int maxTextMsgSize) { - logger.debug("[{}] Server update configuration", debugId); - - if (firstConDelay >= 0 && this.firstConDelay != firstConDelay) { - logger.debug("[{}] Changing firstConDelay to {}", debugId, firstConDelay); - this.firstConDelay = firstConDelay; - } - if (connectErrDelay >= 0 && this.connectErrDelay != connectErrDelay) { - logger.debug("[{}] Changing connectErrDelay to {}", debugId, connectErrDelay); - this.connectErrDelay = connectErrDelay; - } - if (userErrorDelay >= 0 && this.userErrorDelay != userErrorDelay) { - logger.debug("[{}] Changing userErrorDelay to {}", debugId, userErrorDelay); - this.userErrorDelay = userErrorDelay; - } - if (comErrorDelay >= 0 && this.comErrorDelay != comErrorDelay) { - logger.debug("[{}] Changing comErrorDelay to {}", debugId, comErrorDelay); - this.comErrorDelay = comErrorDelay; - } - if (socketClient != null) { - socketClient.update(keepAlivePeriod, connectTimeout, maxBinMsgSize, maxTextMsgSize); - } - } - - /** - * Adds a listener to server's events - * - * @param listener - * an object implementing server's listener interface - */ - public void addListener(LxServerListener listener) { - listeners.add(listener); - } - - /** - * Removes a listener of server's events - * - * @param listener - * listener object to remove - */ - public void removeListener(LxServerListener listener) { - listeners.remove(listener); - } - - /** - * Checks if current Miniserver configuration differs from provided parameters. - * - * @param host - * A new host address to check against - * @param user - * A new user name to check against - * @param port - * A new web service port to check against - * @param password - * A new password to check against - * @return - * true if current Miniserver configuration is different - */ - public boolean isChanged(InetAddress host, int port, String user, String password) { - return (!(this.port == port && this.host.toString().equals(host.toString()) && this.user.equals(user) - && this.password.equals(password))); - } - - /** - * Searches for a control with given UUID - * - * @param id - * UUID of the control to locate - * @return - * Found control or null if not found - */ - public LxControl findControl(LxUuid id) { - if (controls == null || id == null) { - return null; - } - if (controls.containsKey(id)) { - return controls.get(id); - } - return null; - } - - /** - * Searches for a control with given name (descriptive) - * - * @param name - * A name of the control to locate - * @return - * Found control or null if not found - */ - public LxControl findControl(String name) { - for (LxControl l : controls.values()) { - if (l.getName().equals(name)) { - return l; - } - } - return null; - } - - /** - * Gets a set of all controls for this Miniserver - * - * @return Map of controls with UUID as a key - */ - public Map getControls() { - return controls; - } - - /** - * Gets Miniserver name - * - * @return Miniserver name - */ - public String getMiniserverName() { - return miniserverName; - } - - /** - * Gets project name as configured on the Miniserver - * - * @return project name - */ - public String getProjectName() { - return projectName; - } - - /** - * Gets Miniserver cloud address - * - * @return cloud URL - */ - public String getCloudAddress() { - return cloudAddress; - } - - /** - * Gets device location as configured on the Miniserver - * - * @return Description of the device location - */ - public String getLocation() { - return location; - } - - /** - * Gets device serial number as configured on the Miniserver - * - * @return Device serial number - */ - public String getSerial() { - return serial; - } - - /** - * Gets device software version - * - * @return - * software version (e.g. '9.1.0.3') - */ - public String getSwVersion() { - return swVersion; - } - - /** - * Gets device MAC address - * - * @return - * MAC address - */ - public String getMacAddress() { - return macAddress; - } - - /** - * Thread that performs and supervises communication with the Miniserver. - *

- * It will try to maintain the connection as long as possible, handling errors and interruptions. There are two - * reasons when this thread will terminate and stop connecting to the Miniserver: - * when it receives close command from supervisor ({@link LxServer} or when Miniserver locks out user due to too - * many unsuccessful login attempts. - * - * @author Pawel Pieczul - initial contribution - * - */ - private class LxServerThread extends Thread { - private final LxServer server; - private boolean running = true; - private int waitTime = firstConDelay; - - LxServerThread(LxServer server) { - this.server = server; - } - - @Override - public void run() { - logger.debug("[{}] Thread starting", debugId); - try { - boolean connected = false; - while (running) { - while (!connected) { - LxServerEvent wsMsg; - do { - wsMsg = queue.poll(waitTime, TimeUnit.SECONDS); - if (wsMsg != null) { - processMessage(wsMsg); - } - } while (wsMsg != null); - logger.debug("[{}] Server connecting to websocket", debugId); - connected = socketClient.connect(); - if (!connected) { - waitTime = connectErrDelay; - } - } - while (connected) { - LxServerEvent wsMsg = queue.take(); - connected = processMessage(wsMsg); - } - } - } catch (InterruptedException e) { - logger.debug("[{}] Server thread interrupted, terminating", debugId); - } - logger.debug("[{}] Thread ending", debugId); - threadLock.lock(); - try { - socketClient.disconnect(); - monitorThread = null; - queue.clear(); - } finally { - threadLock.unlock(); - } - } - - private boolean processMessage(LxServerEvent wsMsg) { - EventType event = wsMsg.getEvent(); - logger.trace("[{}] Server received event: {}", debugId, event); - switch (event) { - case RECEIVED_CONFIG: - LxJsonApp3 config = (LxJsonApp3) wsMsg.getObject(); - if (config != null) { - updateConfig(config); - for (LxServerListener listener : listeners) { - listener.onNewConfig(server); - } - } else { - logger.debug("[{}] Server failed processing received configuration", debugId); - } - break; - case STATE_UPDATE: - LxWsStateUpdateEvent update = (LxWsStateUpdateEvent) wsMsg.getObject(); - Map perStateUuid = findState(update.getUuid()); - if (perStateUuid != null) { - perStateUuid.forEach((controlUuid, state) -> { - state.setValue(update.getValue(), update.getText()); - LxControl control = state.getControl(); - if (control != null) { - logger.debug("[{}] State update {} ({}:{}) to value {}, text '{}'", debugId, - update.getUuid(), control.getName(), state.getName(), update.getValue(), - update.getText()); - for (LxServerListener listener : listeners) { - listener.onControlStateUpdate(control, state.getName().toLowerCase()); - } - } else { - logger.debug("[{}] State update {} ({}) of unknown control", debugId, update.getUuid(), - state.getName()); - } - }); - } - break; - case SERVER_ONLINE: - for (LxServerListener listener : listeners) { - listener.onServerGoesOnline(); - } - break; - case SERVER_OFFLINE: - LxOfflineReason reason = wsMsg.getOfflineReason(); - String details = null; - if (wsMsg.getObject() instanceof String) { - details = (String) wsMsg.getObject(); - } - logger.debug("[{}] Websocket goes OFFLINE, reason {} : {}.", debugId, reason, details); - - if (reason == LxOfflineReason.TOO_MANY_FAILED_LOGIN_ATTEMPTS) { - // assume credentials are wrong, do not re-attempt connections - // close thread and expect a new LxServer object will have to be re-created - // with corrected configuration - running = false; - } else { - if (reason == LxOfflineReason.UNAUTHORIZED) { - waitTime = userErrorDelay; - } else if (reason == LxOfflineReason.REPEAT_CONNECTION) { - waitTime = 1; - } else { - waitTime = comErrorDelay; - } - socketClient.disconnect(); - } - for (LxServerListener listener : listeners) { - listener.onServerGoesOffline(reason, details); - } - return false; - case CLIENT_CLOSING: - running = false; - return false; - default: - logger.debug("[{}] Received unknown request {}", debugId, wsMsg.getEvent().name()); - break; - } - return true; - } - - } - - /** - * Updates runtime configuration from parsed JSON configuration file of Loxone Miniserver (LoxApp3.json) - * - * @param config - * parsed JSON LoxApp3.json file - */ - private void updateConfig(LxJsonApp3 config) { - logger.trace("[{}] Updating configuration from Miniserver", debugId); - - invalidateMap(rooms); - invalidateMap(categories); - invalidateMap(controls); - invalidateMap(states); - - if (config.msInfo != null) { - logger.trace("[{}] updating global config", debugId); - miniserverName = buildName(config.msInfo.msName); - projectName = buildName(config.msInfo.projectName); - location = buildName(config.msInfo.location); - serial = buildName(config.msInfo.serialNr); - cloudAddress = buildName(config.msInfo.remoteUrl); - swVersion = buildName(config.msInfo.swVersion); - macAddress = buildName(config.msInfo.macAddress); - } else { - logger.warn("[{}] missing global configuration msInfo on Loxone", debugId); - } - - // create internal structures based on configuration file - if (config.rooms != null) { - logger.trace("[{}] creating rooms", debugId); - for (LxJsonApp3.LxJsonRoom room : config.rooms.values()) { - addOrUpdateRoom(new LxUuid(room.uuid), room.name); - } - } - if (config.cats != null) { - logger.trace("[{}] creating categories", debugId); - for (LxJsonApp3.LxJsonCat cat : config.cats.values()) { - addOrUpdateCategory(new LxUuid(cat.uuid), cat.name, cat.type); - } - } - if (config.controls != null) { - logger.trace("[{}] creating controls", debugId); - for (LxJsonApp3.LxJsonControl ctrl : config.controls.values()) { - // create a new control or update existing one - try { - addOrUpdateControl(ctrl); - } catch (Exception e) { - logger.error("[{}] exception creating control {}: ", debugId, ctrl.name, e); - } - } - } - // remove items that do not exist anymore in Miniserver - logger.trace("[{}] removing unused objects", debugId); - removeUnusedFromMap(rooms); - removeUnusedFromMap(categories); - removeUnusedFromMap(controls); - removeUnusedFromMap(states); - } - - /** - * Removes all entries from a map, that do not have the 'updated' flag set on UUID key - * - * @param - * any type of container used in the map - * @param map - * map to remove entries from - */ - - private void removeUnusedFromMap(Map map) { - for (Iterator> it = map.entrySet().iterator(); it.hasNext();) { - Map.Entry entry = it.next(); - if (!entry.getKey().getUpdate()) { - it.remove(); - if (entry.getValue() instanceof LxControl) { - ((LxControl) entry.getValue()).dispose(); - } - } - } - } - - /** - * Sets all entries in a map to not updated - * - * @param map - * map to invalidate entries in - */ - private void invalidateMap(Map map) { - map.keySet().forEach(k -> { - k.setUpdate(false); - }); - } - - /** - * Search for a room with given UUID - * - * @param id - * UUID of a room to search for - * @return - * found room on null if not found - */ - private LxContainer findRoom(LxUuid id) { - if (rooms == null || id == null) { - return null; - } - if (rooms.containsKey(id)) { - return rooms.get(id); - } - return null; - } - - /** - * Add a room to the server, if a room with same UUID already does not exist, otherwise update it with new name. - * - * @param id - * UUID of the room to add - * @param name - * name of the room to add - * @return - * room object (either newly created or already existing) or null if wrong parameters - */ - private LxContainer addOrUpdateRoom(LxUuid id, String name) { - if (rooms == null) { - return null; - } - LxContainer r = findRoom(id); - if (r != null) { - r.setName(name); - return r; - } - LxContainer nr = new LxContainer(id, name); - rooms.put(id, nr); - return nr; - } - - /** - * Search for a state with given UUID - * - * @param id - * UUID of state to locate - * @return - * map of all state objects with control UUID as key - */ - private Map findState(LxUuid id) { - if (states == null || id == null) { - return null; - } - if (states.containsKey(id)) { - return states.get(id); - } - return null; - } - - /** - * Search for a category on the server - * - * @param id - * UUID of the category to find - * @return - * category object found or null if not found - */ - private LxCategory findCategory(LxUuid id) { - if (categories == null || id == null) { - return null; - } - if (categories.containsKey(id)) { - return categories.get(id); - } - return null; - } - - /** - * Add a new category or update and return existing one with same UUID - * - * @param id - * UUID of the category to add or update - * @param name - * name of the category - * @param type - * type of the category - * @return - * newly added category or already existing and updated, null if wrong parameters/configuration - */ - private LxCategory addOrUpdateCategory(LxUuid id, String name, String type) { - if (categories == null) { - return null; - } - LxCategory c = findCategory(id); - if (c != null) { - c.setName(name); - c.setType(type); - return c; - } - LxCategory nc = new LxCategory(id, name, type); - categories.put(id, nc); - return nc; - } - - /** - * Add a new control and its states or update and return existing one with same UUID - * - * @param json - * JSON original object of this control to get extra parameters - */ - private void addOrUpdateControl(LxJsonApp3.LxJsonControl json) { - if (json == null || json.uuidAction == null || json.name == null || json.type == null) { - return; - } - - LxUuid categoryId = null; - if (json.cat != null) { - categoryId = new LxUuid(json.cat); - } - LxUuid roomId = null; - if (json.room != null) { - roomId = new LxUuid(json.room); - } - LxContainer room = findRoom(roomId); - LxCategory category = findCategory(categoryId); - - LxUuid id = new LxUuid(json.uuidAction); - LxControl control = findControl(id); - if (control != null) { - control.update(json, room, category); - } else { - control = LxControlFactory.createControl(socketClient, id, json, room, category); - } - if (control != null) { - updateControls(control); - } - } - - /** - * Updates server structures with a new or updated control and its states and subcontrols - * - * @param control - * control to update in server structures - */ - private void updateControls(LxControl control) { - for (LxControlState state : control.getStates().values()) { - state.getUuid().setUpdate(true); - Map perUuid = states.get(state.getUuid()); - if (perUuid == null) { - perUuid = new HashMap<>(); - states.put(state.getUuid(), perUuid); - } - perUuid.put(control.uuid, state); - } - controls.put(control.uuid, control); - control.uuid.setUpdate(true); - for (LxControl subControl : control.getSubControls().values()) { - updateControls(subControl); - } - } - - /** - * Check and convert null string to empty string. - * - * @param name - * string to check - * @return - * string guaranteed to be not null - */ - private String buildName(String name) { - if (name == null) { - return ""; - } - return name; - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxServerEvent.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxServerEvent.java deleted file mode 100644 index 79ac589024de2..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxServerEvent.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import org.openhab.binding.loxone.internal.core.LxWsClient.LxWebSocket; - -/** - * Event used to communicate between websocket client ({@link LxWebSocket}) and object representing a Miniserver - * ({@link LxServer}) - * - * @author Pawel Pieczul - initial contribution - * - */ -class LxServerEvent { - /** - * Type of {@link LxServerEvent} event - * - * @author Pawel Pieczul - initial contribution - * - */ - public enum EventType { - /** - * Unspecified event - */ - NONE, - /** - * Miniserver is online - websocket connection ready to pass commands and receive controls state updates - */ - SERVER_ONLINE, - /** - * Miniserver is offline - websocket connection is closed. There is a reason parameter associated. - */ - SERVER_OFFLINE, - /** - * Received configuration of Miniserver. There is a {@link LxJsonApp3} object associated. - */ - RECEIVED_CONFIG, - /** - * Received control's state value or text update from Miniserver. There is a {@link LxWsStateUpdateEvent} object - * associated. - */ - STATE_UPDATE, - /** - * Received request to shutdown thread from {@link LxServer} object. - */ - CLIENT_CLOSING - } - - private EventType event; - private LxOfflineReason reason; - private Object object; - - LxServerEvent(EventType event, LxOfflineReason reason, Object object) { - this.event = event; - this.reason = reason; - this.object = object; - } - - /** - * Get type of event - * - * @return - * type of event - */ - EventType getEvent() { - return event; - } - - /** - * Get reason for server going offline - * - * @return - * reason for going offline - */ - LxOfflineReason getOfflineReason() { - return reason; - } - - /** - * Get object associated with the event - * - * @return - * object associated with event - */ - Object getObject() { - return object; - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxServerListener.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxServerListener.java deleted file mode 100644 index b0ae5889c2148..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxServerListener.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.util.Map; - -/** - * Interface to get notifications about {@link LxServer} asynchronous events. - * These events are triggered by messages received from Miniserver over websocket connection or the state of the - * connection to the Miniserver. - * - * @author Pawel Pieczul - initial contribution - * - */ -public interface LxServerListener { - /** - * Called by {@link LxServer} thread when new configuration is received from Loxone Miniserver and stored in - * {@link LxServer} object. - * - * @param server - * server object that can be queried for new configuration of the Miniserver - * - */ - void onNewConfig(LxServer server); - - /** - * Called by {@link LxServer} thread when Loxone Miniserver goes online and communication channel is established and - * ready - * to send commands and - * receive state updates. - */ - void onServerGoesOnline(); - - /** - * Called by {@link LxServer} thread when Loxone Miniserver goes offline and communication channel is broken. - * - * @param reason - * reason for going offline - * @param details - * details describing the disconnection reason - */ - void onServerGoesOffline(LxOfflineReason reason, String details); - - /** - * Called by {@link LxServer} thread when a state of a control is updated on the Loxone Miniserver - * - * @param control - * control object, which state changed - * @param stateName - * name of the state that was updated - */ - void onControlStateUpdate(LxControl control, String stateName); - - Object getSetting(String name); - - void setSettings(Map properties); - -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxUuid.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxUuid.java deleted file mode 100644 index 781668541f1c4..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxUuid.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -/** - * Unique identifier of an object on Loxone Miniserver. - *

- * It is defined by the Miniserver. UUID can represent a control, room, category, etc. and provides a unique ID space - * across all objects residing on the Miniserver. - * - * @author Pawel Pieczul - initial contribution - * - */ -public class LxUuid { - private String uuid; - private String uuidOriginal; - private boolean updated; - - /** - * Create a new {@link LxUuid} object from an UUID on a Miniserver. - * - * @param uuid - * identifier retrieved from Loxone Miniserver - */ - public LxUuid(String uuid) { - init(uuid); - } - - public LxUuid(byte data[], int offset) { - String id = String.format("%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x", - ByteBuffer.wrap(data, offset, 4).order(ByteOrder.LITTLE_ENDIAN).getInt(), - ByteBuffer.wrap(data, offset + 4, 2).order(ByteOrder.LITTLE_ENDIAN).getShort(), - ByteBuffer.wrap(data, offset + 6, 2).order(ByteOrder.LITTLE_ENDIAN).getShort(), data[offset + 8], - data[offset + 9], data[offset + 10], data[offset + 11], data[offset + 12], data[offset + 13], - data[offset + 14], data[offset + 15]); - init(id); - } - - private void init(String uuid) { - uuidOriginal = uuid; - this.uuid = uuidOriginal.replaceAll("[^a-zA-Z0-9-]", "-").toUpperCase(); - updated = true; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null) { - return false; - } - if (o.getClass() != getClass()) { - return false; - } - LxUuid id = (LxUuid) o; - return uuid.equals(id.uuid); - } - - @Override - public int hashCode() { - return uuid.hashCode(); - } - - @Override - public String toString() { - return uuid; - } - - /** - * Returns an original string that was used to create UUID. - * - * @return - * original string for the UUID - */ - - public String getOriginalString() { - return uuidOriginal; - } - - /** - * Indicate the object corresponding to UUID has recently been updated. - * - * @param updated - * true if object has been updated - */ - void setUpdate(boolean updated) { - this.updated = updated; - } - - /** - * See if the object corresponding to UUID has been recently updated. - * - * @return - * true if object was updated - */ - boolean getUpdate() { - return updated; - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsClient.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsClient.java deleted file mode 100644 index 7f0df2d22cf9a..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsClient.java +++ /dev/null @@ -1,1006 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.InetAddress; -import java.net.URI; -import java.net.URL; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import org.apache.commons.codec.binary.Hex; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.StatusCode; -import org.eclipse.jetty.websocket.api.WebSocketPolicy; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; -import org.eclipse.jetty.websocket.api.annotations.WebSocket; -import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; -import org.eclipse.jetty.websocket.client.WebSocketClient; -import org.eclipse.smarthome.core.common.ThreadPoolManager; -import org.openhab.binding.loxone.internal.core.LxJsonResponse.LxJsonCfgApi; -import org.openhab.binding.loxone.internal.core.LxJsonResponse.LxJsonSubResponse; -import org.openhab.binding.loxone.internal.core.LxServer.Configuration; -import org.openhab.binding.loxone.internal.core.LxServerEvent.EventType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.Gson; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSyntaxException; - -/** - * Websocket client facilitating communication with Loxone Miniserver. - * This client is implemented as a state machine, according to guidelines in Loxone API documentation. - * It uses jetty websocket client and creates one own thread to send keep-alive messages to the Miniserver. - * - * @author Pawel Pieczul - initial contribution - * - */ -class LxWsClient { - private final Configuration configuration; - private final InetAddress host; - private final int port; - private final String user; - private final String password; - private final int debugId; - private long keepAlivePeriod = 240; // 4 minutes, server timeout is 5 minutes - private long connectTimeout = 4; // 4 seconds to wait for connection response - private int maxBinMsgSize = 3 * 1024; // 3 MB - private int maxTextMsgSize = 512; // 512 KB - - private String swVersion; - private String macAddress; - private LxWsSecurityType securityType; - private final Gson gson = new Gson(); - private ScheduledFuture timeout; - private LxWebSocket socket; - private WebSocketClient wsClient; - private BlockingQueue queue; - private ClientState state = ClientState.IDLE; - private final Lock stateMachineLock = new ReentrantLock(); - private final Logger logger = LoggerFactory.getLogger(LxWsClient.class); - - private static final ScheduledExecutorService SCHEDULER = ThreadPoolManager - .getScheduledPool(LxWsClient.class.getName()); - - private static final String SOCKET_URL = "/ws/rfc6455"; - private static final String CMD_ACTION = "jdev/sps/io/"; - private static final String CMD_KEEPALIVE = "keepalive"; - private static final String CMD_ENABLE_UPDATES = "jdev/sps/enablebinstatusupdate"; - private static final String CMD_GET_APP_CONFIG = "data/LoxAPP3.json"; - private static final String CMD_CFG_API = "jdev/cfg/api"; - - /** - * Internal state of the websocket client. - * - * @author Pawel Pieczul - initial contribution - * - */ - private enum ClientState { - /** - * Waiting for connection request - */ - IDLE, - /** - * Connection requested, waiting for confirmation - */ - CONNECTING, - /** - * Connection confirmed and established - */ - CONNECTED, - /** - * Waiting for Miniserver's configuration - */ - UPDATING_CONFIGURATION, - /** - * Ready to send commands and receive state updates - */ - RUNNING, - /** - * Received internal request to shutdown - */ - CLOSING - } - - /** - * Type of a binary message received from the Miniserver - * - * @author Pawel Pieczul - * - */ - enum MessageType { - /** - * Text message - jetty websocket client will pass it on automatically to a callback - */ - TEXT_MESSAGE, - /** - * Binary file - */ - BINARY_FILE, - /** - * A set of value states for controls that changed their state - */ - EVENT_TABLE_OF_VALUE_STATES, - /** - * A set of text states for controls that changed their state - */ - EVENT_TABLE_OF_TEXT_STATES, - EVENT_TABLE_OF_DAYTIMER_STATES, - OUT_OF_SERVICE_INDICATOR, - /** - * Response to keepalive request message - */ - KEEPALIVE_RESPONSE, - EVENT_TABLE_OF_WEATHER_STATES, - /** - * Unknown header - */ - UNKNOWN - } - - /** - * A header of a binary message received from Loxone Miniserver on a websocket connection. - * - * @author Pawel Pieczul - initial contribution - * - */ - private class LxWsBinaryHeader { - MessageType type = MessageType.UNKNOWN; - - /** - * Create header from binary buffer at a given offset - * - * @param buffer - * buffer with received message - * @param offset - * offset in bytes at which header is expected - */ - LxWsBinaryHeader(byte[] buffer, int offset) throws IndexOutOfBoundsException { - if (buffer[offset] != 0x03) { - return; - } - switch (buffer[offset + 1]) { - case 0: - type = MessageType.TEXT_MESSAGE; - break; - case 1: - type = MessageType.BINARY_FILE; - break; - case 2: - type = MessageType.EVENT_TABLE_OF_VALUE_STATES; - break; - case 3: - type = MessageType.EVENT_TABLE_OF_TEXT_STATES; - break; - case 4: - type = MessageType.EVENT_TABLE_OF_DAYTIMER_STATES; - break; - case 5: - type = MessageType.OUT_OF_SERVICE_INDICATOR; - break; - case 6: - type = MessageType.KEEPALIVE_RESPONSE; - break; - case 7: - type = MessageType.EVENT_TABLE_OF_WEATHER_STATES; - break; - default: - type = MessageType.UNKNOWN; - break; - } - // These fields are not used today , but left it for future reference - // estimated = ((buffer[offset + 2] & 0x01) != 0); - // length = ByteBuffer.wrap(buffer, offset + 3, 4).getInt(); - } - } - - /** - * Create websocket client object - * - * @param debugId - * instance of the client used for debugging purposes only - * @param queue - * message queue to communicate with its master {@link LxServer}, must be already initialized - * @param configuration - * configuration object for getting and setting custom properties - * @param securityType - * type of authentication/encryption method to use - * @param host - * Miniserver's host address - * @param port - * Miniserver's web services port - * @param user - * user to authenticate - * @param password - * password to authenticate - */ - LxWsClient(int debugId, BlockingQueue queue, Configuration configuration, - LxWsSecurityType securityType, InetAddress host, int port, String user, String password) { - this.debugId = debugId; - this.queue = queue; - this.configuration = configuration; - this.securityType = securityType; - this.host = host; - this.port = port; - this.user = user; - this.password = password; - } - - /** - * Connect the websocket. - * Attempts to connect to the websocket on a remote Miniserver. - * - * @return - * true if connection request initiated correctly, false if not - */ - boolean connect() { - logger.trace("[{}] connect() websocket", debugId); - stateMachineLock.lock(); - try { - if (state != ClientState.IDLE) { - close("Attempt to connect a websocket in non-idle state: " + state); - return false; - } - - socket = new LxWebSocket(); - wsClient = new WebSocketClient(); - - String message = socket.httpGet(CMD_CFG_API); - if (message != null) { - LxJsonSubResponse response = socket.getSubResponse(message); - if (response != null && response.code == 200 && response.value != null) { - try { - LxJsonCfgApi cfgApi = gson.fromJson(response.value.getAsString(), LxJsonCfgApi.class); - swVersion = cfgApi.version; - macAddress = cfgApi.snr; - } catch (JsonSyntaxException | NumberFormatException e) { - logger.debug("[{}] Error parsing API config response: {}, {}", debugId, response, - e.getMessage()); - } - } else { - logger.debug("[{}] Http get null or error in reponse for API config request.", debugId); - } - } else { - logger.debug("[{}] Http get failed for API config request.", debugId); - } - - try { - wsClient.start(); - - URI target = new URI("ws://" + host.getHostAddress() + ":" + port + SOCKET_URL); - ClientUpgradeRequest request = new ClientUpgradeRequest(); - request.setSubProtocols("remotecontrol"); - - startResponseTimeout(); - wsClient.connect(socket, target, request); - setClientState(ClientState.CONNECTING); - - logger.debug("[{}] Connecting to server : {} ", debugId, target); - return true; - } catch (Exception e) { - setClientState(ClientState.IDLE); - close("Connection to websocket failed : " + e.getMessage()); - return false; - } - } finally { - stateMachineLock.unlock(); - } - } - - /** - * Disconnect from the websocket with provided reason. This method is called from {@link LxServer} level and also - * from unsuccessful connect attempt. - * After calling this method, client is ready to perform a new connection request with {@link #connect()}. - * - * @param reason - * text describing reason for disconnection - */ - private void disconnect(String reason) { - logger.trace("[{}] disconnect() websocket : {}", debugId, reason); - stateMachineLock.lock(); - try { - if (wsClient != null) { - try { - close(reason); - wsClient.stop(); - wsClient = null; - } catch (Exception e) { - logger.debug("[{}] Failed to stop websocket client, message = {}", debugId, e.getMessage()); - } - } else { - logger.debug("[{}] Attempt to disconnect websocket client, but wsClient == null", debugId); - } - } finally { - stateMachineLock.unlock(); - } - } - - /** - * Disconnect from the websocket. - * After calling this method, client is ready to perform a new connection request with {@link #connect()}. - */ - void disconnect() { - disconnect("Disconnecting websocket client"); - } - - /** - * Close websocket session from within {@link LxWsClient}, without stopping the client. - * To close session from {@link LxServer} level, use {@link #disconnect()} - * - * @param reason - * reason for closing the websocket - */ - private void close(String reason) { - logger.trace("[{}] close() websocket", debugId); - stateMachineLock.lock(); - try { - stopResponseTimeout(); - if (socket != null) { - if (socket.session != null) { - if (state != ClientState.IDLE) { - logger.debug("[{}] Closing websocket session, reason : {}", debugId, reason); - setClientState(ClientState.CLOSING); - } else { - logger.debug("[{}] Closing websocket, state already IDLE.", debugId); - } - socket.session.close(StatusCode.NORMAL, reason); - } else { - logger.debug("[{}] Closing websocket, but no session, reason : {}", debugId, reason); - setClientState(ClientState.IDLE); - } - } else { - logger.debug("[{}] Closing websocket, but socket = null", debugId); - } - } finally { - stateMachineLock.unlock(); - } - } - - /** - * Notify {@link LxServer} about server going offline and close websocket session from within - * {@link LxWsClient}, - * without stopping the client. - * To close session from {@link LxServer} level, use {@link #disconnect()} - * - * @param reasonCode - * reason code for server going offline - * @param reasonText - * reason text (description) for server going offline - */ - private void notifyAndClose(LxOfflineReason reasonCode, String reasonText) { - notifyMaster(EventType.SERVER_OFFLINE, reasonCode, reasonText); - close(reasonText); - } - - /** - * Update configuration parameter(s) in runtime. - * Calling this method will not interrupt existing services, changes will take effect when new values are used next - * time. - * - * @param keepAlivePeriod - * new period between keep alive messages, in seconds - * @param connectTimeout - * Time to wait for websocket connect response from the Miniserver - * @param maxBinMsgSize - * maximum binary message size of websocket client (in kB) - * @param maxTextMsgSize - * maximum text message size of websocket client (in kB) - */ - void update(int keepAlivePeriod, int connectTimeout, int maxBinMsgSize, int maxTextMsgSize) { - if (keepAlivePeriod > 0 && this.keepAlivePeriod != keepAlivePeriod) { - logger.debug("[{}] Changing keepAlivePeriod to {}", debugId, keepAlivePeriod); - this.keepAlivePeriod = keepAlivePeriod; - } - if (connectTimeout > 0 && this.connectTimeout != connectTimeout) { - logger.debug("[{}] Changing connectTimeout to {}", debugId, connectTimeout); - this.connectTimeout = connectTimeout; - } - if (maxBinMsgSize > 0 && this.maxBinMsgSize != maxBinMsgSize) { - logger.debug("[{}] Changing maxBinMsgSize to {}", debugId, maxBinMsgSize); - this.maxBinMsgSize = maxBinMsgSize; - } - if (maxTextMsgSize > 0 && this.maxTextMsgSize != maxTextMsgSize) { - logger.debug("[{}] Changing maxTextMsgSize to {}", debugId, maxTextMsgSize); - this.maxTextMsgSize = maxTextMsgSize; - } - } - - /** - * Sends an action to a Loxone Miniserver's control. - * - * @param id - * identifier of the control - * @param operation - * identifier of the operation - * @return - * true if action was executed by the Miniserver - */ - boolean sendAction(LxUuid id, String operation) { - String command = CMD_ACTION + id.getOriginalString() + "/" + operation; - logger.debug("[{}] Sending command {}", debugId, command); - LxJsonSubResponse response = socket.sendCmdWithResp(command, true, true); - if (response == null) { - logger.debug("[{}] Error sending command {}", debugId, command); - return false; - } - if (response.code != 200) { - logger.debug("[{}] Received error response {} to command {}", debugId, response.code, command); - return false; - } - return true; - } - - /** - * Returns {@link Gson} object so it can be reused without creating a new instance. - * - * @return - * Gson object for reuse - */ - Gson getGson() { - return gson; - } - - /** - * Sets a new websocket client state. - * The caller must take care of thread synchronization. - * - * @param state - * new state to set - */ - private void setClientState(LxWsClient.ClientState state) { - logger.debug("[{}] changing client state to: {}", debugId, state); - this.state = state; - } - - /** - * Start a timer to wait for a Miniserver response to an action sent from the binding. - * When timer expires, connection is removed and server error is reported. Further connection attempt can be made - * later by the upper layer. - * If a previous timer is running, it will be stopped before a new timer is started. - * The caller must take care of thread synchronization. - */ - private void startResponseTimeout() { - stopResponseTimeout(); - timeout = SCHEDULER.schedule(this::responseTimeout, connectTimeout, TimeUnit.SECONDS); - } - - /** - * Called when response timeout occurred. - */ - private void responseTimeout() { - stateMachineLock.lock(); - try { - logger.debug("[{}] Miniserver response timeout", debugId); - notifyMaster(EventType.SERVER_OFFLINE, LxOfflineReason.COMMUNICATION_ERROR, - "Miniserver response timeout occured"); - disconnect(); - } finally { - stateMachineLock.unlock(); - } - } - - /** - * Stops scheduled timeout waiting for a Miniserver response - * The caller must take care of thread synchronization. - */ - private void stopResponseTimeout() { - logger.trace("[{}] stopping response timeout in state {}", debugId, state); - if (timeout != null) { - timeout.cancel(true); - timeout = null; - } - } - - /** - * Sends an event to {@link LxServer} object - * - * @param event - * event that happened - * @param reason - * reason for the event (applicable to server OFFLINE event} - * @param object - * additional data for the event (text message for OFFLINE event, data for state changes) - */ - private void notifyMaster(EventType event, LxOfflineReason reason, Object object) { - LxOfflineReason localReason; - if (reason == null) { - localReason = LxOfflineReason.NONE; - } else { - localReason = reason; - } - LxServerEvent sync = new LxServerEvent(event, localReason, object); - try { - queue.put(sync); - } catch (InterruptedException e) { - logger.debug("[{}] Interrupted queue operation", debugId); - } - } - - /** - * Implementation of jetty websocket client - * - * @author Pawel Pieczul - initial contribution - * - */ - @WebSocket - public class LxWebSocket { - Session session; - private ScheduledFuture keepAlive; - private LxWsBinaryHeader header; - private LxWsSecurity security; - private boolean syncRequest; - private LxJsonSubResponse commandResponse; - private final Lock responseLock = new ReentrantLock(); - private final Condition responseAvailable = responseLock.newCondition(); - - @OnWebSocketConnect - public void onConnect(Session session) { - stateMachineLock.lock(); - try { - if (state != ClientState.CONNECTING) { - logger.debug("[{}] Unexpected connect received on websocket in state {}", debugId, state); - return; - } - - WebSocketPolicy policy = session.getPolicy(); - policy.setMaxBinaryMessageSize(maxBinMsgSize * 1024); - policy.setMaxTextMessageSize(maxTextMsgSize * 1024); - - logger.debug("[{}] Websocket connected (maxBinMsgSize={}, maxTextMsgSize={})", debugId, - policy.getMaxBinaryMessageSize(), policy.getMaxTextMessageSize()); - this.session = session; - setClientState(ClientState.CONNECTED); - - security = LxWsSecurity.create(securityType, swVersion, debugId, configuration, socket, user, password); - security.authenticate((result, details) -> { - if (result == LxOfflineReason.NONE) { - authenticated(); - } else { - notifyAndClose(result, details); - } - }); - } finally { - stateMachineLock.unlock(); - } - } - - @OnWebSocketClose - public void onClose(int statusCode, String reason) { - stateMachineLock.lock(); - try { - logger.debug("[{}] Websocket connection in state {} closed with code {} reason : {}", debugId, state, - statusCode, reason); - if (security != null) { - security.cancel(); - } - stopKeepAlive(); - if (state == ClientState.CLOSING) { - session = null; - } else if (state != ClientState.IDLE) { - responseLock.lock(); - try { - commandResponse = null; - responseAvailable.signalAll(); - } finally { - responseLock.unlock(); - } - notifyMaster(EventType.SERVER_OFFLINE, LxOfflineReason.getReason(statusCode), reason); - } - setClientState(ClientState.IDLE); - } finally { - stateMachineLock.unlock(); - } - } - - @OnWebSocketError - public void onError(Throwable error) { - logger.debug("[{}] Websocket error : {}", debugId, error.getMessage()); - } - - @OnWebSocketMessage - public void onBinaryMessage(byte data[], int msgOffset, int msgLength) { - int offset = msgOffset; - int length = msgLength; - if (logger.isTraceEnabled()) { - String s = Hex.encodeHexString(data); - logger.trace("[{}] Binary message: length {}: {}", debugId, length, s); - } - stateMachineLock.lock(); - try { - if (state != ClientState.RUNNING) { - return; - } - // websocket will receive header and data in turns as two separate binary messages - if (header == null) { - // header expected now - header = new LxWsBinaryHeader(data, offset); - switch (header.type) { - // following header types precede data in next message - case BINARY_FILE: - case EVENT_TABLE_OF_VALUE_STATES: - case EVENT_TABLE_OF_TEXT_STATES: - case EVENT_TABLE_OF_DAYTIMER_STATES: - case EVENT_TABLE_OF_WEATHER_STATES: - break; - // other header types have no data and next message will be header again - default: - header = null; - break; - } - } else { - // data expected now - switch (header.type) { - case EVENT_TABLE_OF_VALUE_STATES: - stopResponseTimeout(); - while (length > 0) { - LxWsStateUpdateEvent event = new LxWsStateUpdateEvent(true, data, offset); - offset += event.getSize(); - length -= event.getSize(); - notifyMaster(EventType.STATE_UPDATE, null, event); - } - break; - case EVENT_TABLE_OF_TEXT_STATES: - while (length > 0) { - LxWsStateUpdateEvent event = new LxWsStateUpdateEvent(false, data, offset); - offset += event.getSize(); - length -= event.getSize(); - notifyMaster(EventType.STATE_UPDATE, null, event); - } - break; - case KEEPALIVE_RESPONSE: - case TEXT_MESSAGE: - default: - break; - } - // header will be next - header = null; - } - } catch (IndexOutOfBoundsException e) { - logger.debug("[{}] malformed binary message received, discarded", debugId); - } finally { - stateMachineLock.unlock(); - } - } - - @OnWebSocketMessage - public void onMessage(String msg) { - stateMachineLock.lock(); - try { - if (logger.isTraceEnabled()) { - String trace = msg; - if (trace.length() > 100) { - trace = msg.substring(0, 100); - } - logger.trace("[{}] received message in state {}: {}", debugId, state, trace); - } - switch (state) { - case IDLE: - case CONNECTING: - logger.debug("[{}] Unexpected message received by websocket in state {}", debugId, state); - break; - case CONNECTED: - case RUNNING: - processResponse(msg); - break; - case UPDATING_CONFIGURATION: - try { - stopResponseTimeout(); - LxJsonApp3 config = gson.fromJson(msg, LxJsonApp3.class); - if (config.msInfo != null) { - config.msInfo.swVersion = swVersion; - config.msInfo.macAddress = macAddress; - } - logger.debug("[{}] Received configuration from server", debugId); - notifyMaster(EventType.RECEIVED_CONFIG, null, config); - setClientState(ClientState.RUNNING); - notifyMaster(EventType.SERVER_ONLINE, null, null); - if (sendCmdWithResp(CMD_ENABLE_UPDATES, false, false) == null) { - notifyAndClose(LxOfflineReason.COMMUNICATION_ERROR, "Failed to enable state updates."); - } - } catch (JsonParseException e) { - notifyAndClose(LxOfflineReason.INTERNAL_ERROR, "Error processing received configuration"); - } - break; - case CLOSING: - default: - break; - } - } finally { - stateMachineLock.unlock(); - } - } - - LxJsonSubResponse getSubResponse(String msg) { - try { - LxJsonSubResponse subResp = gson.fromJson(msg, LxJsonResponse.class).subResponse; - if (subResp == null) { - logger.debug("[{}] Miniserver response subresponse is null: {}", debugId, msg); - return null; - } - if (subResp.control == null) { - logger.debug("[{}] Miniserver response control is null: {}", debugId, msg); - return null; - } - return subResp; - } catch (JsonSyntaxException e) { - logger.debug("[{}] Miniserver response JSON parsing error: {}, {}", debugId, msg, e.getMessage()); - return null; - } - } - - /** - * Returns {@link Gson} object so it can be reused without creating a new instance. - * - * @return - * Gson object for reuse - */ - Gson getGson() { - return LxWsClient.this.getGson(); - } - - /** - * Send a HTTP GET request and return server's response. - * - * @param request - * request content - * @return - * response received - */ - String httpGet(String request) { - HttpURLConnection con = null; - try { - URL url = new URL("http", host.getHostAddress(), port, - request.startsWith("/") ? request : "/" + request); - con = (HttpURLConnection) url.openConnection(); - con.setRequestMethod("GET"); - StringBuilder result = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()))) { - String l; - while ((l = reader.readLine()) != null) { - result.append(l); - } - return result.toString(); - } - } catch (IOException e) { - return null; - } finally { - if (con != null) { - con.disconnect(); - } - } - } - - /** - * Sends a command to the Miniserver and encrypts it if command can be encrypted and encryption is available. - * Request can be synchronous or asynchronous. There is always a response expected to the command, that is a - * standard command response as defined in {@link LxJsonSubResponse}. Such commands are the majority of commands - * used for performing actions on the controls and for executing authentication procedure. - * A synchronous command must not be sent from the websocket thread or it will cause a deadlock. - * An asynchronous command request returns immediately, but the returned value will not contain valid data until - * the response if received. Asynchronous request can be sent from the websocket thread. - * There can be only one command sent which awaits response, whether this is synchronous or asynchronous - * command. For synchronous commands this is ensured naturally, for asynchronous the caller must manage it. - * If this method is called before a response to the previous command is received, it will return error and not - * send the command. - * - * @param command - * command to send to the Miniserver - * @param sync - * true is synchronous request, false if ansynchronous - * @param encrypt - * true if command can be encrypted - * @return - * response received (for sync command) or to be received (for async), null if error occurred - */ - LxJsonSubResponse sendCmdWithResp(String command, boolean sync, boolean encrypt) { - responseLock.lock(); - try { - if (commandResponse != null) { - logger.warn("[{}] Command not sent, previous command not finished: {}", debugId, command); - return null; - } - if (!sendCmdNoResp(command, encrypt)) { - return null; - } - commandResponse = new LxJsonSubResponse(); - commandResponse.control = command; - LxJsonSubResponse response = commandResponse; - syncRequest = sync; - if (sync) { - if (!responseAvailable.await(connectTimeout, TimeUnit.SECONDS)) { - commandResponse = null; - responseTimeout(); - return null; - } - commandResponse = null; - } - return response; - } catch (InterruptedException e) { - logger.debug("[{}] Interrupted waiting for response: {}", debugId, command); - commandResponse = null; - return null; - } finally { - responseLock.unlock(); - } - } - - /** - * Sends a command to the Miniserver and encrypts it if command can be encrypted and encryption is available. - * The request is asynchronous and no response is expected. It can be used to send commands from the websocket - * thread or commands for which the responses are not following the standard format defined in - * {@link LxJsonSubResponse}. - * If the caller expects the non-standard response it should manage its reception and the response timeout. - * - * @param command - * command to send to the Miniserver - * @param encrypt - * true if command can be encrypted - * @return - * true if command was sent (no information if it was received) - */ - private boolean sendCmdNoResp(String command, boolean encrypt) { - stateMachineLock.lock(); - try { - if (session != null && state != ClientState.IDLE && state != ClientState.CONNECTING - && state != ClientState.CLOSING) { - String encrypted = encrypt ? security.encrypt(command) : command; - if (logger.isDebugEnabled()) { - // security.encrypt() may return the original string if it did not encrypt - if (encrypted.equals(command)) { - logger.debug("[{}] Sending string: {}", debugId, command); - } else { - logger.debug("[{}] Sending encrypted string: {}", debugId, command); - logger.debug("[{}] Encrypted: {}", debugId, encrypted); - } - } - try { - session.getRemote().sendString(encrypted); - return true; - } catch (IOException e) { - logger.debug("[{}] Error sending command: {}, {}", debugId, command, e.getMessage()); - return false; - } - } else { - logger.debug("[{}] NOT sending command, state {}: {}", debugId, state, command); - return false; - } - } finally { - stateMachineLock.unlock(); - } - } - - /** - * Process a Miniserver's response to a command. The response is in plain text format as received from the - * websocket, but is expected to follow the standard format defined in {@link LxJsonSubResponse}. - * If there is a thread waiting for the response (on a synchronous command request), the thread will be - * released. - * Only one requester is expected to wait for the response at a time - commands must be sent sequentially - a - * command can be sent only after a response to the previous command was received, whether it was sent - * synchronously or asynchronously. - * If the received message is encrypted, it will be decrypted before processing. - * - * @param message - * websocket message with the response - */ - private void processResponse(String message) { - LxJsonSubResponse subResp = getSubResponse(message); - if (subResp == null) { - return; - } - logger.debug("[{}] Response: {}", debugId, message.trim()); - String control = subResp.control.trim(); - control = security.decryptControl(subResp.control); - // for some reason the responses to some commands starting with jdev begin with dev, not jdev - // this seems to be a bug in the Miniserver - if (control.startsWith("dev/")) { - control = "j" + control; - } - responseLock.lock(); - try { - if (commandResponse == null) { - logger.warn("[{}] Received response, but awaiting none.", debugId); - return; - } - String awaitedControl = commandResponse.control; - if (awaitedControl == null) { - logger.warn("[{}] Malformed awaiting response structure - no control.", debugId); - commandResponse = null; - return; - } - if (!awaitedControl.equals(control)) { - logger.warn("[{}] Waiting for another response: {}", debugId, awaitedControl); - } - commandResponse.code = subResp.code; - commandResponse.value = subResp.value; - if (syncRequest) { - logger.debug("[{}] Releasing command sender with response: {}, {}, {}", debugId, control, - subResp.code, subResp.value); - responseAvailable.signal(); - } else { - logger.debug("[{}] Reponse to asynchronous request: {}, {}, {}", debugId, control, subResp.code, - subResp.value); - commandResponse = null; - } - } finally { - responseLock.unlock(); - } - } - - /** - * Perform actions after user authentication is successfully completed. - * This method sends a request to receive Miniserver configuration. - */ - private void authenticated() { - logger.debug("[{}] Websocket authentication successfull.", debugId); - stateMachineLock.lock(); - try { - setClientState(ClientState.UPDATING_CONFIGURATION); - if (sendCmdNoResp(CMD_GET_APP_CONFIG, false)) { - startResponseTimeout(); - startKeepAlive(); - } else { - notifyAndClose(LxOfflineReason.INTERNAL_ERROR, "Error sending get config command."); - } - } finally { - stateMachineLock.unlock(); - } - } - - /** - * Start keep alive thread. The thread will periodically send keep alive messages until {@link #stopKeepAlive()} - * is called or a connection terminates. - */ - private void startKeepAlive() { - keepAlive = SCHEDULER.scheduleWithFixedDelay(() -> { - stateMachineLock.lock(); - try { - if (state == ClientState.CLOSING || state == ClientState.IDLE || state == ClientState.CONNECTING) { - stopKeepAlive(); - } else { - logger.debug("[{}] sending keepalive message", debugId); - if (!sendCmdNoResp(CMD_KEEPALIVE, false)) { - logger.debug("[{}] error sending keepalive message", debugId); - } - } - } finally { - stateMachineLock.unlock(); - } - }, keepAlivePeriod, keepAlivePeriod, TimeUnit.SECONDS); - } - - /** - * Stops keep alive thread and ceases sending keep alive messages to the Miniserver - * The caller must take care of thread synchronization. - */ - private void stopKeepAlive() { - logger.trace("[{}] stopping keepalives in state {}", debugId, state); - if (keepAlive != null) { - keepAlive.cancel(true); - keepAlive = null; - } - } - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsSecurity.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsSecurity.java deleted file mode 100644 index b35a226b16b3e..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsSecurity.java +++ /dev/null @@ -1,241 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.function.BiConsumer; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -import org.apache.commons.codec.DecoderException; -import org.apache.commons.codec.binary.Hex; -import org.openhab.binding.loxone.internal.core.LxJsonResponse.LxJsonSubResponse; -import org.openhab.binding.loxone.internal.core.LxServer.Configuration; -import org.openhab.binding.loxone.internal.core.LxWsClient.LxWebSocket; - -/** - * Security abstract class providing authentication and encryption services. - * Used by the {@link LxWsClient} during connection establishment to authenticate user and during message exchange for - * encryption and decryption or the messages. - * - * @author Pawel Pieczul - initial contribution - * - */ -abstract class LxWsSecurity { - final int debugId; - final String user; - final String password; - final LxWebSocket socket; - final Configuration configuration; - - LxOfflineReason reason; - String details; - boolean cancel = false; - - private final Lock authenticationLock = new ReentrantLock(); - - /** - * Create an authentication instance. - * - * @param debugId - * instance of the client used for debugging purposes only - * @param configuration - * configuration object for getting and setting custom properties (e.g. token) - * @param socket - * websocket to perform communication with Miniserver - * @param user - * user to authenticate - * @param password - * password to authenticate - */ - LxWsSecurity(int debugId, Configuration configuration, LxWebSocket socket, String user, String password) { - this.debugId = debugId; - this.configuration = configuration; - this.socket = socket; - this.user = user; - this.password = password; - } - - /** - * Initiate user authentication. This method will return immediately and authentication will be done in a separate - * thread asynchronously. On successful or unsuccessful completion, a provided callback will be called with - * information about failed reason and details of failure. In case of success, the reason value will be - * {@link LxOfflineReason#NONE} - * Only one authentication can run in parallel and must be performed sequentially (create no more threads). - * - * @param doneCallback - * callback to execute when authentication is finished or failed - */ - void authenticate(BiConsumer doneCallback) { - Runnable init = () -> { - authenticationLock.lock(); - try { - execute(); - doneCallback.accept(reason, details); - } finally { - authenticationLock.unlock(); - } - }; - new Thread(init).start(); - } - - /** - * Perform user authentication using a specific authentication algorithm. - * This method will be executed in a dedicated thread to allow sending synchronous messages to the Miniserver. - * - * @return - * true when authentication granted - */ - abstract boolean execute(); - - /** - * Cancel authentication procedure and any pending activities. - * It is supposed to be overridden by implementing classes. - */ - void cancel() { - cancel = true; - } - - /** - * Check a response received from the Miniserver for errors, interpret it and store the results in class fields. - * - * @param response - * response received from the Miniserver - * @return - * {@link LxOfflineReason#NONE} when response is correct or a specific {@link LxOfflineReason} - */ - boolean checkResponse(LxJsonSubResponse response) { - if (response == null || cancel) { - reason = LxOfflineReason.COMMUNICATION_ERROR; - return false; - } - reason = LxOfflineReason.getReason(response.code); - return (reason == LxOfflineReason.NONE); - } - - /** - * Hash string (e.g. containing user name and password or token) according to the algorithm required by the - * Miniserver. - * - * @param string - * string to be hashed - * @param hashKeyHex - * hash key received from the Miniserver in hex format - * @return - * hashed string or null if failed - */ - String hashString(String string, String hashKeyHex) { - try { - byte[] hashKeyBytes = Hex.decodeHex(hashKeyHex.toCharArray()); - SecretKeySpec signKey = new SecretKeySpec(hashKeyBytes, "HmacSHA1"); - Mac mac = Mac.getInstance("HmacSHA1"); - mac.init(signKey); - byte[] rawData = mac.doFinal(string.getBytes()); - return Hex.encodeHexString(rawData); - } catch (DecoderException | NoSuchAlgorithmException | InvalidKeyException e) { - return null; - } - } - - /** - * Encrypt string using current encryption algorithm. - * - * @param string - * input string to encrypt - * @return - * encrypted string - */ - String encrypt(String string) { - // by default no encryption - return string; - } - - /** - * Check if control is encrypted and decrypt it using current decryption algorithm. - * If control is not encrypted or decryption is not available or not ready, the control should be returned in its - * original form. - * - * @param control - * control to be decrypted - * @return - * decrypted control or original control in case decryption is unavailable, control is not encrypted or - * other issue occurred - */ - String decryptControl(String control) { - // by default no decryption - return control; - } - - /** - * Set error code and return false. It is used to report detailed error information from inside the algorithms. - * - * @param reason - * reason for failure - * @param details - * details of the failure - * @return - * always false - */ - boolean setError(LxOfflineReason reason, String details) { - if (reason != null) { - this.reason = reason; - } - if (details != null) { - this.details = details; - } - return false; - } - - /** - * Create an authentication instance. - * - * @param type - * type of security algorithm - * @param swVersion - * Miniserver's software version or null if unknown - * @param debugId - * instance of the client used for debugging purposes only - * @param configuration - * configuration object for getting and setting custom properties (e.g. token) - * @param socket - * websocket to perform communication with Miniserver - * @param user - * user to authenticate - * @param password - * password to authenticate - * @return - * created security object - */ - static LxWsSecurity create(LxWsSecurityType type, String swVersion, int debugId, Configuration configuration, - LxWebSocket socket, String user, String password) { - LxWsSecurityType securityType = type; - if (securityType == LxWsSecurityType.AUTO && swVersion != null) { - String[] versions = swVersion.split("[.]"); - if (versions != null && versions.length > 0 && Integer.parseInt(versions[0]) <= 8) { - securityType = LxWsSecurityType.HASH; - } else { - securityType = LxWsSecurityType.TOKEN; - } - } - if (securityType == LxWsSecurityType.HASH) { - return new LxWsSecurityHash(debugId, configuration, socket, user, password); - } else { - return new LxWsSecurityToken(debugId, configuration, socket, user, password); - } - } - -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsSecurityHash.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsSecurityHash.java deleted file mode 100644 index 76735d531d073..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsSecurityHash.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import org.openhab.binding.loxone.internal.core.LxJsonResponse.LxJsonSubResponse; -import org.openhab.binding.loxone.internal.core.LxServer.Configuration; -import org.openhab.binding.loxone.internal.core.LxWsClient.LxWebSocket; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A hash-based authentication algorithm. No encryption and decryption supported. - * The algorithm computes a HMAC-SHA1 hash from the user name and password, using a key received from the Miniserver. - * This hash is sent to the Miniserver to authorize the user. - * - * @author Pawel Pieczul - initial contribution - * - */ -class LxWsSecurityHash extends LxWsSecurity { - - private static final String CMD_GET_KEY = "jdev/sys/getkey"; - private static final String CMD_AUTHENTICATE = "authenticate/"; - - private Logger logger = LoggerFactory.getLogger(LxWsSecurityHash.class); - - /** - * Create a hash-based authentication instance. - * - * @param debugId - * instance of the client used for debugging purposes only - * @param configuration - * configuration object for getting and setting custom properties (e.g. token) - * @param socket - * websocket to perform communication with Miniserver - * @param user - * user to authenticate - * @param password - * password to authenticate - */ - LxWsSecurityHash(int debugId, Configuration configuration, LxWebSocket socket, String user, String password) { - super(debugId, configuration, socket, user, password); - } - - @Override - boolean execute() { - logger.debug("[{}] Starting hash-based authentication.", debugId); - if (password == null || password.isEmpty()) { - return setError(LxOfflineReason.UNAUTHORIZED, "Enter password for hash-based authentication."); - } - LxJsonSubResponse resp = socket.sendCmdWithResp(CMD_GET_KEY, true, false); - if (!checkResponse(resp)) { - return false; - } - String hash = hashString(user + ":" + password, resp.value.getAsString()); - if (hash == null) { - return setError(LxOfflineReason.INTERNAL_ERROR, "Error hashing credentials."); - } - String cmd = CMD_AUTHENTICATE + hash; - if (!checkResponse(socket.sendCmdWithResp(cmd, true, false))) { - return false; - } - logger.debug("[{}] Authenticated - hash based authentication.", debugId); - return true; - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsStateUpdateEvent.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsStateUpdateEvent.java deleted file mode 100644 index 66e2eaa2762f9..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsStateUpdateEvent.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.core; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -/** - * An event received from Loxone Miniserver with control's state update - * - * @author Pawel Pieczul - initial contribution - * - */ -class LxWsStateUpdateEvent { - private LxUuid uuid; - @SuppressWarnings("unused") - private LxUuid iconUuid; - private Double value; - private String text; - private int size = 0; - - /** - * Create new state update event from binary message - * - * @param isValueEvent - * true if this event updates double value, false if it updates text message - * @param data - * buffer with binary message received from Miniserver - * @param offsetParam - * offset in buffer where event is expected - */ - LxWsStateUpdateEvent(boolean isValueEvent, byte data[], int offsetParam) throws IndexOutOfBoundsException { - int offset = offsetParam; - uuid = new LxUuid(data, offset); - offset += 16; - - if (isValueEvent) { - value = ByteBuffer.wrap(data, offset, 8).order(ByteOrder.LITTLE_ENDIAN).getDouble(); - size = 24; - return; - } - - iconUuid = new LxUuid(data, offset); - offset += 16; - - int textLen = ByteBuffer.wrap(data, offset, 4).order(ByteOrder.LITTLE_ENDIAN).getInt(); - offset += 4; - - text = new String(data, offset, textLen); - size = 36 + (textLen % 4 > 0 ? textLen + 4 - (textLen % 4) : textLen); - } - - /** - * Get UUID of this state - * - * @return - * UUID of this state - */ - LxUuid getUuid() { - return uuid; - } - - /** - * Get current value of this state - * - * @return - * current value of the state or null if state has no value - */ - Double getValue() { - return value; - } - - /** - * Get current text value of this state - * - * @return - * current text value of this state - */ - String getText() { - return text; - } - - /** - * Get size of binary representation of state update event in bytes, as received from Loxone Miniserver - * Used to traverse a binary buffer with more than one state update events - * - * @return - * size of event in binary buffer, in bytes - */ - int getSize() { - return size; - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/discovery/LoxoneMiniserverDiscoveryParticipant.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/discovery/LoxoneMiniserverDiscoveryParticipant.java deleted file mode 100644 index 51e6933e72dc7..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/discovery/LoxoneMiniserverDiscoveryParticipant.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.discovery; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import org.eclipse.smarthome.config.discovery.DiscoveryResult; -import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; -import org.eclipse.smarthome.config.discovery.upnp.UpnpDiscoveryParticipant; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.ThingUID; -import org.jupnp.model.meta.DeviceDetails; -import org.jupnp.model.meta.RemoteDevice; -import org.openhab.binding.loxone.internal.LoxoneBindingConstants; -import org.openhab.binding.loxone.internal.handler.LoxoneMiniserverHandler; -import org.osgi.service.component.annotations.Component; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@link LoxoneMiniserverDiscoveryParticipant} class creates Miniserver things. - * It analyzes UPNP devices discovered by the framework and if Loxone Miniserver is found, - * a new thing discovery is reported, which in turn will result in creating a {@link Thing} - * and subsequently a new {@link LoxoneMiniserverHandler} object. - * - * @author Pawel Pieczul - Initial contribution - * - */ -@Component(immediate = true) -public class LoxoneMiniserverDiscoveryParticipant implements UpnpDiscoveryParticipant { - - private Logger logger = LoggerFactory.getLogger(LoxoneMiniserverDiscoveryParticipant.class); - - @Override - public Set getSupportedThingTypeUIDs() { - return LoxoneMiniserverHandler.SUPPORTED_THING_TYPES_UIDS; - } - - @Override - public DiscoveryResult createResult(RemoteDevice device) { - ThingUID uid = getThingUID(device); - if (uid != null) { - Map properties = new HashMap<>(2); - - // After correct Thing UID is created, we have confidence that all following parameters exist and we don't - // need to check for null objects here in the device details - DeviceDetails details = device.getDetails(); - String serial = details.getSerialNumber(); - String host = details.getPresentationURI().getHost(); - String label = details.getFriendlyName() + " @ " + host; - int port = details.getPresentationURI().getPort(); - String vendor = details.getManufacturerDetails().getManufacturer(); - String model = details.getModelDetails().getModelName(); - - logger.debug("Creating discovery result for serial {} label {} port {}", serial, label, port); - properties.put(LoxoneBindingConstants.MINISERVER_PARAM_HOST, host); - properties.put(LoxoneBindingConstants.MINISERVER_PARAM_PORT, port); - properties.put(Thing.PROPERTY_VENDOR, vendor); - properties.put(Thing.PROPERTY_MODEL_ID, model); - properties.put(Thing.PROPERTY_SERIAL_NUMBER, serial); - - return DiscoveryResultBuilder.create(uid).withProperties(properties).withLabel(label) - .withRepresentationProperty(serial).build(); - } - return null; - } - - @Override - public ThingUID getThingUID(RemoteDevice device) { - String manufacturer = device.getDetails().getManufacturerDetails().getManufacturer(); - if (manufacturer != null && manufacturer.toLowerCase().contains("loxone")) { - String model = device.getDetails().getModelDetails().getModelName(); - if (model != null && model.toLowerCase().contentEquals("loxone miniserver")) { - String serial = device.getDetails().getSerialNumber(); - if (serial == null) { - serial = device.getIdentity().getUdn().getIdentifierString(); - } - if (serial != null) { - return new ThingUID(LoxoneBindingConstants.THING_TYPE_MINISERVER, serial); - } - } - } - return null; - } -} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/handler/LoxoneMiniserverHandler.java b/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/handler/LoxoneMiniserverHandler.java deleted file mode 100644 index 07cc9c820f90e..0000000000000 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/handler/LoxoneMiniserverHandler.java +++ /dev/null @@ -1,753 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.loxone.internal.handler; - -import static org.openhab.binding.loxone.internal.LoxoneBindingConstants.*; - -import java.io.IOException; -import java.math.BigDecimal; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.channels.Channels; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import org.eclipse.smarthome.config.core.Configuration; -import org.eclipse.smarthome.core.library.types.DecimalType; -import org.eclipse.smarthome.core.library.types.OnOffType; -import org.eclipse.smarthome.core.library.types.PercentType; -import org.eclipse.smarthome.core.library.types.StopMoveType; -import org.eclipse.smarthome.core.library.types.StringType; -import org.eclipse.smarthome.core.library.types.UpDownType; -import org.eclipse.smarthome.core.thing.Channel; -import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingStatus; -import org.eclipse.smarthome.core.thing.ThingStatusDetail; -import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; -import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; -import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder; -import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; -import org.eclipse.smarthome.core.types.Command; -import org.eclipse.smarthome.core.types.RefreshType; -import org.eclipse.smarthome.core.types.StateDescription; -import org.eclipse.smarthome.core.types.StateOption; -import org.eclipse.smarthome.core.types.UnDefType; -import org.openhab.binding.loxone.internal.LoxoneDynamicStateDescriptionProvider; -import org.openhab.binding.loxone.internal.config.LoxoneMiniserverConfig; -import org.openhab.binding.loxone.internal.core.LxCategory; -import org.openhab.binding.loxone.internal.core.LxContainer; -import org.openhab.binding.loxone.internal.core.LxControl; -import org.openhab.binding.loxone.internal.core.LxControlDimmer; -import org.openhab.binding.loxone.internal.core.LxControlInfoOnlyAnalog; -import org.openhab.binding.loxone.internal.core.LxControlInfoOnlyDigital; -import org.openhab.binding.loxone.internal.core.LxControlJalousie; -import org.openhab.binding.loxone.internal.core.LxControlLightController; -import org.openhab.binding.loxone.internal.core.LxControlLightControllerV2; -import org.openhab.binding.loxone.internal.core.LxControlMood; -import org.openhab.binding.loxone.internal.core.LxControlPushbutton; -import org.openhab.binding.loxone.internal.core.LxControlRadio; -import org.openhab.binding.loxone.internal.core.LxControlSwitch; -import org.openhab.binding.loxone.internal.core.LxControlTextState; -import org.openhab.binding.loxone.internal.core.LxControlTimedSwitch; -import org.openhab.binding.loxone.internal.core.LxOfflineReason; -import org.openhab.binding.loxone.internal.core.LxServer; -import org.openhab.binding.loxone.internal.core.LxServerListener; -import org.openhab.binding.loxone.internal.core.LxUuid; -import org.openhab.binding.loxone.internal.core.LxWsSecurityType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Representation of a Loxone Miniserver. It is an openHAB {@link Thing}, which is used to communicate with - * objects (controls) configured in the Miniserver over {@link Channels}. - * - * @author Pawel Pieczul - Initial contribution - */ -public class LoxoneMiniserverHandler extends BaseThingHandler implements LxServerListener { - - public static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_MINISERVER); - - private LxServer server; - - private ChannelTypeUID switchTypeId = new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH); - private ChannelTypeUID lightCtrlTypeId = new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_LIGHT_CTRL); - private ChannelTypeUID radioButtonTypeId = new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RADIO_BUTTON); - private ChannelTypeUID rollershutterTypeId = new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_ROLLERSHUTTER); - private ChannelTypeUID dimmerTypeId = new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_DIMMER); - private ChannelTypeUID roTextTypeId = new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_TEXT); - private ChannelTypeUID roSwitchTypeId = new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_SWITCH); - private ChannelTypeUID roAnalogTypeId = new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_ANALOG); - private ChannelTypeUID roTimedSwitchDeactivationDelayTypeId = new ChannelTypeUID(BINDING_ID, - MINISERVER_CHANNEL_TYPE_RO_NUMBER); - - private Logger logger = LoggerFactory.getLogger(LoxoneMiniserverHandler.class); - private Map controls = new HashMap<>(); - - private LoxoneDynamicStateDescriptionProvider dynamicStateDescriptionProvider; - - /** - * Create {@link LoxoneMiniserverHandler} object - * - * @param thing - * Thing object that creates the handler - * @param provider - * state description provider service - */ - public LoxoneMiniserverHandler(Thing thing, LoxoneDynamicStateDescriptionProvider provider) { - super(thing); - if (provider != null) { - dynamicStateDescriptionProvider = provider; - } else { - logger.warn("Dynamic state description provider is null"); - } - } - - @Override - public void handleCommand(ChannelUID channelUID, Command command) { - if (server == null) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "No server attached to this thing"); - return; - } - - LxControl control = getControlFromChannelUID(channelUID); - if (control == null) { - // This situation should not happen under normal circumstances, it indicates binding somehow lost its - // controls - logger.error("Received command {} for unknown control.", command); - return; - } - - logger.debug("Control '{}' received command: {}", control.getName(), command); - - try { - if (command instanceof RefreshType) { - updateChannelStates(channelUID, control); - return; - } - - if (control instanceof LxControlSwitch) { - if (command instanceof OnOffType) { - if ((OnOffType) command == OnOffType.ON) { - if (control instanceof LxControlPushbutton) { - ((LxControlPushbutton) control).pulse(); - } else { - ((LxControlSwitch) control).on(); - } - } else { - ((LxControlSwitch) control).off(); - } - } - return; - } - - if (control instanceof LxControlTimedSwitch) { - if (command instanceof OnOffType) { - if (command == OnOffType.ON) { - ((LxControlTimedSwitch) control).pulse(); - } else { - ((LxControlTimedSwitch) control).off(); - } - } - return; - } - - if (control instanceof LxControlDimmer) { - LxControlDimmer dimmer = (LxControlDimmer) control; - if (command instanceof OnOffType) { - if (command == OnOffType.ON) { - dimmer.on(); - } else { - dimmer.off(); - } - } else if (command instanceof PercentType) { - PercentType percentCmd = (PercentType) command; - dimmer.setPosition(percentCmd.doubleValue()); - } - return; - } - - if (control instanceof LxControlJalousie) { - LxControlJalousie jalousie = (LxControlJalousie) control; - if (command instanceof PercentType) { - jalousie.moveToPosition(((PercentType) command).doubleValue() / 100); - } else if (command instanceof UpDownType) { - if ((UpDownType) command == UpDownType.UP) { - jalousie.fullUp(); - } else { - jalousie.fullDown(); - } - } else if (command instanceof StopMoveType) { - if ((StopMoveType) command == StopMoveType.STOP) { - jalousie.stop(); - } - } - return; - } - - if (control instanceof LxControlLightController) { - LxControlLightController controller = (LxControlLightController) control; - if (command instanceof OnOffType) { - if ((OnOffType) command == OnOffType.ON) { - controller.allOn(); - } else { - controller.allOff(); - } - } else if (command instanceof UpDownType) { - if ((UpDownType) command == UpDownType.UP) { - controller.nextScene(); - } else { - controller.previousScene(); - } - } else if (command instanceof DecimalType) { - controller.setScene(((DecimalType) command).intValue()); - } - return; - } - - if (control instanceof LxControlLightControllerV2) { - LxControlLightControllerV2 controller = (LxControlLightControllerV2) control; - if (command instanceof UpDownType) { - if ((UpDownType) command == UpDownType.UP) { - controller.nextMood(); - } else { - controller.previousMood(); - } - } else if (command instanceof DecimalType) { - controller.setMood(((DecimalType) command).intValue()); - } - return; - } - - if (control instanceof LxControlRadio) { - LxControlRadio radio = (LxControlRadio) control; - if (command instanceof OnOffType) { - if ((OnOffType) command == OnOffType.OFF) { - radio.setOutput(0); - } - } else if (command instanceof DecimalType) { - radio.setOutput(((DecimalType) command).intValue()); - } - return; - } - logger.debug("Incompatible operation on control {}", control.getUuid()); - } catch (IOException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); - } - } - - @Override - public void channelLinked(ChannelUID channelUID) { - logger.debug("Channel linked: {}", channelUID.getAsString()); - LxControl control = getControlFromChannelUID(channelUID); - if (control != null) { - updateChannelStates(channelUID, control); - } - } - - @Override - public void initialize() { - logger.trace("Initializing thing"); - LoxoneMiniserverConfig cfg = getConfig().as(LoxoneMiniserverConfig.class); - try { - InetAddress ip = InetAddress.getByName(cfg.host); - server = new LxServer(LxWsSecurityType.getType(cfg.authMethod), ip, cfg.port, cfg.user, cfg.password); - server.addListener(this); - server.update(cfg.firstConDelay, cfg.keepAlivePeriod, cfg.connectErrDelay, cfg.responseTimeout, - cfg.userErrorDelay, cfg.comErrorDelay, cfg.maxBinMsgSize, cfg.maxTextMsgSize); - server.start(); - } catch (UnknownHostException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Unknown host"); - } - } - - @Override - public void onNewConfig(LxServer server) { - logger.trace("Processing new configuration"); - Thing thing = getThing(); - thing.setProperty(MINISERVER_PROPERTY_MINISERVER_NAME, server.getMiniserverName()); - thing.setProperty(MINISERVER_PROPERTY_PROJECT_NAME, server.getProjectName()); - thing.setProperty(MINISERVER_PROPERTY_CLOUD_ADDRESS, server.getCloudAddress()); - thing.setProperty(MINISERVER_PROPERTY_PHYSICAL_LOCATION, server.getLocation()); - thing.setProperty(Thing.PROPERTY_FIRMWARE_VERSION, server.getSwVersion()); - thing.setProperty(Thing.PROPERTY_SERIAL_NUMBER, server.getSerial()); - thing.setProperty(Thing.PROPERTY_MAC_ADDRESS, server.getMacAddress()); - - ArrayList channels = new ArrayList<>(); - ThingBuilder builder = editThing(); - controls.clear(); - dynamicStateDescriptionProvider.removeAllDescriptions(); - - logger.trace("Building new channels ({} controls)", server.getControls().size()); - for (LxControl control : server.getControls().values()) { - List newChannels = createChannelsForControl(control); - if (newChannels != null) { - channels.addAll(newChannels); - for (Channel channel : newChannels) { - ChannelUID id = channel.getUID(); - controls.put(id, control); - } - } - } - - logger.trace("Sorting channels"); - channels.sort(new Comparator() { - @Override - public int compare(Channel c1, Channel c2) { - String label = c1.getLabel(); - if (label == null) { - return 1; - } - return label.compareTo(c2.getLabel()); - } - }); - - logger.trace("Updating thing"); - builder.withChannels(channels); - updateThing(builder.build()); - } - - @Override - public void onControlStateUpdate(LxControl control, String stateName) { - ChannelUID channelId = getChannelIdForControl(control, 0); - - if (control instanceof LxControlLightController - && LxControlLightController.STATE_SCENE_LIST.equals(stateName)) { - LxControlLightController controller = (LxControlLightController) control; - setStateDescription(channelId, null, false, controller.getSceneNames(), BigDecimal.ZERO, - new BigDecimal((LxControlLightController.NUM_OF_SCENES - 1))); - return; - } else if (control instanceof LxControlLightControllerV2) { - LxControlLightControllerV2 controller = (LxControlLightControllerV2) control; - - if (LxControlLightControllerV2.STATE_MOODS_LIST.equals(stateName)) { - // A new list of moods arrived as state update - we update dynamic state description for the channel - // that represents single mood selection and we create new channels per mood and remove any obsolete - // mood channels for this controller - Map moods = controller.getMoods(); - if (moods == null) { - logger.debug("Moods list state was received, but mood list is null."); - return; - } - - // convert all moods to options list for state description - List optionsList = moods.values().stream() - .map(mood -> new StateOption(mood.getId().toString(), mood.getName())) - .collect(Collectors.toList()); - - // for all moods but 'all off' mood create and store channels - Map newChannels = new HashMap<>(); - moods.values().stream().filter(mood -> !mood.isAllOffMood()).forEach( - mood -> createChannelsForControl(mood).forEach(channel -> newChannels.put(channel, mood))); - - dynamicStateDescriptionProvider.setDescription(channelId, - new StateDescription(new BigDecimal(controller.getMinMoodId()), - new BigDecimal(controller.getMaxMoodId()), BigDecimal.ONE, null, false, optionsList)); - - // collect all moods that currently belong to this controller - List toRemove = new ArrayList<>(); - controls.forEach((k, v) -> { - if (v instanceof LxControlMood - && controller.getUuid().equals(((LxControlMood) v).getControllerUuid()) - && !newChannels.containsKey(k)) { - toRemove.add(k); - } - }); - - // remove the collected mood channels from the thing and controls - ThingBuilder builder = editThing(); - toRemove.forEach(k -> { - builder.withoutChannel(k); - controls.remove(k); - }); - - // add channels for the new moods - newChannels.forEach((k, v) -> { - builder.withChannel(k); - controls.put(k.getUID(), v); - }); - - updateThing(builder.build()); - return; - } - } - // for all state updates not handled above just update the channel state the regular way - updateChannelStates(channelId, control); - } - - @Override - public void onServerGoesOnline() { - logger.debug("Server goes online."); - updateStatus(ThingStatus.ONLINE); - } - - @Override - public void onServerGoesOffline(LxOfflineReason reason, String details) { - logger.debug("Server goes offline: {}, {}", reason, details); - - switch (reason) { - case AUTHENTICATION_TIMEOUT: - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "User authentication timeout"); - break; - case COMMUNICATION_ERROR: - String text = "Error communicating with Miniserver"; - if (details != null) { - text += " (" + details + ")"; - } - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, text); - break; - case INTERNAL_ERROR: - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - details != null ? "Internal error (" + details + ")" : "Internal error"); - break; - case TOO_MANY_FAILED_LOGIN_ATTEMPTS: - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Too many failed login attempts - stopped trying"); - break; - case UNAUTHORIZED: - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - details != null ? details : "User authentication error (invalid user name or password)"); - break; - case IDLE_TIMEOUT: - logger.warn("Idle timeout from Loxone Miniserver - adjust keepalive settings"); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Timeout due to no activity"); - break; - default: - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Unknown reason"); - break; - } - } - - @Override - public void dispose() { - logger.debug("Disposing of server"); - dynamicStateDescriptionProvider.removeAllDescriptions(); - if (server != null) { - server.stop(); - server = null; - } - } - - @Override - public Object getSetting(String name) { - return getConfig().get(name); - } - - @Override - public void setSettings(Map properties) { - Configuration config = getConfig(); - properties.forEach((name, value) -> config.put(name, value)); - updateConfiguration(config); - } - - /** - * Create and add a new channel to the channels list. - * - * @param channels - * list of channels to add the channel to - * @param itemType - * item type for the channel - * @param typeId - * channel type ID for the channel - * @param channelId - * channel ID - * @param channelLabel - * channel label - * @param channelDescription - * channel description - * @param tags - * tags for the channel or null if no tags needed - * @return - * true if channel was created and added to the list - */ - private boolean addChannel(List channels, String itemType, ChannelTypeUID typeId, ChannelUID channelId, - String channelLabel, String channelDescription, Set tags) { - if (channels != null && channelId != null && itemType != null && typeId != null && channelDescription != null) { - ChannelBuilder builder = ChannelBuilder.create(channelId, itemType).withType(typeId).withLabel(channelLabel) - .withDescription(channelDescription + " : " + channelLabel); - if (tags != null) { - builder.withDefaultTags(tags); - } - Channel newChannel = builder.build(); - channels.add(newChannel); - return true; - } - return false; - } - - /** - * Creates a new list of {@link Channel} for a single Loxone control object. Registers channel type within the - * factory, which is the channel type provider, or uses one of pre-registered type. - * Most of controls create only one channel, but some of them will create more channels to facilitate different - * types of states they support. - * - * @param control - * control object to create a channel for - * @return - * created list of {@link Channel} object - */ - private List createChannelsForControl(LxControl control) { - logger.trace("Creating channels for control: {}, {}", control.getClass().getSimpleName(), control.getUuid()); - - String label; - ChannelUID id = getChannelIdForControl(control, 0); - - List channels = new ArrayList<>(); - - LxContainer room = control.getRoom(); - String roomName = room != null ? room.getName() : null; - - String controlName = control.getName(); - if (controlName == null) { - // Each control on a Miniserver must have a name defined, but in case this is a subject - // of some malicious data attack, we'll prevent null pointer exception - controlName = "Undefined name"; - } - - if (control instanceof LxControlMood) { - controlName = "Mood / " + controlName; - } - if (roomName != null) { - label = roomName + " / " + controlName; - } else { - label = controlName; - } - - Set tags = new HashSet<>(); - addChannelTags(tags, control); - - // LxControlSwitch covers LxControlPushbutton, LxControlMood and LxControlTimedSwitch as child classes - if (control instanceof LxControlSwitch) { - String description; - if (control instanceof LxControlTimedSwitch) { - description = "Timed switch"; - // adding a deactivation delay channel for timed switch, don't tag it - ChannelUID deactivationDelayChannelId = getChannelIdForControl(control, 1); - addChannel(channels, "Number", roTimedSwitchDeactivationDelayTypeId, deactivationDelayChannelId, - label + " / Deactivation Delay", "Deactivation Delay", null); - } else if (control instanceof LxControlPushbutton) { - // this must be compared after LxControlTimedSwitch (pusbutton is parent class) - description = "Pushbutton"; - } else if (control instanceof LxControlMood) { - description = "Mood mixer"; - } else { - description = "Switch"; - } - addChannel(channels, "Switch", switchTypeId, id, label, description, tags); - } else if (control instanceof LxControlJalousie) { - addChannel(channels, "Rollershutter", rollershutterTypeId, id, label, "Rollershutter", tags); - } else if (control instanceof LxControlInfoOnlyDigital) { - addChannel(channels, "Switch", roSwitchTypeId, id, label, "Digital virtual state", tags); - } else if (control instanceof LxControlInfoOnlyAnalog) { - // add both channel and state description (all needed configuration is available) - if (addChannel(channels, "Number", roAnalogTypeId, id, label, "Analog virtual state", tags)) { - setStateDescription(id, ((LxControlInfoOnlyAnalog) control).getFormatString(), true, null, null, null); - } - } else if (control instanceof LxControlLightController) { - // add only channel, state description will be added later when a control state update message is received - addChannel(channels, "Number", lightCtrlTypeId, id, label, "Light controller", tags); - } else if (control instanceof LxControlLightControllerV2) { - // add only channel, state description will be added later when a control state update message is received - addChannel(channels, "Number", lightCtrlTypeId, id, label, "Light controller V2", tags); - } else if (control instanceof LxControlRadio) { - // add both channel and state description (all needed configuration is available) - if (addChannel(channels, "Number", radioButtonTypeId, id, label, "Radio button", tags)) { - setStateDescription(id, null, false, ((LxControlRadio) control).getOutputs(), BigDecimal.ZERO, - new BigDecimal(LxControlRadio.MAX_RADIO_OUTPUTS)); - } - } else if (control instanceof LxControlTextState) { - addChannel(channels, "String", roTextTypeId, id, label, "Text state", tags); - } else if (control instanceof LxControlDimmer) { - addChannel(channels, "Dimmer", dimmerTypeId, id, label, "Dimmer", tags); - } - return channels; - } - - /** - * Add tags that can be used by homekit transport and Alexa openHAB skill - * - * @param tags - * collection to add tags to - * @param control - * control object for which the tags are to be identified - */ - private void addChannelTags(Set tags, LxControl control) { - if (control instanceof LxControlSwitch) { - // All switches that belong to the lights category can be turned on or off by voice - LxCategory category = control.getCategory(); - if (category != null && category.getType() == LxCategory.CategoryType.LIGHTS) { - tags.add("Lighting"); - } - } - } - - /** - * Update thing's states for all channels associated with the control - * - * @param channelId - * first channel for the control - * @param control - * control to update states for - */ - private void updateChannelStates(ChannelUID channelId, LxControl control) { - if (control instanceof LxControlSwitch) { - Double value = ((LxControlSwitch) control).getState(); - if (value != null) { - if (value == 1.0) { - updateState(channelId, OnOffType.ON); - } else if (value == 0) { - updateState(channelId, OnOffType.OFF); - } - } - // timed switch is a child class of a switch - if (control instanceof LxControlTimedSwitch) { - // getting second channel for this control and update the state - LxControlTimedSwitch timedSwitch = (LxControlTimedSwitch) control; - Double deactivationValue = timedSwitch.getDeactivationDelay(); - if (deactivationValue != null) { - updateState(getChannelIdForControl(timedSwitch, 1), new DecimalType(deactivationValue)); - } - } - } else if (control instanceof LxControlJalousie) { - Double value = ((LxControlJalousie) control).getPosition(); - if (value != null && value >= 0 && value <= 1) { - // state UP or DOWN from Loxone indicates blinds are moving up or down - // state UP in openHAB means blinds are fully up (0%) and DOWN means fully down (100%) - // so we will update only position and not up or down states - updateState(channelId, new PercentType((int) (value * 100))); - } - } else if (control instanceof LxControlDimmer) { - Double value = ((LxControlDimmer) control).getPosition(); - if (value != null && value >= 0 && value <= 100) { - updateState(channelId, new PercentType(value.intValue())); - } - } else if (control instanceof LxControlInfoOnlyDigital) { - Double value = ((LxControlInfoOnlyDigital) control).getValue(); - if (value != null) { - if (value == 0) { - updateState(channelId, OnOffType.OFF); - } else if (value == 1.0) { - updateState(channelId, OnOffType.ON); - } - } - } else if (control instanceof LxControlInfoOnlyAnalog) { - Double value = ((LxControlInfoOnlyAnalog) control).getValue(); - if (value != null) { - updateState(channelId, new DecimalType(value)); - } - } else if (control instanceof LxControlLightController) { - LxControlLightController controller = (LxControlLightController) control; - Integer value = controller.getCurrentScene(); - if (value != null && value >= 0 && value < LxControlLightController.NUM_OF_SCENES) { - updateState(channelId, new DecimalType(value)); - } - } else if (control instanceof LxControlLightControllerV2) { - LxControlLightControllerV2 controller = (LxControlLightControllerV2) control; - List activeMoods = controller.getActiveMoods(); - // update the single mood channel state - if (activeMoods.size() == 1) { - updateState(channelId, new DecimalType(activeMoods.get(0))); - } else { - updateState(channelId, UnDefType.UNDEF); - } - // update the individual mood mixing channels - Map allMoods = controller.getMoods(); - allMoods.values().forEach(v -> { - // we update moods like all other switches with no special dedicated code - updateChannelStates(getChannelIdForControl(v, 0), v); - }); - } else if (control instanceof LxControlRadio) { - LxControlRadio radio = (LxControlRadio) control; - Integer output = radio.getActiveOutput(); - if (output != null && output >= 0 && output <= LxControlRadio.MAX_RADIO_OUTPUTS) { - updateState(channelId, new DecimalType(output)); - } - } else if (control instanceof LxControlTextState) { - LxControlTextState state = (LxControlTextState) control; - String value = state.getText(); - if (value != null) { - updateState(channelId, new StringType(value)); - } - } - } - - /** - * Sets a new {@link StateDescription} for a channel that has multiple options to select from or a custom format - * string. A previous description, if existed, will be replaced. - * - * @param channelUID - * channel UID - * @param format - * format string to present the value - * @param readOnly - * true if this control does not accept commands - * @param options - * collection of options, where key is option ID (number in reality) and value is option name - * @param minimum - * minimum value an option ID can have - * @param maximum - * maximum value an option ID can have - */ - private void setStateDescription(ChannelUID channelUID, String format, boolean readOnly, - Map options, BigDecimal minimum, BigDecimal maximum) { - if (channelUID != null) { - List optionsList = null; - if (options != null) { - optionsList = options.entrySet().stream().map(e -> new StateOption(e.getKey(), e.getValue())) - .collect(Collectors.toList()); - } - dynamicStateDescriptionProvider.setDescription(channelUID, - new StateDescription(minimum, maximum, BigDecimal.ONE, format, readOnly, optionsList)); - } - } - - /** - * Based on channel ID, return corresponding {@link LxControl} object - * - * @param channelUID - * channel ID of the control to find - * @return - * control corresponding to the channel ID or null if not found - */ - private LxControl getControlFromChannelUID(ChannelUID channelUID) { - return controls.get(channelUID); - } - - /** - * Build channel ID for a control, based on control's UUID, thing's UUID and index of the channel for the control - * - * @param control - * control to build the channel ID for - * @param index - * index of a channel within control (0 for primary channel) - * all indexes greater than 0 will have -index added to the channel ID - * @return - * channel ID for the control and index - */ - private ChannelUID getChannelIdForControl(LxControl control, int index) { - String controlId = control.getUuid().toString(); - if (index > 0) { - controlId += "-" + index; - } - return new ChannelUID(getThing().getUID(), controlId); - } -} diff --git a/addons/binding/org.openhab.binding.modbus.test/.classpath b/addons/binding/org.openhab.binding.modbus.test/.classpath deleted file mode 100644 index ee295c1a0e79b..0000000000000 --- a/addons/binding/org.openhab.binding.modbus.test/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.modbus.test/.project b/addons/binding/org.openhab.binding.modbus.test/.project deleted file mode 100644 index 9f690616bfa60..0000000000000 --- a/addons/binding/org.openhab.binding.modbus.test/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.modbus.test - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.modbus.test/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.modbus.test/META-INF/MANIFEST.MF deleted file mode 100644 index 48e0ebbce50b1..0000000000000 --- a/addons/binding/org.openhab.binding.modbus.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,40 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.modbus.test -Bundle-ManifestVersion: 2 -Bundle-Name: Modbus Binding Tests -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.modbus.test;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Fragment-Host: org.openhab.binding.modbus -Require-Bundle: org.hamcrest -Import-Package: - net.bytebuddy.dynamic.loading, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.events, - org.eclipse.smarthome.core.items, - org.eclipse.smarthome.core.library.items, - org.eclipse.smarthome.core.storage, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.link, - org.eclipse.smarthome.core.thing.util, - org.eclipse.smarthome.test.java, - org.eclipse.smarthome.test.storage, - org.hamcrest;core=split, - org.junit;version="4.0.0", - org.junit.runner;version="4.0.0", - org.mockito, - org.mockito.hamcrest, - org.mockito.invocation, - org.mockito.junit, - org.mockito.stubbing, - org.objenesis, - org.openhab.io.transport.modbus, - org.osgi.framework, - org.osgi.service.component, - org.osgi.service.device, - org.slf4j diff --git a/addons/binding/org.openhab.binding.modbus.test/build.properties b/addons/binding/org.openhab.binding.modbus.test/build.properties deleted file mode 100644 index 64491e243ba70..0000000000000 --- a/addons/binding/org.openhab.binding.modbus.test/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -source.. = src/test/java/ -output.. = target/test-classes -bin.includes = META-INF/,\ - .,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.modbus.test/org.openhab.binding.modbus.test.launch b/addons/binding/org.openhab.binding.modbus.test/org.openhab.binding.modbus.test.launch deleted file mode 100644 index a3e7ddff88129..0000000000000 --- a/addons/binding/org.openhab.binding.modbus.test/org.openhab.binding.modbus.test.launch +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.modbus.test/pom.xml b/addons/binding/org.openhab.binding.modbus.test/pom.xml deleted file mode 100644 index 853b9e7b4c37c..0000000000000 --- a/addons/binding/org.openhab.binding.modbus.test/pom.xml +++ /dev/null @@ -1,129 +0,0 @@ - - - 4.0.0 - - - pom - org.openhab.binding - 2.5.0-SNAPSHOT - - - org.openhab.binding.modbus.test - eclipse-test-plugin - - Modbus Binding Tests - - - - org.openhab.io - org.openhab.io.transport.modbus - ${project.version} - - - - - - - ${tycho-groupid} - target-platform-configuration - - - - - eclipse-plugin - org.eclipse.equinox.event - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.ds - 0.0.0 - - - eclipse-plugin - org.eclipse.smarthome.config.xml - 0.0.0 - - - eclipse-plugin - org.eclipse.smarthome.core.thing.xml - 0.0.0 - - - eclipse-plugin - org.eclipse.smarthome.core.binding.xml - 0.0.0 - - - - - - - ${tycho-groupid} - tycho-surefire-plugin - - - - eclipse-plugin - org.eclipse.equinox.ds - 0.0.0 - - - eclipse-plugin - ch.qos.logback.classic - 0.0.0 - - - eclipse-plugin - ch.qos.logback.core - 0.0.0 - - - eclipse-plugin - ch.qos.logback.slf4j - 0.0.0 - - - - - org.eclipse.equinox.ds - 1 - true - - - org.eclipse.equinox.event - 2 - true - - - org.eclipse.smarthome.core - 4 - true - - - org.eclipse.smarthome.core.thing - 4 - true - - - org.eclipse.smarthome.config.xml - 4 - true - - - org.eclipse.smarthome.core.thing.xml - 4 - true - - - org.eclipse.smarthome.core.binding.xml - 4 - true - - - - - - - - diff --git a/addons/binding/org.openhab.binding.modbus/.classpath b/addons/binding/org.openhab.binding.modbus/.classpath deleted file mode 100644 index 04f0184a01454..0000000000000 --- a/addons/binding/org.openhab.binding.modbus/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.modbus/.project b/addons/binding/org.openhab.binding.modbus/.project deleted file mode 100644 index b3562dba628bc..0000000000000 --- a/addons/binding/org.openhab.binding.modbus/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.modbus - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.modbus/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.modbus/META-INF/MANIFEST.MF deleted file mode 100644 index fbb7dc32a5686..0000000000000 --- a/addons/binding/org.openhab.binding.modbus/META-INF/MANIFEST.MF +++ /dev/null @@ -1,34 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.modbus -Bundle-ManifestVersion: 2 -Bundle-Name: Modbus Binding -Bundle-SymbolicName: org.openhab.binding.modbus;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-ClassPath: . -Import-Package: org.apache.commons.lang, - org.apache.commons.lang.builder, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.library.items, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.transform, - org.eclipse.smarthome.core.types, - org.openhab.binding.modbus, - org.openhab.binding.modbus.handler, - org.openhab.binding.modbus.discovery, - org.openhab.io.transport.modbus, - org.openhab.io.transport.modbus.endpoint, - org.openhab.io.transport.modbus.json, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml -Export-Package: org.openhab.binding.modbus, - org.openhab.binding.modbus.handler, - org.openhab.binding.modbus.discovery -Bundle-ActivationPolicy: lazy diff --git a/addons/binding/org.openhab.binding.modbus/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.modbus/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.modbus/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.modbus/build.properties b/addons/binding/org.openhab.binding.modbus/build.properties deleted file mode 100644 index 0f6636e9de4f6..0000000000000 --- a/addons/binding/org.openhab.binding.modbus/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.modbus/pom.xml b/addons/binding/org.openhab.binding.modbus/pom.xml deleted file mode 100644 index 30dd89a08d324..0000000000000 --- a/addons/binding/org.openhab.binding.modbus/pom.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.modbus - - Modbus Binding - eclipse-plugin - - - - org.openhab.io - org.openhab.io.transport.modbus - ${project.version} - - - - diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/.classpath b/addons/binding/org.openhab.binding.nikohomecontrol/.classpath deleted file mode 100644 index 7f457fa4138d1..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/.project b/addons/binding/org.openhab.binding.nikohomecontrol/.project deleted file mode 100644 index acbd84740ae80..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.nikohomecontrol - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/ESH-INF/binding/binding.xml b/addons/binding/org.openhab.binding.nikohomecontrol/ESH-INF/binding/binding.xml deleted file mode 100644 index a38ac98abe57f..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/ESH-INF/binding/binding.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - Niko Home Control Binding - This is the binding for the Niko Home Control system - Mark Herwege - - diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/ESH-INF/thing/thing-types.xml b/addons/binding/org.openhab.binding.nikohomecontrol/ESH-INF/thing/thing-types.xml deleted file mode 100644 index 2e645e8b84f9b..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/ESH-INF/thing/thing-types.xml +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - This bridge represents a Niko Home Control IP-interface - - - - - - - - IP Address of Niko Home Control IP-interface - false - network-address - - - - Port to communicate with Niko Home Control IP-interface, default 8000 - 8000 - true - - - - Refresh interval for connection with Niko Home Control IP-interface (min), default 300. If set to 0 or left empty, no refresh will be scheduled - 300 - true - - - - - - - - - - On/Off type action in Niko Home Control - - - - - - - Niko Home Control IP Interface Action Object ID - false - - - - - - - - - Dimmer type actions in Niko Home Control - - - - - - - Niko Home Control IP Interface Action Object ID - false - - - - Step value used for increase/decrease of dimmer brightness, default 10% - 10 - true - - - - - - - - - Rollershutter type actions in Niko Home Control - - - - - - - Niko Home Control IP Interface Action Object ID - false - - - - - - - - - Thermostat in the Niko Home Control system - - - - - - - - - - Niko Home Control IP Interface Thermostat Object ID - false - - - - Default overrule duration in minutes when an overrule temperature is set without providing overrule time, 60 minutes by default - 60 - true - - - - - - Switch - - Switch control for action in Niko Home Control - Switch - - - Dimmer - - Brightness control for dimmer action in Niko Home Control - DimmableLight - - - Rollershutter - - Rollershutter control for rollershutter action in Niko Home Control - Blinds - - - - Number:Temperature - - Temperature measured by thermostat - Temperature - - CurrentTemperature - - - - - Number:Temperature - - Setpoint temperature of thermostat - Temperature - - TargetTemperature - - - - - Number - - Time duration for overruling thermostat target temperature in min. - Number - - - - Number - - Thermostat mode - Number - - - - - - - - - - - - - - - - trigger - - Alarm from Niko Home Control - - - trigger - - Notice from Niko Home Control - - - diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.nikohomecontrol/META-INF/MANIFEST.MF deleted file mode 100644 index a47d3aaa156f5..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/META-INF/MANIFEST.MF +++ /dev/null @@ -1,26 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.nikohomecontrol -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: . -Bundle-ManifestVersion: 2 -Bundle-Name: Niko Home Control Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.nikohomecontrol;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - com.google.gson, - com.google.gson.stream, - javax.measure.quantity, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.library.unit, - org.eclipse.smarthome.core.net, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.types, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.nikohomecontrol/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/README.md b/addons/binding/org.openhab.binding.nikohomecontrol/README.md deleted file mode 100644 index 19dbda4e1f830..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/README.md +++ /dev/null @@ -1,217 +0,0 @@ -# Niko Home Control Binding - -The Niko Home Control binding integrates with a [Niko Home Control](http://www.nikohomecontrol.be/) system through a Niko Home Control IP-interface. - -The binding has been tested with a Niko Home Control IP-interface (550-00508). This IP-interface provides access on the LAN. -The binding does not require a Niko Home Control Gateway (550-00580), but does work with it in the LAN. It will not make a remote connection. -It has also been confirmed to work with the Niko Home Control Connected Controller (550-00003). -The binding does not work for Niko Home Control II. - -The binding exposes all actions from the Niko Home Control System that can be triggered from the smartphone/tablet interface, as defined in the Niko Home Control programming software. - -Supported action types are switches, dimmers and rollershutters or blinds. -Niko Home Control alarm and notice messages are retrieved and made available in the binding. - -## Supported Things - -The Niko Home Control Controller is represented as a bridge in the binding. -Connected to a bridge, the Niko Home Control Binding supports on/off actions (e.g. for lights or groups of lights), dimmers, rollershutters or blinds and thermostats. - -## Binding Configuration - -The bridge representing the Niko Home Control IP-interface needs to be added first in the things file or through Paper UI. -A bridge can be auto-discovered or created manually. -No bridge configuration is required when using auto-discovery. An auto-discovered bridge will have an IP-address parameter automatically filled with the current IP-address of the IP-interface. This IP-address for the discovered bridge will automatically update when the IP-address of the IP-interface changes. - -The IP-address and port can be set when manually creating the bridge. - -If the IP-address is set on a manually created bridge, no attempt will be made to discover the correct IP-address. You are responsible to force a fixed IP address on the Niko Home Control IP-interface through settings in your DHCP server. - -The port is set to 8000 by default and should match the port used by the Niko Home Control IP-interface. - -An optional refresh interval will be used to restart the bridge at regular intervals (every 300 minutes by default). -Restarting the bridge at regular times improves the connection stability and avoids loss of connection. It can be turned off completely. - -## Discovery - -A discovery scan will first discover the Niko Home Control IP-interface in the network as a bridge. -Default parameters will be used. -Note that this may fail to find the correct Niko Home Control IP-interface when there are multiple IP-interfaces in the network, or when traffic to port 10000 on the openHAB server is blocked. - -When the Niko Home Control bridge is added as a thing, from the discovery inbox or manually, system information will be read from the Niko Home Control Controller and will be put in the bridge properties, visible through Paper UI. - -Subsequently, all defined actions that can be triggered from a smartphone/tablet in the Niko Home Control system will be discovered and put in the inbox. -It is possible to trigger a manual scan for things on the Niko Home Control bridge. - -If the Niko Home Control system has locations configured, these will be copied to thing locations and grouped as such in PaperUI. -Locations can subsequently be changed through the thing location parameter in PaperUI. - -## Thing Configuration - -Besides using PaperUI to manually configure things or adding automatically discovered things through PaperUI, you can add thing definitions in the things file. - -The Thing configuration for the **bridge** uses the following syntax: - -``` -Bridge nikohomecontrol:bridge: [ addr="", port=, - refresh= ] -``` - -`bridgeId` can have any value. - -`addr` is the fixed Niko Home Control IP-interface address and is required. -`port` will be the port used to connect and is 8000 by default. -`refresh` is the interval to restart the communication in minutes (300 by default), if 0 or omitted the connection will not restart at regular intervals. - -The Thing configuration for **Niko Home Control actions** has the following syntax: - -``` -Thing nikohomecontrol::: "Label" @ "Location" - [ actionId=, - step= ] -``` - -or nested in the bridge configuration: - -``` - "Label" @ "Location" [ actionId=, - step= ] -``` - -The following action thing types are valid for the configuration: - -``` -onOff, dimmer, blind -``` - -`thingId` can have any value, but will be set to the same value as the actionId parameter if discovery is used. - -`"Label"` is an optional label for the thing. - -`@ "Location"` is optional, and represents the location of the Thing. Auto-discovery would have assigned a value automatically. - -The `actionId` parameter is the unique ip Interface Object ID (`ipInterfaceObjectId`) as automatically assigned in the Niko Home Control Controller when programming the Niko Home Control system using the Niko Home Control programming software. -It is not directly visible in the Niko Home Control programming or user software, but will be detected and automatically set by openHAB discovery. -For textual configuration, you can manually retrieve it from the content of the .nhcp configuration file created by the programming software. -Open the file with an unzip tool to read it's content. - -The `step` parameter is only available for dimmers. -It sets a step value for dimmer increase/decrease actions. The parameter is optional and set to 10 by default. - -The Thing configuration for **Niko Home Control thermostats** has the following syntax: - -``` -Thing nikohomecontrol:thermostat:: "Label" @ "Location" - [ thermostatId= ] -``` - -or nested in the bridge configuration: - -``` -thermostat "Label" @ "Location" [ thermostatId= ] -``` - -`thingId` can have any value, but will be set to the same value as the thermostatId parameter if discovery is used. - -`"Label"` is an optional label for the Thing. - -`@ "Location"` is optional, and represents the location of the thing. Auto-discovery would have assigned a value automatically. - -The `thermostatId` parameter is the unique ip Interface Object ID as automatically assigned in the Niko Home Control Controller when programming the Niko Home Control system using the Niko Home Control programming software. -It is not directly visible in the Niko Home Control programming or user software, but will be detected and automatically set by openHAB discovery. -For textual configuration, it can be retrieved from the .nhcp configuration file. - -The `overruleTime` parameter is used to set the standard overrule duration when you set a new setpoint without providing an overrule duration. The default value is 60 minutes. - -## Channels - -For thing type `onOff` the supported channel is `switch`. -OnOff command types are supported. - -For thing type `dimmer` the supported channel is `brightness`. -OnOff, IncreaseDecrease and Percent command types are supported. -Note that sending an ON command will switch the dimmer to the value stored when last turning the dimmer off, or 100% depending on the configuration in the Niko Home Control Controller. -This can be changed with the Niko Home Control programming software. - -For thing type `blind` the supported channel is `rollershutter`. UpDown, StopMove and Percent command types are supported. - -For thing type `thermostat` the supported channels are `measured`, `mode`, `setpoint` and `overruletime`. -`measured` gives the current temperature in QuantityType, allowing for different temperature units. This channel is read only. -`mode` can be set and shows the current thermostat mode. Allowed values are 0 (day), 1 (night), 2 (eco), 3 (off), 4 (coll), 5 (prog 1), 6 (prog 2), 7 (prog 3). If mode is set, the `setpoint` temperature will return to its standard value from the mode. -`setpoint` can be set and shows the current thermostat setpoint value in QuantityType. When updating `setpoint`, it will overrule the temperature setpoint defined by the thermostat mode for `overruletime` duration. -`overruletime` is used to set the total duration to apply the setpoint temperature set in the setpoint channel before the thermostat returns to the setting in its mode. - -The bridge has two trigger channels `alarm` and `notice`. -It can be used as a trigger to rules. The event message is the alarm or notice text coming from Niko Home Control. - -## Limitations - -The binding has been tested with a Niko Home Control IP-interface (550-00508) and the Niko Home Control Connected Controller (550-00003). - -The binding has been developed for and tested with Niko Home Control I. It does not work with Niko Home Control II, or with Niko Home Control I installations upgraded to Niko Home Control II. - -The action events implemented are limited to onOff, dimmer and rollershutter or blinds. -Other actions have not been implemented. -It is not possible to tilt the slats of venetian blinds. - -Beyond action and thermostat events, the Niko Home Control communication also supports electricity usage data. -This has not been implemented. - -## Example - -.things: - -``` -Bridge nikohomecontrol:bridge:nhc1 [ addr="192.168.0.70", port=8000, refresh=300 ] { - onOff 1 "LivingRoom" @ "Downstairs" [ actionId=1 ] - dimmer 2 "TVRoom" [ actionId=2, step=5 ] - blind 3 [ actionId=3 ] - thermostat 4 [ thermostatId=0 ] -} - -Bridge nikohomecontrol:bridge:nhc2 [ addr="192.168.0.110" ] { - onOff 11 @ "Upstairs"[ actionId=11 ] - dimmer 12 [ actionId=12, step=5 ] - blind 13 [ actionId=13 ] - thermostat 14 [ thermostatId=10 ] -} -``` - -.items: - -``` -Switch LivingRoom {channel="nikohomecontrol:onOff:nhc1:1:switch"} # Switch for onOff type action -Dimmer TVRoom {channel="nikohomecontrol:dimmer:nhc1:2:brightness"} # Changing brightness dimmer type action -Rollershutter Kitchen {channel="nikohomecontrol:blind:nhc1:3:rollershutter"} # Controlling rollershutter or blind type action -Number:Temperature CurTemperature "[%.1f °F]" {channel="nikohomecontrol:thermostat:nhc1:4:measured"} # Getting measured temperature from thermostat in °F, read only -Number ThermostatMode {channel="nikohomecontrol:thermostat:nhc1:4:mode"} # Get and set thermostat mode -Number:Temperature SetTemperature "[%.1f °C]" {channel="nikohomecontrol:thermostat:nhc1:4:setpoint"} # Get and set target temperature in °C -Number OverruleDuration {channel="nikohomecontrol:thermostat:nhc1:4:overruletime} # Get and set the overrule time -``` - -.sitemap: - -``` -Switch item=LivingRoom -Slider item=TVRoom -Switch item=TVRoom # allows switching dimmer item off or on (with controller defined behavior) -Rollershutter item=Kitchen -Text item=CurTemperature -Selection item=ThermostatMode mappings="[0="day", 1="night", 2="eco", 3="off", 4="cool", 5="prog 1", 6="prog 2", 7="prog 3"] -Setpoint item=SetTemperature minValue=0 maxValue=30 -Slider item=OverruleDuration minValue=0 maxValue=120 -``` - -Example trigger rule: - -``` -rule "example trigger rule" -when - Channel 'nikohomecontrol:bridge:nhc1:alarm' triggered or - Channel 'nikohomecontrol:bridge:nhc1:notice' triggered -then - var message = receivedEvent.getEvent() - logInfo("nhcTriggerExample", "Message: {}", message) - ... -end -``` diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/build.properties b/addons/binding/org.openhab.binding.nikohomecontrol/build.properties deleted file mode 100644 index 0f6636e9de4f6..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/pom.xml b/addons/binding/org.openhab.binding.nikohomecontrol/pom.xml deleted file mode 100644 index 41e0c53cf5dc9..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.nikohomecontrol - eclipse-plugin - - Niko Home Control Binding - - diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/NikoHomeControlBindingConstants.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/NikoHomeControlBindingConstants.java deleted file mode 100644 index 08597f8d33398..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/NikoHomeControlBindingConstants.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal; - -import java.util.Collections; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.thing.ThingTypeUID; - -/** - * The {@link NikoHomeControlBindingConstants} class defines common constants, which are - * used across the whole binding. - * - * @author Mark Herwege - Initial Contribution - */ -@NonNullByDefault -public class NikoHomeControlBindingConstants { - - public static final String BINDING_ID = "nikohomecontrol"; - - // List of all Thing Type UIDs - - // bridge - public static final ThingTypeUID BRIDGE_THING_TYPE = new ThingTypeUID(BINDING_ID, "bridge"); - - // generic thing types - public static final ThingTypeUID THING_TYPE_ON_OFF_LIGHT = new ThingTypeUID(BINDING_ID, "onOff"); - public static final ThingTypeUID THING_TYPE_DIMMABLE_LIGHT = new ThingTypeUID(BINDING_ID, "dimmer"); - public static final ThingTypeUID THING_TYPE_BLIND = new ThingTypeUID(BINDING_ID, "blind"); - public static final ThingTypeUID THING_TYPE_THERMOSTAT = new ThingTypeUID(BINDING_ID, "thermostat"); - - // thing type sets - public static final Set BRIDGE_THING_TYPES_UIDS = Collections.singleton(BRIDGE_THING_TYPE); - public static final Set ACTION_THING_TYPES_UIDS = Collections.unmodifiableSet(Stream - .of(THING_TYPE_ON_OFF_LIGHT, THING_TYPE_DIMMABLE_LIGHT, THING_TYPE_BLIND).collect(Collectors.toSet())); - public static final Set SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet( - Stream.of(THING_TYPE_ON_OFF_LIGHT, THING_TYPE_DIMMABLE_LIGHT, THING_TYPE_BLIND, THING_TYPE_THERMOSTAT) - .collect(Collectors.toSet())); - - // List of all Channel ids - public static final String CHANNEL_SWITCH = "switch"; - public static final String CHANNEL_BRIGHTNESS = "brightness"; - public static final String CHANNEL_ROLLERSHUTTER = "rollershutter"; - - public static final String CHANNEL_MEASURED = "measured"; - public static final String CHANNEL_SETPOINT = "setpoint"; - public static final String CHANNEL_OVERRULETIME = "overruletime"; - public static final String CHANNEL_MODE = "mode"; - - public static final String CHANNEL_ALARM = "alarm"; - public static final String CHANNEL_NOTICE = "notice"; - - // Bridge config properties - public static final String CONFIG_HOST_NAME = "addr"; - public static final String CONFIG_PORT = "port"; - public static final String CONFIG_REFRESH = "refresh"; - - // Thing config properties - public static final String CONFIG_ACTION_ID = "actionId"; - public static final String CONFIG_STEP_VALUE = "step"; - - public static final String CONFIG_THERMOSTAT_ID = "thermostatId"; - public static final String CONFIG_OVERRULETIME = "overruleTime"; -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/NikoHomeControlHandlerFactory.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/NikoHomeControlHandlerFactory.java deleted file mode 100644 index 16aba76952bbc..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/NikoHomeControlHandlerFactory.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal; - -import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*; - -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Map; - -import org.eclipse.smarthome.config.discovery.DiscoveryService; -import org.eclipse.smarthome.core.thing.Bridge; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.ThingUID; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; -import org.eclipse.smarthome.core.thing.binding.ThingHandler; -import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; -import org.openhab.binding.nikohomecontrol.internal.discovery.NikoHomeControlDiscoveryService; -import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlActionHandler; -import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler; -import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlThermostatHandler; -import org.osgi.framework.ServiceRegistration; -import org.osgi.service.component.annotations.Component; - -/** - * The {@link NikoHomeControlHandlerFactory} is responsible for creating things and thing - * handlers. - * - * @author Mark Herwege - Initial Contribution - */ - -@Component(service = ThingHandlerFactory.class, configurationPid = "binding.nikohomecontrol") -public class NikoHomeControlHandlerFactory extends BaseThingHandlerFactory { - - private Map> discoveryServiceRegs = new HashMap<>(); - - @Override - public boolean supportsThingType(ThingTypeUID thingTypeUID) { - return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID) || BRIDGE_THING_TYPES_UIDS.contains(thingTypeUID); - } - - @Override - protected ThingHandler createHandler(Thing thing) { - if (BRIDGE_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) { - NikoHomeControlBridgeHandler handler = new NikoHomeControlBridgeHandler((Bridge) thing); - registerNikoHomeControlDiscoveryService(handler); - return handler; - } else if (THING_TYPE_THERMOSTAT.equals(thing.getThingTypeUID())) { - return new NikoHomeControlThermostatHandler(thing); - } else if (ACTION_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) { - return new NikoHomeControlActionHandler(thing); - } - - return null; - } - - private synchronized void registerNikoHomeControlDiscoveryService(NikoHomeControlBridgeHandler bridgeHandler) { - NikoHomeControlDiscoveryService nhcDiscoveryService = new NikoHomeControlDiscoveryService(bridgeHandler); - this.discoveryServiceRegs.put(bridgeHandler.getThing().getUID(), bundleContext.registerService( - DiscoveryService.class.getName(), nhcDiscoveryService, new Hashtable())); - nhcDiscoveryService.activate(); - } - - @Override - protected synchronized void removeHandler(ThingHandler thingHandler) { - if (thingHandler instanceof NikoHomeControlBridgeHandler) { - ServiceRegistration serviceReg = this.discoveryServiceRegs.remove(thingHandler.getThing().getUID()); - if (serviceReg != null) { - // remove discovery service, if bridge handler is removed - NikoHomeControlDiscoveryService service = (NikoHomeControlDiscoveryService) bundleContext - .getService(serviceReg.getReference()); - serviceReg.unregister(); - if (service != null) { - service.deactivate(); - } - } - } - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlDiscoveryService.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlDiscoveryService.java deleted file mode 100644 index a9fb2588e8d58..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlDiscoveryService.java +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.discovery; - -import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*; - -import java.util.Date; -import java.util.Map; - -import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; -import org.eclipse.smarthome.config.discovery.DiscoveryResult; -import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; -import org.eclipse.smarthome.core.thing.ThingUID; -import org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants; -import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler; -import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction; -import org.openhab.binding.nikohomecontrol.internal.protocol.NhcThermostat; -import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * If a Niko Home Control bridge is added or if the user scans manually for things this - * {@link NikoHomeControlDiscoveryService} - * is used to return Niko Home Control Actions as things to the framework. - * - * @author Mark Herwege - Initial Contribution - */ -public class NikoHomeControlDiscoveryService extends AbstractDiscoveryService { - - private final Logger logger = LoggerFactory.getLogger(NikoHomeControlDiscoveryService.class); - - private static final int TIMEOUT = 5; - - private ThingUID bridgeUID; - private NikoHomeControlBridgeHandler handler; - - public NikoHomeControlDiscoveryService(NikoHomeControlBridgeHandler handler) { - super(NikoHomeControlBindingConstants.SUPPORTED_THING_TYPES_UIDS, TIMEOUT, false); - logger.debug("Niko Home Control: discovery service {}", handler); - this.bridgeUID = handler.getThing().getUID(); - this.handler = handler; - } - - public void activate() { - this.handler.setNhcDiscovery(this); - } - - @Override - public void deactivate() { - removeOlderResults(new Date().getTime()); - this.handler.setNhcDiscovery(null); - this.handler = null; - } - - /** - * Discovers devices connected to a Niko Home Control controller - */ - public void discoverDevices() { - NikoHomeControlCommunication nhcComm = this.handler.getCommunication(); - - if ((nhcComm == null) || !nhcComm.communicationActive()) { - logger.warn("Niko Home Control: not connected."); - return; - } - logger.debug("Niko Home Control: getting devices on {}", this.handler.getThing().getUID().getId()); - - Map actions = nhcComm.getActions(); - - for (Map.Entry action : actions.entrySet()) { - - int actionId = action.getKey(); - NhcAction nhcAction = action.getValue(); - String thingName = nhcAction.getName(); - String thingLocation = nhcAction.getLocation(); - - switch (nhcAction.getType()) { - case 0: // handles all-off - case 1: // switch - addActionDevice(new ThingUID(THING_TYPE_ON_OFF_LIGHT, this.handler.getThing().getUID(), - Integer.toString(actionId)), actionId, thingName, thingLocation); - - break; - case 2: // dimmer - addActionDevice(new ThingUID(THING_TYPE_DIMMABLE_LIGHT, this.handler.getThing().getUID(), - Integer.toString(actionId)), actionId, thingName, thingLocation); - break; - case 4: // rollershutter - case 5: - addActionDevice(new ThingUID(THING_TYPE_BLIND, this.handler.getThing().getUID(), - Integer.toString(actionId)), actionId, thingName, thingLocation); - break; - default: - logger.debug("Niko Home Control: unrecognized action type {} for {} {}", nhcAction.getType(), - actionId, thingName); - } - } - - Map thermostats = nhcComm.getThermostats(); - - for (Map.Entry thermostatEntry : thermostats.entrySet()) { - - int thermostatId = thermostatEntry.getKey(); - NhcThermostat nhcThermostat = thermostatEntry.getValue(); - String thingName = nhcThermostat.getName(); - String thingLocation = nhcThermostat.getLocation(); - addThermostatDevice(new ThingUID(THING_TYPE_THERMOSTAT, this.handler.getThing().getUID(), - Integer.toString(thermostatId)), thermostatId, thingName, thingLocation); - } - } - - private void addActionDevice(ThingUID uid, int actionId, String thingName, String thingLocation) { - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID).withLabel(thingName) - .withProperty("Location", thingLocation).withProperty(CONFIG_ACTION_ID, actionId).build(); - thingDiscovered(discoveryResult); - } - - private void addThermostatDevice(ThingUID uid, int thermostatId, String thingName, String thingLocation) { - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID).withLabel(thingName) - .withProperty("Location", thingLocation).withProperty(CONFIG_THERMOSTAT_ID, thermostatId).build(); - thingDiscovered(discoveryResult); - } - - @Override - protected void startScan() { - discoverDevices(); - } - - @Override - protected synchronized void stopScan() { - super.stopScan(); - removeOlderResults(getTimestampOfLastScan()); - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionHandler.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionHandler.java deleted file mode 100644 index 11682feff9c84..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionHandler.java +++ /dev/null @@ -1,514 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.handler; - -import static org.eclipse.smarthome.core.types.RefreshType.REFRESH; -import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.config.core.Configuration; -import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; -import org.eclipse.smarthome.core.library.types.OnOffType; -import org.eclipse.smarthome.core.library.types.PercentType; -import org.eclipse.smarthome.core.library.types.StopMoveType; -import org.eclipse.smarthome.core.library.types.UpDownType; -import org.eclipse.smarthome.core.thing.Bridge; -import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingStatus; -import org.eclipse.smarthome.core.thing.ThingStatusDetail; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; -import org.eclipse.smarthome.core.types.Command; -import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction; -import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@link NikoHomeControlActionHandler} is responsible for handling commands, which are - * sent to one of the channels. - * - * @author Mark Herwege - Initial Contribution - */ -@NonNullByDefault -public class NikoHomeControlActionHandler extends BaseThingHandler { - - private final Logger logger = LoggerFactory.getLogger(NikoHomeControlActionHandler.class); - - // dimmer constants - static final int NHCON = 254; - static final int NHCOFF = 255; - - // rollershutter constants - static final int NHCDOWN = 254; - static final int NHCUP = 255; - static final int NHCSTOP = 253; - - @FunctionalInterface - private interface Action { - void execute(); - } - - @Nullable - private volatile Action rollershutterTask; - @Nullable - private volatile ScheduledFuture rollershutterStopTask; - @Nullable - private volatile ScheduledFuture rollershutterMovingFlagTask; - - private volatile boolean filterEvent; // flag to filter first event from rollershutter on percent move to - // avoid wrong position update - private volatile boolean rollershutterMoving; // flag to indicate if rollershutter is currently moving - private volatile boolean waitForEvent; // flag to wait for position update rollershutter before doing next - // move - - private volatile int prevActionState; - - public NikoHomeControlActionHandler(Thing thing) { - super(thing); - } - - @Override - public void handleCommand(ChannelUID channelUID, Command command) { - Integer actionId = ((Number) this.getConfig().get(CONFIG_ACTION_ID)).intValue(); - - Bridge nhcBridge = getBridge(); - if (nhcBridge == null) { - updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, - "Niko Home Control: no bridge initialized when trying to execute action " + actionId); - return; - } - NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler(); - if (nhcBridgeHandler == null) { - updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, - "Niko Home Control: no bridge initialized when trying to execute action " + actionId); - return; - } - NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication(); - - if (nhcComm == null) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "Niko Home Control: bridge communication not initialized when trying to execute action " - + actionId); - return; - } - - NhcAction nhcAction = nhcComm.getActions().get(actionId); - if (nhcAction == null) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "Niko Home Control: actionId " + actionId + " does not match an action in the controller"); - return; - } - - if (nhcComm.communicationActive()) { - handleCommandSelection(nhcAction, channelUID, command); - } else { - // We lost connection but the connection object is there, so was correctly started. - // Try to restart communication. - // This can be expensive, therefore do it in a job. - scheduler.submit(() -> { - nhcComm.restartCommunication(); - // If still not active, take thing offline and return. - if (!nhcComm.communicationActive()) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Niko Home Control: communication socket error"); - return; - } - // Also put the bridge back online - nhcBridgeHandler.bridgeOnline(); - - // And finally handle the command - handleCommandSelection(nhcAction, channelUID, command); - }); - } - } - - private void handleCommandSelection(NhcAction nhcAction, ChannelUID channelUID, Command command) { - logger.debug("Niko Home Control: handle command {} for {}", command, channelUID); - - if (command == REFRESH) { - handleStateUpdate(nhcAction); - return; - } - - switch (channelUID.getId()) { - case CHANNEL_SWITCH: - handleSwitchCommand(nhcAction, command); - updateStatus(ThingStatus.ONLINE); - break; - - case CHANNEL_BRIGHTNESS: - handleBrightnessCommand(nhcAction, command); - updateStatus(ThingStatus.ONLINE); - break; - - case CHANNEL_ROLLERSHUTTER: - handleRollershutterCommand(nhcAction, command); - updateStatus(ThingStatus.ONLINE); - break; - - default: - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Niko Home Control: channel unknown " + channelUID.getId()); - } - } - - private void handleSwitchCommand(NhcAction nhcAction, Command command) { - if (command instanceof OnOffType) { - OnOffType s = (OnOffType) command; - if (s == OnOffType.OFF) { - nhcAction.execute(0); - } else { - nhcAction.execute(100); - } - } - } - - private void handleBrightnessCommand(NhcAction nhcAction, Command command) { - if (command instanceof OnOffType) { - OnOffType s = (OnOffType) command; - if (s == OnOffType.OFF) { - nhcAction.execute(NHCOFF); - } else { - nhcAction.execute(NHCON); - } - } else if (command instanceof IncreaseDecreaseType) { - IncreaseDecreaseType s = (IncreaseDecreaseType) command; - int stepValue = ((Number) this.getConfig().get(CONFIG_STEP_VALUE)).intValue(); - int currentValue = nhcAction.getState(); - int newValue; - if (s == IncreaseDecreaseType.INCREASE) { - newValue = currentValue + stepValue; - // round down to step multiple - newValue = newValue - newValue % stepValue; - nhcAction.execute(newValue > 100 ? 100 : newValue); - } else { - newValue = currentValue - stepValue; - // round up to step multiple - newValue = newValue + newValue % stepValue; - nhcAction.execute(newValue < 0 ? 0 : newValue); - } - } else if (command instanceof PercentType) { - PercentType p = (PercentType) command; - if (p == PercentType.ZERO) { - nhcAction.execute(NHCOFF); - } else { - nhcAction.execute(p.intValue()); - } - } - } - - private void handleRollershutterCommand(NhcAction nhcAction, Command command) { - Configuration config = this.getConfig(); - if (logger.isTraceEnabled()) { - String actionId = (String) config.get(CONFIG_ACTION_ID); - logger.trace("handleRollerShutterCommand: rollershutter {} command {}", actionId, command); - logger.trace("handleRollerShutterCommand: rollershutter {}, current position {}", actionId, - nhcAction.getState()); - } - - // first stop all current movement of rollershutter and wait until exact position is known - if (this.rollershutterMoving) { - if (logger.isTraceEnabled()) { - logger.trace("handleRollerShutterCommand: rollershutter {} moving, therefore stop", - config.get(CONFIG_ACTION_ID)); - } - rollershutterPositionStop(nhcAction); - } - - // task to be executed once exact position received from Niko Home Control - this.rollershutterTask = () -> { - if (logger.isTraceEnabled()) { - logger.trace("handleRollerShutterCommand: rollershutter {} task running", - this.getConfig().get(CONFIG_ACTION_ID)); - } - - int currentValue = nhcAction.getState(); - - if (command instanceof UpDownType) { - UpDownType s = (UpDownType) command; - if (s == UpDownType.UP) { - nhcAction.execute(NHCUP); - } else { - nhcAction.execute(NHCDOWN); - } - } else if (command instanceof StopMoveType) { - nhcAction.execute(NHCSTOP); - } else if (command instanceof PercentType) { - int newValue = 100 - ((PercentType) command).intValue(); - if (logger.isTraceEnabled()) { - logger.trace("handleRollerShutterCommand: rollershutter {} percent command, current {}, new {}", - config.get(CONFIG_ACTION_ID), currentValue, newValue); - } - if (currentValue == newValue) { - return; - } - if ((newValue > 0) && (newValue < 100)) { - scheduleRollershutterStop(nhcAction, currentValue, newValue); - } - if (newValue < currentValue) { - nhcAction.execute(NHCDOWN); - } else if (newValue > currentValue) { - nhcAction.execute(NHCUP); - } - } - }; - - // execute immediately if not waiting for exact position - if (!this.waitForEvent) { - if (logger.isTraceEnabled()) { - logger.trace("handleRollerShutterCommand: rollershutter {} task executing immediately", - this.getConfig().get(CONFIG_ACTION_ID)); - } - executeRollershutterTask(); - } - } - - /** - * Method used to stop rollershutter when moving. This will then result in an exact position to be received, so next - * percentage movements could be done accurately. - * - * @param nhcAction Niko Home Control action - * - */ - private void rollershutterPositionStop(NhcAction nhcAction) { - if (logger.isTraceEnabled()) { - logger.trace("rollershutterPositionStop: rollershutter {} executing", - this.getConfig().get(CONFIG_ACTION_ID)); - } - cancelRollershutterStop(); - this.rollershutterTask = null; - this.filterEvent = false; - this.waitForEvent = true; - nhcAction.execute(NHCSTOP); - } - - private void executeRollershutterTask() { - if (logger.isTraceEnabled()) { - logger.trace("executeRollershutterTask: rollershutter {} task triggered", - this.getConfig().get(CONFIG_ACTION_ID)); - } - this.waitForEvent = false; - - Action action = this.rollershutterTask; - if (action != null) { - action.execute(); - this.rollershutterTask = null; - } - } - - /** - * Method used to schedule a rollershutter stop when moving. This allows stopping the rollershutter at a percent - * position. - * - * @param nhcAction Niko Home Control action - * @param currentValue current percent position - * @param newValue new percent position - * - */ - private void scheduleRollershutterStop(NhcAction nhcAction, int currentValue, int newValue) { - // filter first event for a rollershutter coming from Niko Home Control if moving to an intermediate - // position to avoid updating state to full open or full close - this.filterEvent = true; - - long duration = rollershutterMoveTime(nhcAction, currentValue, newValue); - setRollershutterMovingTrue(nhcAction, duration); - - if (logger.isTraceEnabled()) { - logger.trace("scheduleRollershutterStop: schedule rollershutter {} stop in {}ms", - this.getConfig().get(CONFIG_ACTION_ID), duration); - } - this.rollershutterStopTask = scheduler.schedule(() -> { - logger.trace("scheduleRollershutterStop: run rollershutter {} stop", - this.getConfig().get(CONFIG_ACTION_ID)); - nhcAction.execute(NHCSTOP); - }, duration, TimeUnit.MILLISECONDS); - } - - private void cancelRollershutterStop() { - ScheduledFuture stopTask = this.rollershutterStopTask; - if (stopTask != null) { - if (logger.isTraceEnabled()) { - logger.trace("cancelRollershutterStop: cancel rollershutter {} stop", - this.getConfig().get(CONFIG_ACTION_ID)); - } - stopTask.cancel(true); - } - this.rollershutterStopTask = null; - - this.filterEvent = false; - } - - private void setRollershutterMovingTrue(NhcAction nhcAction, long duration) { - if (logger.isTraceEnabled()) { - logger.trace("setRollershutterMovingTrue: rollershutter {} moving", this.getConfig().get(CONFIG_ACTION_ID)); - } - this.rollershutterMoving = true; - this.rollershutterMovingFlagTask = scheduler.schedule(() -> { - if (logger.isTraceEnabled()) { - logger.trace("setRollershutterMovingTrue: rollershutter {} stopped moving", - this.getConfig().get(CONFIG_ACTION_ID)); - } - this.rollershutterMoving = false; - }, duration, TimeUnit.MILLISECONDS); - } - - private void setRollershutterMovingFalse() { - if (logger.isTraceEnabled()) { - logger.trace("setRollershutterMovingFalse: rollershutter {} not moving", - this.getConfig().get(CONFIG_ACTION_ID)); - } - this.rollershutterMoving = false; - ScheduledFuture future = this.rollershutterMovingFlagTask; - if (future != null) { - future.cancel(true); - this.rollershutterMovingFlagTask = null; - } - } - - private long rollershutterMoveTime(NhcAction nhcAction, int currentValue, int newValue) { - int totalTime = (newValue > currentValue) ? nhcAction.getCloseTime() : nhcAction.getOpenTime(); - long duration = Math.abs(newValue - currentValue) * totalTime * 10; - if (logger.isTraceEnabled()) { - logger.trace("rollershutterMoveTime: rollershutter {} move time {}", this.getConfig().get(CONFIG_ACTION_ID), - duration); - } - return duration; - } - - @Override - public void initialize() { - Configuration config = this.getConfig(); - - Integer actionId = ((Number) config.get(CONFIG_ACTION_ID)).intValue(); - - Bridge nhcBridge = getBridge(); - if (nhcBridge == null) { - updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, - "Niko Home Control: no bridge initialized for action " + actionId); - return; - } - NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler(); - if (nhcBridgeHandler == null) { - updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, - "Niko Home Control: no bridge initialized for action " + actionId); - return; - } - NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication(); - if (nhcComm == null || !nhcComm.communicationActive()) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "Niko Home Control: no connection with Niko Home Control, could not initialize action " + actionId); - return; - } - - NhcAction nhcAction = nhcComm.getActions().get(actionId); - if (nhcAction == null) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, - "Niko Home Control: actionId does not match an action in the controller " + actionId); - return; - } - - int actionState = nhcAction.getState(); - int actionType = nhcAction.getType(); - String actionLocation = nhcAction.getLocation(); - - this.prevActionState = actionState; - nhcAction.setThingHandler(this); - - Map properties = new HashMap<>(); - properties.put("type", String.valueOf(actionType)); - if (this.getThing().getThingTypeUID() == THING_TYPE_BLIND) { - cancelRollershutterStop(); - this.waitForEvent = false; - setRollershutterMovingFalse(); - - properties.put("timeToOpen", String.valueOf(nhcAction.getOpenTime())); - properties.put("timeToClose", String.valueOf(nhcAction.getCloseTime())); - } - thing.setProperties(properties); - - if (thing.getLocation() == null) { - thing.setLocation(actionLocation); - } - - handleStateUpdate(nhcAction); - - logger.debug("Niko Home Control: action intialized {}", actionId); - } - - /** - * Method to update state of channel, called from Niko Home Control action. - * - * @param nhcAction Niko Home Control action - * - */ - public void handleStateUpdate(NhcAction nhcAction) { - Configuration config = this.getConfig(); - Integer actionId = ((Number) config.get(CONFIG_ACTION_ID)).intValue(); - - int actionType = nhcAction.getType(); - int actionState = nhcAction.getState(); - - if (this.filterEvent) { - this.filterEvent = false; - logger.debug("Niko Home Control: filtered event {} for {}", actionState, actionId); - updateStatus(ThingStatus.ONLINE); - return; - } - - switch (actionType) { - case 0: - case 1: - updateState(CHANNEL_SWITCH, (actionState == 0) ? OnOffType.OFF : OnOffType.ON); - updateStatus(ThingStatus.ONLINE); - break; - case 2: - updateState(CHANNEL_BRIGHTNESS, new PercentType(actionState)); - updateStatus(ThingStatus.ONLINE); - break; - case 4: - case 5: - cancelRollershutterStop(); - - int state = 100 - actionState; - int prevState = 100 - this.prevActionState; - if (((state == 0) || (state == 100)) && (state != prevState)) { - long duration = rollershutterMoveTime(nhcAction, prevState, state); - setRollershutterMovingTrue(nhcAction, duration); - } else { - setRollershutterMovingFalse(); - } - - if (this.waitForEvent) { - logger.debug("Niko Home Control: received requested rollershutter {} position event {}", actionId, - actionState); - executeRollershutterTask(); - } else { - updateState(CHANNEL_ROLLERSHUTTER, new PercentType(state)); - updateStatus(ThingStatus.ONLINE); - } - break; - default: - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "Niko Home Control: unknown action type " + actionType); - } - - this.prevActionState = actionState; - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler.java deleted file mode 100644 index 3fee11905fd02..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler.java +++ /dev/null @@ -1,293 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.handler; - -import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -import org.eclipse.smarthome.config.core.Configuration; -import org.eclipse.smarthome.core.thing.Bridge; -import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.thing.ThingStatus; -import org.eclipse.smarthome.core.thing.ThingStatusDetail; -import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; -import org.eclipse.smarthome.core.types.Command; -import org.openhab.binding.nikohomecontrol.internal.discovery.NikoHomeControlDiscoveryService; -import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * {@link NikoHomeControlBridgeHandler} is the handler for a Niko Home Control IP-interface and connects it to - * the framework. - * - * @author Mark Herwege - Initial Contribution - */ -public class NikoHomeControlBridgeHandler extends BaseBridgeHandler { - - private final Logger logger = LoggerFactory.getLogger(NikoHomeControlBridgeHandler.class); - - private NikoHomeControlCommunication nhcComm; - - private ScheduledFuture refreshTimer; - - private NikoHomeControlDiscoveryService nhcDiscovery; - - public NikoHomeControlBridgeHandler(Bridge nikoHomeControlBridge) { - super(nikoHomeControlBridge); - } - - @Override - public void handleCommand(ChannelUID channelUID, Command command) { - // There is nothing to handle in the bridge handler - } - - @Override - public void initialize() { - logger.debug("Niko Home Control: initializing bridge handler"); - - Configuration config = this.getConfig(); - InetAddress addr = getAddr(); - int port = getPort(); - - logger.debug("Niko Home Control: bridge handler host {}, port {}", addr, port); - - if (addr != null) { - createCommunicationObject(addr, port); - } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, - "Niko Home Control: cannot resolve bridge IP with hostname " + config.get(CONFIG_HOST_NAME)); - } - } - - /** - * Create communication object to Niko Home Control IP-interface and start communication. - * Trigger discovery when communication setup is successful. - * - * @param addr IP address of Niko Home Control IP-interface - * @param port - */ - private void createCommunicationObject(InetAddress addr, int port) { - Configuration config = this.getConfig(); - - scheduler.submit(() -> { - nhcComm = new NikoHomeControlCommunication(); - - // Set callback from NikoHomeControlCommunication object to this bridge to be able to take bridge - // offline when non-resolvable communication error occurs. - setBridgeCallBack(); - - nhcComm.startCommunication(); - if (!nhcComm.communicationActive()) { - nhcComm = null; - bridgeOffline(); - return; - } - - updateProperties(); - - updateStatus(ThingStatus.ONLINE); - - Integer refreshInterval = ((Number) config.get(CONFIG_REFRESH)).intValue(); - setupRefreshTimer(refreshInterval); - - if (nhcDiscovery != null) { - nhcDiscovery.discoverDevices(); - } else { - logger.debug("Niko Home Control: cannot discover devices, discovery service not started"); - } - }); - } - - private void setBridgeCallBack() { - this.nhcComm.setBridgeCallBack(this); - } - - /** - * Schedule future communication refresh. - * - * @param interval_config Time before refresh in minutes. - */ - private void setupRefreshTimer(Integer refreshInterval) { - if (this.refreshTimer != null) { - this.refreshTimer.cancel(true); - this.refreshTimer = null; - } - - if ((refreshInterval == null) || (refreshInterval == 0)) { - return; - } - - // This timer will restart the bridge connection periodically - logger.debug("Niko Home Control: restart bridge connection every {} min", refreshInterval); - this.refreshTimer = scheduler.scheduleWithFixedDelay(() -> { - logger.debug("Niko Home Control: restart communication at scheduled time"); - - nhcComm.restartCommunication(); - if (!nhcComm.communicationActive()) { - logger.debug("Niko Home Control: communication socket error"); - bridgeOffline(); - return; - } - - updateProperties(); - - updateStatus(ThingStatus.ONLINE); - }, refreshInterval, refreshInterval, TimeUnit.MINUTES); - } - - /** - * Take bridge offline when error in communication with Niko Home Control IP-interface. This method can also be - * called directly from {@link NikoHomeControlCommunication} object. - */ - public void bridgeOffline() { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, - "Niko Home Control: error starting bridge connection"); - } - - /** - * Put bridge online when error in communication resolved. - */ - public void bridgeOnline() { - updateProperties(); - updateStatus(ThingStatus.ONLINE); - } - - /** - * Update bridge properties with properties returned from Niko Home Control Controller, so they can be made visible - * in PaperUI. - */ - private void updateProperties() { - Map properties = new HashMap<>(); - - properties.put("softwareVersion", this.nhcComm.getSystemInfo().getSwVersion()); - properties.put("apiVersion", this.nhcComm.getSystemInfo().getApi()); - properties.put("language", this.nhcComm.getSystemInfo().getLanguage()); - properties.put("currency", this.nhcComm.getSystemInfo().getCurrency()); - properties.put("units", this.nhcComm.getSystemInfo().getUnits()); - properties.put("tzOffset", this.nhcComm.getSystemInfo().getTz()); - properties.put("dstOffset", this.nhcComm.getSystemInfo().getDst()); - properties.put("configDate", this.nhcComm.getSystemInfo().getLastConfig()); - properties.put("energyEraseDate", this.nhcComm.getSystemInfo().getLastEnergyErase()); - properties.put("connectionStartDate", this.nhcComm.getSystemInfo().getTime()); - - thing.setProperties(properties); - } - - @Override - public void dispose() { - if (this.refreshTimer != null) { - this.refreshTimer.cancel(true); - } - this.refreshTimer = null; - if (this.nhcComm != null) { - nhcComm.stopCommunication(); - } - this.nhcComm = null; - } - - @Override - public void handleConfigurationUpdate(Map configurationParameters) { - Configuration configuration = editConfiguration(); - for (Entry configurationParmeter : configurationParameters.entrySet()) { - configuration.put(configurationParmeter.getKey(), configurationParmeter.getValue()); - } - updateConfiguration(configuration); - - scheduler.submit(() -> { - nhcComm.restartCommunication(); - if (!nhcComm.communicationActive()) { - bridgeOffline(); - return; - } - - updateProperties(); - - updateStatus(ThingStatus.ONLINE); - - Integer refreshInterval = ((Number) configuration.get(CONFIG_REFRESH)).intValue(); - setupRefreshTimer(refreshInterval); - }); - } - - /** - * Set discovery service handler to be able to start discovery after bridge initialization. - * - * @param nhcDiscovery - */ - public void setNhcDiscovery(NikoHomeControlDiscoveryService nhcDiscovery) { - this.nhcDiscovery = nhcDiscovery; - } - - /** - * Send a trigger from an alarm received from Niko Home Control. - * - * @param Niko Home Control alarm message - */ - public void triggerAlarm(String alarmText) { - triggerChannel(CHANNEL_ALARM, alarmText); - updateStatus(ThingStatus.ONLINE); - } - - /** - * Send a trigger from a notice received from Niko Home Control. - * - * @param Niko Home Control alarm message - */ - public void triggerNotice(String alarmText) { - triggerChannel(CHANNEL_NOTICE, alarmText); - updateStatus(ThingStatus.ONLINE); - } - - /** - * Get the Niko Home Control communication object. - * - * @return Niko Home Control communication object - */ - public NikoHomeControlCommunication getCommunication() { - return this.nhcComm; - } - - /** - * Get the IP-address of the Niko Home Control IP-interface. - * - * @return the addr - */ - public InetAddress getAddr() { - Configuration config = this.getConfig(); - InetAddress addr = null; - try { - addr = InetAddress.getByName((String) config.get(CONFIG_HOST_NAME)); - } catch (UnknownHostException e) { - logger.debug("Niko Home Control: Cannot resolve hostname {} to IP adress", config.get(CONFIG_HOST_NAME)); - } - return addr; - } - - /** - * Get the listening port of the Niko Home Control IP-interface. - * - * @return the port - */ - public int getPort() { - Configuration config = this.getConfig(); - return ((Number) config.get(CONFIG_PORT)).intValue(); - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcAction.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcAction.java deleted file mode 100644 index f5c9b7182ecf9..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcAction.java +++ /dev/null @@ -1,183 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.protocol; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlActionHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@link NhcAction} class represents the action Niko Home Control communication object. It contains all fields - * representing a Niko Home Control action and has methods to trigger the action in Niko Home Control and receive action - * updates. - * - * @author Mark Herwege - Initial Contribution - */ -@NonNullByDefault -public final class NhcAction { - - private final Logger logger = LoggerFactory.getLogger(NhcAction.class); - - @Nullable - private NikoHomeControlCommunication nhcComm; - - private int id; - private String name; - private Integer type; - private String location; - private Integer state = 0; - @Nullable - private Integer closeTime; - @Nullable - private Integer openTime; - - @Nullable - private NikoHomeControlActionHandler thingHandler; - - NhcAction(int id, String name, Integer type, String location, @Nullable Integer closeTime, - @Nullable Integer openTime) { - this.id = id; - this.name = name; - this.type = type; - this.location = location; - this.closeTime = closeTime; - this.openTime = openTime; - } - - /** - * This method should be called if the ThingHandler for the thing corresponding to this action is initialized. - * It keeps a record of the thing handler in this object so the thing can be updated when - * the action receives an update from the Niko Home Control IP-interface. - * - * @param handler - */ - public void setThingHandler(NikoHomeControlActionHandler handler) { - this.thingHandler = handler; - } - - /** - * This method sets a pointer to the nhcComm object of class {@link NikoHomeControlCommuncation}. - * This is then used to be able to call back the sendCommand method in this class to send a command to the - * Niko Home Control IP-interface when.. - * - * @param nhcComm - */ - public void setNhcComm(NikoHomeControlCommunication nhcComm) { - this.nhcComm = nhcComm; - } - - /** - * Get name of action. - * - * @return action name - */ - public String getName() { - return this.name; - } - - /** - * Get type of action identified. - *

- * Action type is 0 or 1 for a switch, 2 for a dimmer, 3 or 4 for a rollershutter. - * - * @return action type - */ - public Integer getType() { - return this.type; - } - - /** - * Get location name of action. - * - * @return location name - */ - public String getLocation() { - return this.location; - } - - /** - * Get state of action. - *

- * State is a value between 0 and 100 for a dimmer or rollershutter. - * Rollershutter state is 0 for fully closed and 100 for fully open. - * State is 0 or 100 for a switch. - * - * @return action state - */ - public Integer getState() { - return this.state; - } - - /** - * Get openTime of action. - *

- * openTime is the time in seconds to fully open a rollershutter. - * - * @return action openTime - */ - public @Nullable Integer getOpenTime() { - return this.openTime; - } - - /** - * Get closeTime of action. - *

- * closeTime is the time in seconds to fully close a rollershutter. - * - * @return action closeTime - */ - public @Nullable Integer getCloseTime() { - return this.closeTime; - } - - /** - * Sets state of action. - *

- * State is a value between 0 and 100 for a dimmer or rollershutter. - * Rollershutter state is 0 for fully closed and 100 for fully open. - * State is 0 or 100 for a switch. - * If a thing handler is registered for the action, send a state update through the handler. - * This method should only be called from inside this package. - * - * @param state - */ - void setState(int state) { - this.state = state; - NikoHomeControlActionHandler handler = thingHandler; - if (handler != null) { - logger.debug("Niko Home Control: update channel state for {} with {}", id, state); - handler.handleStateUpdate(this); - } - } - - /** - * Sends action to Niko Home Control. - * - * @param percent - The allowed values depend on the action type. - * switch action: 0 or 100 - * dimmer action: between 0 and 100, 254 for on, 255 for off - * rollershutter action: 254 to close, 255 to open, 253 to stop - */ - public void execute(int percent) { - logger.debug("Niko Home Control: execute action {} of type {} for {}", percent, this.type, this.id); - - NhcMessageCmd nhcCmd = new NhcMessageCmd("executeactions", this.id, percent); - - NikoHomeControlCommunication comm = nhcComm; - if (comm != null) { - comm.sendMessage(nhcCmd); - } - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcLocation.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcLocation.java deleted file mode 100644 index c7259aba386c9..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcLocation.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.protocol; - -/** - * The {@link NhcLocation} class represents the location Niko Home Control communication object. It contains all fields - * representing a Niko Home Control location. - * - * @author Mark Herwege - Initial Contribution - */ -final class NhcLocation { - - private String name; - - NhcLocation(String name) { - this.name = name; - } - - String getName() { - return this.name; - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcMessageBase.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcMessageBase.java deleted file mode 100644 index a99997c5216c0..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcMessageBase.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.protocol; - -/** - * Class {@link NhcMessageBase} used as base class for output from gson for cmd or event feedback from Niko Home - * Control. This class only contains the common base fields required for the deserializer - * {@link NikoHomeControlMessageDeserializer} to select the specific formats implemented in {@link NhcMessageMap}, - * {@link NhcMessageListMap}, {@link NhcMessageCmd}. - *

- * - * @author Mark Herwege - Initial Contribution - */ -abstract class NhcMessageBase { - - private String cmd; - private String event; - - String getCmd() { - return this.cmd; - } - - void setCmd(String cmd) { - this.cmd = cmd; - } - - String getEvent() { - return this.event; - } - - void setEvent(String event) { - this.event = event; - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcMessageCmd.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcMessageCmd.java deleted file mode 100644 index b37a909506b37..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcMessageCmd.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.protocol; - -/** - * Class {@link NhcMessageCmd} used as input to gson to send commands to Niko Home Control. Extends - * {@link NhcMessageBase}. - *

- * Example: {"cmd":"executeactions","id":1,"value1":0} - * - * @author Mark Herwege - Initial Contribution - */ -@SuppressWarnings("unused") -class NhcMessageCmd extends NhcMessageBase { - - private Integer id; - private Integer value1; - private Integer value2; - private Integer value3; - private Integer mode; - private Integer overrule; - private String overruletime; - - NhcMessageCmd(String cmd) { - super.setCmd(cmd); - } - - NhcMessageCmd(String cmd, Integer id) { - this(cmd); - this.id = id; - } - - NhcMessageCmd(String cmd, Integer id, Integer value1) { - this(cmd, id); - this.value1 = value1; - } - - NhcMessageCmd(String cmd, Integer id, Integer value1, Integer value2, Integer value3) { - this(cmd, id, value1); - this.value2 = value2; - this.value3 = value3; - } - - NhcMessageCmd withMode(Integer mode) { - this.mode = mode; - return this; - } - - NhcMessageCmd withOverrule(Integer overrule) { - this.overrule = overrule; - return this; - } - - NhcMessageCmd withOverruletime(String overruletime) { - this.overruletime = overruletime; - return this; - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcMessageListMap.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcMessageListMap.java deleted file mode 100644 index f6b1279ca2956..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcMessageListMap.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.protocol; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * Class {@link NhcMessageListMap} used as output from gson for cmd or event feedback from Niko Home Control where the - * data part is enclosed by [] and contains a list of json strings. Extends {@link NhcMessageBase}. - *

- * Example: {"cmd":"listactions","data":[{"id":1,"name":"Garage","type":1,"location":1,"value1":0}, - * {"id":25,"name":"Frontdoor","type":2,"location":2,"value1":0}]} - * - * @author Mark Herwege - Initial Contribution - */ -class NhcMessageListMap extends NhcMessageBase { - - private List> data = new ArrayList<>(); - - List> getData() { - return this.data; - } - - void setData(List> data) { - this.data = data; - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcMessageMap.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcMessageMap.java deleted file mode 100644 index 70102ec433465..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcMessageMap.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.protocol; - -import java.util.HashMap; -import java.util.Map; - -/** - * Class {@link NhcMessageMap} used as output from gson for cmd or event feedback from Niko Home Control where the - * data part is a simple json string. Extends {@link NhcMessageBase}. - *

- * Example: {"cmd":"executeactions", "data":{"error":0}} - * - * @author Mark Herwege - Initial Contribution - */ -class NhcMessageMap extends NhcMessageBase { - - private Map data = new HashMap<>(); - - Map getData() { - return this.data; - } - - void setData(Map data) { - this.data = data; - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcSystemInfo.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcSystemInfo.java deleted file mode 100644 index 7afb6f1657884..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcSystemInfo.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.protocol; - -/** - * The {@link NhcSystemInfo} class represents the systeminfo Niko Home Control communication object. It contains all - * Niko Home Control system data received from the Niko Home Control controller when initializing the connection. - * - * @author Mark Herwege - Initial Contribution - */ -public final class NhcSystemInfo { - - // Initialize with empty strings. If null, downstream methods may throw null pointer exceptions. These - // exceptions cause threads to stop without warning, no way to catch the exception. This behavior happened when - // trying to write null to properties of things. So this avoids having to check before the call for null - // pointers each time as the null exceptions cannot be caught. - private String swVersion = ""; - private String api = ""; - private String time = ""; - private String language = ""; - private String currency = ""; - private String units = ""; - private String dst = ""; - private String tz = ""; - private String lastEnergyErase = ""; - private String lastConfig = ""; - - public String getSwVersion() { - return swVersion; - } - - void setSwVersion(String swVersion) { - this.swVersion = swVersion; - } - - public String getApi() { - return api; - } - - void setApi(String api) { - this.api = api; - } - - public String getTime() { - return time; - } - - void setTime(String time) { - this.time = time; - } - - public String getLanguage() { - return language; - } - - void setLanguage(String language) { - this.language = language; - } - - public String getCurrency() { - return currency; - } - - void setCurrency(String currency) { - this.currency = currency; - } - - public String getUnits() { - return units; - } - - void setUnits(String units) { - this.units = units; - } - - public String getDst() { - return dst; - } - - void setDst(String dst) { - this.dst = dst; - } - - public String getTz() { - return tz; - } - - void setTz(String tz) { - this.tz = tz; - } - - public String getLastEnergyErase() { - return lastEnergyErase; - } - - void setLastEnergyErase(String lastEnergyErase) { - this.lastEnergyErase = lastEnergyErase; - } - - public String getLastConfig() { - return lastConfig; - } - - void setLastConfig(String lastConfig) { - this.lastConfig = lastConfig; - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostat.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostat.java deleted file mode 100644 index 6665fb280859f..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostat.java +++ /dev/null @@ -1,275 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.protocol; - -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlThermostatHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The {@link NhcThermostat} class represents the thermostat Niko Home Control communication object. It contains all - * fields representing a Niko Home Control thermostat and has methods to set the thermostat in Niko Home Control and - * receive thermostat updates. - * - * @author Mark Herwege - Initial Contribution - */ -@NonNullByDefault -public final class NhcThermostat { - - private final Logger logger = LoggerFactory.getLogger(NhcThermostat.class); - - @Nullable - private NikoHomeControlCommunication nhcComm; - - private int id; - private String name; - private String location; - private Integer measured = 0; - private Integer setpoint = 0; - private Integer mode = 0; - private Integer overrule = 0; - private Integer overruletime = 0; - private Integer ecosave = 0; - - @Nullable - private LocalDateTime overruleStart; - - @Nullable - private NikoHomeControlThermostatHandler thingHandler; - - NhcThermostat(int id, String name, String location) { - this.id = id; - this.name = name; - this.location = location; - } - - /** - * Update all values of the thermostat without touching the thermostat definition (id, name and location) and - * without changing the ThingHandler callback. - * - * @param measured current temperature in 0.1°C multiples - * @param setpoint the setpoint temperature in 0.1°C multiples - * @param mode: 0 = day, 1 = night, 2 = eco, 3 = off, 4 = cool, 5 = prog 1, 6 = prog 2, 7 = prog 3 - * @param overrule the overrule temperature in 0.1°C multiples - * @param overruletime in minutes - * @param ecosave - */ - public void updateState(Integer measured, Integer setpoint, Integer mode, Integer overrule, Integer overruletime, - Integer ecosave) { - setMeasured(measured); - setSetpoint(setpoint); - setMode(mode); - setOverrule(overrule); - setOverruletime(overruletime); - setEcosave(ecosave); - - NikoHomeControlThermostatHandler handler = thingHandler; - if (handler != null) { - logger.debug("Niko Home Control: update channels for {}", id); - handler.handleStateUpdate(this); - } - } - - /** - * This method should be called if the ThingHandler for the thing corresponding to this action is initialized. - * It keeps a record of the thing handler in this object so the thing can be updated when - * the action receives an update from the Niko Home Control IP-interface. - * - * @param handler - */ - public void setThingHandler(NikoHomeControlThermostatHandler handler) { - this.thingHandler = handler; - } - - /** - * This method sets a pointer to the nhcComm object of class {@link NikoHomeControlCommuncation}. - * This is then used to be able to call back the sendCommand method in this class to send a command to the - * Niko Home Control IP-interface when.. - * - * @param nhcComm - */ - public void setNhcComm(NikoHomeControlCommunication nhcComm) { - this.nhcComm = nhcComm; - } - - /** - * Get name of action. - * - * @return action name - */ - public String getName() { - return this.name; - } - - /** - * Get location name of action. - * - * @return location name - */ - public String getLocation() { - return this.location; - } - - /** - * Get measured temperature. - * - * @return measured temperature in 0.1°C multiples - */ - public Integer getMeasured() { - return this.measured; - } - - private void setMeasured(Integer measured) { - this.measured = measured; - } - - /** - * @return the setpoint temperature in 0.1°C multiples - */ - public Integer getSetpoint() { - return setpoint; - } - - private void setSetpoint(Integer setpoint) { - this.setpoint = setpoint; - } - - /** - * Get the thermostat mode. - * - * @return the mode: 0 = day, 1 = night, 2 = eco, 3 = off, 4 = cool, 5 = prog 1, 6 = prog 2, 7 = prog 3 - */ - public Integer getMode() { - return mode; - } - - private void setMode(Integer mode) { - this.mode = mode; - } - - /** - * Get the overrule temperature. - * - * @return the overrule temperature in 0.1°C multiples - */ - public Integer getOverrule() { - return overrule; - } - - private void setOverrule(Integer overrule) { - this.overrule = overrule; - } - - /** - * Get the duration for an overrule temperature - * - * @return the overruletime in minutes - */ - public int getOverruletime() { - return overruletime; - } - - /** - * Set the duration for an overrule temperature - * - * @param overruletime the overruletime in minutes - */ - private void setOverruletime(int overruletime) { - if (overruletime <= 0) { - stopOverrule(); - } else if (overruletime != this.overruletime) { - startOverrule(); - } - this.overruletime = overruletime; - } - - /** - * @return the ecosave mode - */ - public Integer getEcosave() { - return ecosave; - } - - /** - * @param ecosave the ecosave mode to set - */ - private void setEcosave(Integer ecosave) { - this.ecosave = ecosave; - } - - /** - * Sends thermostat mode to Niko Home Control. - * - * @param mode - */ - public void executeMode(int mode) { - logger.debug("Niko Home Control: execute thermostat mode {} for {}", mode, this.id); - - NhcMessageCmd nhcCmd = new NhcMessageCmd("executethermostat", this.id).withMode(mode); - - NikoHomeControlCommunication comm = nhcComm; - if (comm != null) { - comm.sendMessage(nhcCmd); - } - } - - /** - * Sends thermostat setpoint to Niko Home Control. - * - * @param overrule temperature to overrule the setpoint in 0.1°C multiples - * @param time time duration in min for overrule - */ - public void executeOverrule(int overrule, int overruletime) { - logger.debug("Niko Home Control: execute thermostat overrule {} during {} min for {}", overrule, overruletime, - this.id); - - String overruletimeString = String.format("%1$02d:%2$02d", overruletime / 60, overruletime % 60); - NhcMessageCmd nhcCmd = new NhcMessageCmd("executethermostat", this.id).withOverrule(overrule) - .withOverruletime(overruletimeString); - - NikoHomeControlCommunication comm = nhcComm; - if (comm != null) { - comm.sendMessage(nhcCmd); - } - } - - /** - * @return remaining overrule time in minutes - */ - public long getRemainingOverruletime() { - if (overruleStart == null) { - return 0; - } else { - return overruletime - ChronoUnit.MINUTES.between(overruleStart, LocalDateTime.now()); - } - } - - /** - * Start a new overrule, this method is used to be able to calculate the remaining overrule time - */ - private void startOverrule() { - this.overruleStart = LocalDateTime.now(); - } - - /** - * Reset overrule start - */ - private void stopOverrule() { - this.overruleStart = null; - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlCommunication.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlCommunication.java deleted file mode 100644 index bfc4bfe636ca5..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlCommunication.java +++ /dev/null @@ -1,573 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.protocol; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.net.InetAddress; -import java.net.Socket; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonParseException; - -/** - * The {@link NikoHomeControlCommunication} class is able to do the following tasks with Niko Home Control - * systems: - *

    - *
  • Start and stop TCP socket connection with Niko Home Control IP-interface. - *
  • Read all setup and status information from the Niko Home Control Controller. - *
  • Execute Niko Home Control commands. - *
  • Listen to events from Niko Home Control. - *
- * - * Only switch, dimmer and rollershutter actions are currently implemented. - * - * A class instance is instantiated from the {@link NikoHomeControlBridgeHandler} class initialization. - * - * @author Mark Herwege - Initial Contribution - */ -@NonNullByDefault -public final class NikoHomeControlCommunication { - - private final Logger logger = LoggerFactory.getLogger(NikoHomeControlCommunication.class); - - @Nullable - private Socket nhcSocket; - @Nullable - private PrintWriter nhcOut; - @Nullable - private BufferedReader nhcIn; - - private boolean listenerStopped; - private boolean nhcEventsRunning; - - // We keep only 2 gson adapters used to serialize and deserialize all messages sent and received - private Gson gsonOut = new Gson(); - private Gson gsonIn; - - private final NhcSystemInfo systemInfo = new NhcSystemInfo(); - private final Map locations = new HashMap<>(); - private final Map actions = new HashMap<>(); - private final Map thermostats = new HashMap<>(); - - @Nullable - private NikoHomeControlBridgeHandler bridgeCallBack; - - /** - * Constructor for Niko Home Control communication object, manages communication with - * Niko Home Control IP-interface. - * - */ - public NikoHomeControlCommunication() { - // When we set up this object, we want to get the proper gson adapter set up once - GsonBuilder gsonBuilder = new GsonBuilder(); - gsonBuilder.registerTypeAdapter(NhcMessageBase.class, new NikoHomeControlMessageDeserializer()); - this.gsonIn = gsonBuilder.create(); - } - - /** - * Start communication with Niko Home Control IP-interface, run through initialization and start thread listening - * to all messages coming from Niko Home Control. - * - * @param addr IP-address of Niko Home Control IP-interface - * @param port port - * - */ - public synchronized void startCommunication() { - NikoHomeControlBridgeHandler handler = this.bridgeCallBack; - try { - for (int i = 1; nhcEventsRunning && (i <= 5); i++) { - // the events listener thread did not finish yet, so wait max 5000ms before restarting - Thread.sleep(1000); - } - if (nhcEventsRunning) { - logger.error( - "Niko Home Control: starting from thread {}, but previous connection still active after 5000ms", - Thread.currentThread().getId()); - throw new IOException(); - } - - if (handler == null) { - throw new IOException(); - } - InetAddress addr = handler.getAddr(); - int port = handler.getPort(); - - Socket socket = new Socket(addr, port); - this.nhcSocket = socket; - this.nhcOut = new PrintWriter(socket.getOutputStream(), true); - this.nhcIn = new BufferedReader(new InputStreamReader(socket.getInputStream())); - logger.debug("Niko Home Control: connected via local port {} from thread {}", socket.getLocalPort(), - Thread.currentThread().getId()); - - // initialize all info in local fields - initialize(); - - // Start Niko Home Control event listener. This listener will act on all messages coming from - // IP-interface. - (new Thread(nhcEvents)).start(); - - } catch (IOException | InterruptedException e) { - logger.warn("Niko Home Control: error initializing communication from thread {}", - Thread.currentThread().getId()); - stopCommunication(); - if (handler != null) { - handler.bridgeOffline(); - } - } - } - - /** - * Cleanup socket when the communication with Niko Home Control IP-interface is closed. - * - */ - public synchronized void stopCommunication() { - this.listenerStopped = true; - - Socket socket = this.nhcSocket; - if (socket != null) { - try { - socket.close(); - } catch (IOException ignore) { - // ignore IO Error when trying to close the socket if the intention is to close it anyway - } - } - this.nhcSocket = null; - - logger.debug("Niko Home Control: communication stopped from thread {}", Thread.currentThread().getId()); - } - - /** - * Close and restart communication with Niko Home Control IP-interface. - * - */ - public synchronized void restartCommunication() { - stopCommunication(); - - logger.debug("Niko Home Control: restart communication from thread {}", Thread.currentThread().getId()); - - startCommunication(); - } - - /** - * Method to check if communication with Niko Home Control IP-interface is active - * - * @return True if active - */ - public boolean communicationActive() { - return (this.nhcSocket != null); - } - - /** - * Runnable that handles inbound communication from Niko Home Control. - *

- * The thread listens to the TCP socket opened at instantiation of the {@link NikoHomeControlCommunication} class - * and interprets all inbound json messages. It triggers state updates for active channels linked to the Niko Home - * Control actions. It is started after initialization of the communication. - * - */ - private Runnable nhcEvents = () -> { - String nhcMessage; - - logger.debug("Niko Home Control: listening for events on thread {}", Thread.currentThread().getId()); - listenerStopped = false; - nhcEventsRunning = true; - - BufferedReader reader = this.nhcIn; - - try { - if (reader == null) { - throw new IOException(); - } - while (!listenerStopped & ((nhcMessage = reader.readLine()) != null)) { - if (nhcMessage != null) { - readMessage(nhcMessage); - } - } - } catch (IOException e) { - if (!listenerStopped) { - nhcEventsRunning = false; - // this is a socket error, not a communication stop triggered from outside this runnable - logger.warn("Niko Home Control: IO error in listener on thread {}", Thread.currentThread().getId()); - // the IO has stopped working, so we need to close cleanly and try to restart - restartCommunication(); - return; - } - } - - nhcEventsRunning = false; - // this is a stop from outside the runnable, so just log it and stop - logger.debug("Niko Home Control: event listener thread stopped on thread {}", Thread.currentThread().getId()); - }; - - /** - * Method that interprets all feedback from Niko Home Control and calls appropriate handling methods. - * - * @param nhcMessage message read from Niko Home Control. - */ - private void readMessage(String nhcMessage) { - logger.debug("Niko Home Control: received json {} on thread {}", nhcMessage, Thread.currentThread().getId()); - - try { - NhcMessageBase nhcMessageGson = this.gsonIn.fromJson(nhcMessage, NhcMessageBase.class); - - String cmd = nhcMessageGson.getCmd(); - String event = nhcMessageGson.getEvent(); - - if ("systeminfo".equals(cmd)) { - cmdSystemInfo(((NhcMessageMap) nhcMessageGson).getData()); - } else if ("startevents".equals(cmd)) { - cmdStartEvents(((NhcMessageMap) nhcMessageGson).getData()); - } else if ("listlocations".equals(cmd)) { - cmdListLocations(((NhcMessageListMap) nhcMessageGson).getData()); - } else if ("listactions".equals(cmd)) { - cmdListActions(((NhcMessageListMap) nhcMessageGson).getData()); - } else if (("listthermostat").equals(cmd)) { - cmdListThermostat(((NhcMessageListMap) nhcMessageGson).getData()); - } else if ("executeactions".equals(cmd)) { - cmdExecuteActions(((NhcMessageMap) nhcMessageGson).getData()); - } else if ("executethermostat".equals(cmd)) { - cmdExecuteThermostat(((NhcMessageMap) nhcMessageGson).getData()); - } else if ("listactions".equals(event)) { - eventListActions(((NhcMessageListMap) nhcMessageGson).getData()); - } else if ("listthermostat".equals(event)) { - eventListThermostat(((NhcMessageListMap) nhcMessageGson).getData()); - } else if ("getalarms".equals(event)) { - eventGetAlarms(((NhcMessageMap) nhcMessageGson).getData()); - } else { - logger.debug("Niko Home Control: not acted on json {}", nhcMessage); - } - } catch (JsonParseException e) { - logger.debug("Niko Home Control: not acted on unsupported json {}", nhcMessage); - } - } - - /** - * After setting up the communication with the Niko Home Control IP-interface, send all initialization messages. - *

- * Only at first initialization, also set the return values. Otherwise use the runnable to get updated values. - * While communication is set up for thermostats, tariff data and alarms, only info from locations and actions - * is used beyond this point in openHAB. All other elements are for future extensions. - * - * @throws IOException - */ - private void initialize() throws IOException { - sendAndReadMessage("systeminfo"); - sendAndReadMessage("startevents"); - sendAndReadMessage("listlocations"); - sendAndReadMessage("listactions"); - sendAndReadMessage("listthermostat"); - sendAndReadMessage("listthermostatHVAC"); - sendAndReadMessage("readtariffdata"); - sendAndReadMessage("getalarms"); - } - - private void sendAndReadMessage(String command) throws IOException { - sendMessage(new NhcMessageCmd(command)); - BufferedReader reader = this.nhcIn; - if (reader == null) { - throw new IOException("Cannot read from socket, reader not connected."); - } - readMessage(reader.readLine()); - } - - private void cmdSystemInfo(Map data) { - logger.debug("Niko Home Control: systeminfo"); - - if (data.containsKey("swversion")) { - this.systemInfo.setSwVersion(data.get("swversion")); - } - if (data.containsKey("api")) { - this.systemInfo.setApi(data.get("api")); - } - if (data.containsKey("time")) { - this.systemInfo.setTime(data.get("time")); - } - if (data.containsKey("language")) { - this.systemInfo.setLanguage(data.get("language")); - } - if (data.containsKey("currency")) { - this.systemInfo.setCurrency(data.get("currency")); - } - if (data.containsKey("units")) { - this.systemInfo.setUnits(data.get("units")); - } - if (data.containsKey("DST")) { - this.systemInfo.setDst(data.get("DST")); - } - if (data.containsKey("TZ")) { - this.systemInfo.setTz(data.get("TZ")); - } - if (data.containsKey("lastenergyerase")) { - this.systemInfo.setLastEnergyErase(data.get("lastenergyerase")); - } - if (data.containsKey("lastconfig")) { - this.systemInfo.setLastConfig(data.get("lastconfig")); - } - } - - private void cmdStartEvents(Map data) { - Integer errorCode = Integer.valueOf(data.get("error")); - - if (errorCode.equals(0)) { - logger.debug("Niko Home Control: start events success"); - } else { - logger.warn("Niko Home Control: error code {} returned on start events", errorCode); - } - } - - private void cmdListLocations(List> data) { - logger.debug("Niko Home Control: list locations"); - - this.locations.clear(); - - for (Map location : data) { - int id = Integer.valueOf(location.get("id")); - String name = location.get("name"); - NhcLocation nhcLocation = new NhcLocation(name); - this.locations.put(id, nhcLocation); - } - } - - private void cmdListActions(List> data) { - logger.debug("Niko Home Control: list actions"); - - for (Map action : data) { - - int id = Integer.parseInt(action.get("id")); - Integer state = Integer.valueOf(action.get("value1")); - String value2 = action.get("value2"); - @SuppressWarnings("null") - Integer closeTime = (value2 == null ? null : Integer.valueOf(value2)); - String value3 = action.get("value3"); - @SuppressWarnings("null") - Integer openTime = (value3 == null ? null : Integer.valueOf(value3)); - - if (!this.actions.containsKey(id)) { - // Initial instantiation of NhcAction class for action object - String name = action.get("name"); - Integer type = Integer.valueOf(action.get("type")); - Integer locationId = Integer.valueOf(action.get("location")); - String location = ""; - if (locationId != null) { - location = this.locations.get(locationId).getName(); - } - NhcAction nhcAction = new NhcAction(id, name, type, location, closeTime, openTime); - nhcAction.setState(state); - nhcAction.setNhcComm(this); - this.actions.put(id, nhcAction); - } else { - // Action object already exists, so only update state. - // If we would re-instantiate action, we would lose pointer back from action to thing handler that was - // set in thing handler initialize(). - this.actions.get(id).setState(state); - } - } - } - - private void cmdListThermostat(List> data) { - logger.debug("Niko Home Control: list thermostats"); - - for (Map thermostat : data) { - - int id = Integer.parseInt(thermostat.get("id")); - Integer measured = Integer.valueOf(thermostat.get("measured")); - Integer setpoint = Integer.valueOf(thermostat.get("setpoint")); - Integer mode = Integer.valueOf(thermostat.get("mode")); - Integer overrule = Integer.valueOf(thermostat.get("overrule")); - // overruletime received in "HH:MM" format - String[] overruletimeStrings = thermostat.get("overruletime").split(":"); - Integer overruletime = 0; - if (overruletimeStrings.length == 2) { - overruletime = Integer.valueOf(overruletimeStrings[0]) * 60 + Integer.valueOf(overruletimeStrings[1]); - } - Integer ecosave = Integer.valueOf(thermostat.get("ecosave")); - - if (!this.thermostats.containsKey(id)) { - // Initial instantiation of NhcThermostat class for thermostat object - String name = thermostat.get("name"); - Integer locationId = Integer.valueOf(thermostat.get("location")); - String location = ""; - if (locationId != null) { - location = this.locations.get(locationId).getName(); - } - NhcThermostat nhcThermostat = new NhcThermostat(id, name, location); - nhcThermostat.updateState(measured, setpoint, mode, overrule, overruletime, ecosave); - nhcThermostat.setNhcComm(this); - this.thermostats.put(id, nhcThermostat); - } else { - // Thermostat object already exists, so only update state. - // If we would re-instantiate thermostat, we would lose pointer back from thermostat to thing handler - // that was set in thing handler initialize(). - this.thermostats.get(id).updateState(measured, setpoint, mode, overrule, overruletime, ecosave); - } - } - } - - private void cmdExecuteActions(Map data) { - Integer errorCode = Integer.valueOf(data.get("error")); - if (errorCode.equals(0)) { - logger.debug("Niko Home Control: execute action success"); - } else { - logger.warn("Niko Home Control: error code {} returned on command execution", errorCode); - } - } - - private void cmdExecuteThermostat(Map data) { - Integer errorCode = Integer.valueOf(data.get("error")); - if (errorCode.equals(0)) { - logger.debug("Niko Home Control: execute thermostats success"); - } else { - logger.warn("Niko Home Control: error code {} returned on command execution", errorCode); - } - } - - private void eventListActions(List> data) { - for (Map action : data) { - int id = Integer.valueOf(action.get("id")); - if (!this.actions.containsKey(id)) { - logger.warn("Niko Home Control: action in controller not known {}", id); - return; - } - Integer state = Integer.valueOf(action.get("value1")); - logger.debug("Niko Home Control: event execute action {} with state {}", id, state); - this.actions.get(id).setState(state); - } - } - - private void eventListThermostat(List> data) { - for (Map thermostat : data) { - int id = Integer.parseInt(thermostat.get("id")); - if (!this.thermostats.containsKey(id)) { - logger.warn("Niko Home Control: thermostat in controller not known {}", id); - return; - } - - Integer measured = Integer.valueOf(thermostat.get("measured")); - Integer setpoint = Integer.valueOf(thermostat.get("setpoint")); - Integer mode = Integer.valueOf(thermostat.get("mode")); - Integer overrule = Integer.valueOf(thermostat.get("overrule")); - // overruletime received in "HH:MM" format - String[] overruletimeStrings = thermostat.get("overruletime").split(":"); - Integer overruletime = 0; - if (overruletimeStrings.length == 2) { - overruletime = Integer.valueOf(overruletimeStrings[0]) * 60 + Integer.valueOf(overruletimeStrings[1]); - } - Integer ecosave = Integer.valueOf(thermostat.get("ecosave")); - - logger.debug( - "Niko Home Control: event execute thermostat {} with measured {}, setpoint {}, mode {}, overrule {}, overruletime {}, ecosave {}", - id, measured, setpoint, mode, overrule, overruletime, ecosave); - this.thermostats.get(id).updateState(measured, setpoint, mode, overrule, overruletime, ecosave); - } - } - - private void eventGetAlarms(Map data) { - NikoHomeControlBridgeHandler handler = this.bridgeCallBack; - - Integer type = Integer.valueOf(data.get("type")); - String alarmText = data.get("text"); - - if (handler == null) { - logger.error("Niko Home Control: triggering alarm '{}' but no bridge handler", alarmText); - return; - } - - switch (type) { - case 0: - logger.debug("Niko Home Control: alarm - {}", alarmText); - handler.triggerAlarm(alarmText); - break; - case 1: - logger.debug("Niko Home Control: notice - {}", alarmText); - handler.triggerNotice(alarmText); - break; - default: - logger.debug("Niko Home Control: unexpected message type {}", type); - } - } - - /** - * Called by other methods to send json cmd to Niko Home Control. - * - * @param nhcMessage - */ - synchronized void sendMessage(Object nhcMessage) { - PrintWriter writer = this.nhcOut; - String json = gsonOut.toJson(nhcMessage); - logger.debug("Niko Home Control: send json {} from thread {}", json, Thread.currentThread().getId()); - if (writer != null) { - writer.println(json); - } - if ((writer == null) || (writer.checkError())) { - logger.warn("Niko Home Control: error sending message, trying to restart communication"); - restartCommunication(); - // retry sending after restart - logger.debug("Niko Home Control: resend json {} from thread {}", json, Thread.currentThread().getId()); - writer = this.nhcOut; - if (writer != null) { - writer.println(json); - } - if ((writer == null) || (writer.checkError())) { - logger.warn("Niko Home Control: error resending message"); - - } - } - } - - /** - * Return the object with system info as read from the Niko Home Control controller. - * - * @return the systemInfo - */ - public NhcSystemInfo getSystemInfo() { - return this.systemInfo; - } - - /** - * Return all actions in the Niko Home Control Controller. - * - * @return Map<Integer, {@link NhcAction}> - */ - public Map getActions() { - return this.actions; - } - - /** - * Return all thermostats in the Niko Home Control Controller. - * - * @return Map<Integer, {@link NhcThermostat}> - */ - public Map getThermostats() { - return this.thermostats; - } - - /** - * @param bridgeCallBack the bridgeCallBack to set - */ - public void setBridgeCallBack(NikoHomeControlBridgeHandler bridgeCallBack) { - this.bridgeCallBack = bridgeCallBack; - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlDiscover.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlDiscover.java deleted file mode 100644 index 48b68dd737a04..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlDiscover.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.protocol; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Class {@link NikoHomeControlDiscover} is used to get the Niko Home Control IP-interface IP address for bridge - * discovery. - *

- * The constructor broadcasts a UDP packet with content 0x44 on port 10000. - * The Niko Home Control IP-interface responds to this UDP packet. - * The IP-address from the Niko Home Control IP-interface is then extracted from the response packet. - * The data content of the response packet is used as a unique identifier for the bridge. - * - * @author Mark Herwege - Initial Contribution - */ -public final class NikoHomeControlDiscover { - - private final Logger logger = LoggerFactory.getLogger(NikoHomeControlDiscover.class); - - private InetAddress addr; - private String nhcBridgeId; - - public NikoHomeControlDiscover(String broadcast) throws IOException { - final byte[] discoverBuffer = { (byte) 0x44 }; - final InetAddress broadcastAddr = InetAddress.getByName(broadcast); - final int broadcastPort = 10000; - - DatagramPacket discoveryPacket = new DatagramPacket(discoverBuffer, discoverBuffer.length, broadcastAddr, - broadcastPort); - byte[] buffer = new byte[1024]; - DatagramPacket packet = new DatagramPacket(buffer, buffer.length); - - try (DatagramSocket datagramSocket = new DatagramSocket(null)) { - datagramSocket.setBroadcast(true); - datagramSocket.setSoTimeout(500); - datagramSocket.send(discoveryPacket); - datagramSocket.receive(packet); - this.addr = packet.getAddress(); - setNhcBridgeId(packet); - logger.debug("Niko Home Control: IP address is {}, unique ID is {}", this.addr, this.nhcBridgeId); - } - } - - /** - * @return the addr - */ - public InetAddress getAddr() { - return this.addr; - } - - /** - * @return the nhcBridgeId - */ - public String getNhcBridgeId() { - return this.nhcBridgeId; - } - - /** - * Retrieves a unique ID from the returned datagram packet received after sending the UDP discovery message. - * - * @param packet - */ - private void setNhcBridgeId(DatagramPacket packet) { - byte[] packetData = packet.getData(); - int packetLength = packet.getLength(); - packetLength = packetLength > 6 ? 6 : packetLength; - StringBuilder sb = new StringBuilder(packetLength); - for (int i = 0; i < packetLength; i++) { - sb.append(String.format("%02x", packetData[i])); - } - this.nhcBridgeId = sb.toString(); - } -} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlMessageDeserializer.java b/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlMessageDeserializer.java deleted file mode 100644 index dbca9a6f6b946..0000000000000 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlMessageDeserializer.java +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.nikohomecontrol.internal.protocol; - -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; - -/** - * Class {@link NikoHomeControlMessageDeserializer} deserializes all json messages from Niko Home Control. Various json - * message formats are supported. The format is selected based on the content of the cmd and event json objects. - * - * @author Mark Herwege - Initial Contribution - * - */ -class NikoHomeControlMessageDeserializer implements JsonDeserializer { - - @Override - public NhcMessageBase deserialize(final JsonElement json, final Type typeOfT, - final JsonDeserializationContext context) throws JsonParseException { - final JsonObject jsonObject = json.getAsJsonObject(); - - try { - String cmd = null; - String event = null; - if (jsonObject.has("cmd")) { - cmd = jsonObject.get("cmd").getAsString(); - } - if (jsonObject.has("event")) { - event = jsonObject.get("event").getAsString(); - } - - JsonElement jsonData = null; - if (jsonObject.has("data")) { - jsonData = jsonObject.get("data"); - } - - NhcMessageBase message = null; - - if (jsonData != null) { - if (jsonData.isJsonObject()) { - message = new NhcMessageMap(); - - Map data = new HashMap<>(); - for (Entry entry : jsonData.getAsJsonObject().entrySet()) { - data.put(entry.getKey(), entry.getValue().getAsString()); - } - ((NhcMessageMap) message).setData(data); - - } else if (jsonData.isJsonArray()) { - JsonArray jsonDataArray = jsonData.getAsJsonArray(); - - message = new NhcMessageListMap(); - - List> dataList = new ArrayList<>(); - for (int i = 0; i < jsonDataArray.size(); i++) { - JsonObject jsonDataObject = jsonDataArray.get(i).getAsJsonObject(); - - Map data = new HashMap<>(); - for (Entry entry : jsonDataObject.entrySet()) { - data.put(entry.getKey(), entry.getValue().getAsString()); - } - dataList.add(data); - } - ((NhcMessageListMap) message).setData(dataList); - } - } - - if (message != null) { - message.setCmd(cmd); - message.setEvent(event); - } else { - throw new JsonParseException("Unexpected Json type"); - } - - return message; - - } catch (IllegalStateException | ClassCastException e) { - throw new JsonParseException("Unexpected Json type"); - } - } -} diff --git a/addons/binding/org.openhab.binding.onewiregpio/.classpath b/addons/binding/org.openhab.binding.onewiregpio/.classpath deleted file mode 100644 index 6566c1e3a0327..0000000000000 --- a/addons/binding/org.openhab.binding.onewiregpio/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.onewiregpio/.project b/addons/binding/org.openhab.binding.onewiregpio/.project deleted file mode 100644 index b91bbec26ed4c..0000000000000 --- a/addons/binding/org.openhab.binding.onewiregpio/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.onewiregpio - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.onewiregpio/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.onewiregpio/META-INF/MANIFEST.MF deleted file mode 100644 index be54f18229237..0000000000000 --- a/addons/binding/org.openhab.binding.onewiregpio/META-INF/MANIFEST.MF +++ /dev/null @@ -1,24 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.onewiregpio -Bundle-ManifestVersion: 2 -Bundle-Name: OneWireGPIO Binding -Bundle-SymbolicName: org.openhab.binding.onewiregpio;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-ClassPath: . -Import-Package: - javax.measure.quantity, - org.apache.commons.lang, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.core.library.unit, - org.osgi.service.component, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.onewiregpio/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.onewiregpio/OSGI-INF/.gitignore deleted file mode 100644 index b83de716f28a7..0000000000000 --- a/addons/binding/org.openhab.binding.onewiregpio/OSGI-INF/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.xml - diff --git a/addons/binding/org.openhab.binding.onewiregpio/build.properties b/addons/binding/org.openhab.binding.onewiregpio/build.properties deleted file mode 100644 index 0dbc0455bd695..0000000000000 --- a/addons/binding/org.openhab.binding.onewiregpio/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - NOTICE,\ - OSGI-INF/,\ - ESH-INF/ diff --git a/addons/binding/org.openhab.binding.onewiregpio/pom.xml b/addons/binding/org.openhab.binding.onewiregpio/pom.xml deleted file mode 100644 index b3ec0d35d06af..0000000000000 --- a/addons/binding/org.openhab.binding.onewiregpio/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.onewiregpio - eclipse-plugin - - OneWireGPIO Binding - - diff --git a/addons/binding/org.openhab.binding.onkyo/.classpath b/addons/binding/org.openhab.binding.onkyo/.classpath deleted file mode 100644 index 7f457fa4138d1..0000000000000 --- a/addons/binding/org.openhab.binding.onkyo/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.onkyo/.project b/addons/binding/org.openhab.binding.onkyo/.project deleted file mode 100644 index 5d4789ccfe312..0000000000000 --- a/addons/binding/org.openhab.binding.onkyo/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.onkyo - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.onkyo/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.onkyo/META-INF/MANIFEST.MF deleted file mode 100644 index 06c5a443aff53..0000000000000 --- a/addons/binding/org.openhab.binding.onkyo/META-INF/MANIFEST.MF +++ /dev/null @@ -1,34 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.onkyo -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: . -Bundle-ManifestVersion: 2 -Bundle-Name: Onkyo Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.onkyo;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - org.apache.commons.io, - org.apache.commons.lang, - org.openhab.core.automation.annotation;resolution:=optional, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.config.discovery.upnp, - org.eclipse.smarthome.core.audio, - org.eclipse.smarthome.core.library.items, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.net, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.core.util, - org.eclipse.smarthome.io.net.http, - org.eclipse.smarthome.io.transport.upnp, - org.jupnp.model.meta, - org.jupnp.model.types, - org.osgi.framework, - org.osgi.service.component, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.onkyo/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.onkyo/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.onkyo/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.onkyo/build.properties b/addons/binding/org.openhab.binding.onkyo/build.properties deleted file mode 100644 index 2802d321c602c..0000000000000 --- a/addons/binding/org.openhab.binding.onkyo/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE - diff --git a/addons/binding/org.openhab.binding.onkyo/pom.xml b/addons/binding/org.openhab.binding.onkyo/pom.xml deleted file mode 100644 index 11a2fcc2cf725..0000000000000 --- a/addons/binding/org.openhab.binding.onkyo/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.onkyo - eclipse-plugin - - Onkyo Binding - - diff --git a/addons/binding/org.openhab.binding.orvibo/.classpath b/addons/binding/org.openhab.binding.orvibo/.classpath deleted file mode 100644 index 83dc9a6113ff7..0000000000000 --- a/addons/binding/org.openhab.binding.orvibo/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/addons/binding/org.openhab.binding.orvibo/.project b/addons/binding/org.openhab.binding.orvibo/.project deleted file mode 100644 index 89327880051dc..0000000000000 --- a/addons/binding/org.openhab.binding.orvibo/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.orvibo - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.orvibo/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.orvibo/META-INF/MANIFEST.MF deleted file mode 100644 index d4a7a0126366b..0000000000000 --- a/addons/binding/org.openhab.binding.orvibo/META-INF/MANIFEST.MF +++ /dev/null @@ -1,29 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.orvibo -Bundle-ActivationPolicy: lazy -Bundle-Activator: org.openhab.binding.orvibo.internal.OrviboActivator -Bundle-ClassPath: - ., - lib/s20-sdk-0.0.6-SNAPSHOT.jar -Bundle-ManifestVersion: 2 -Bundle-Name: Orvibo Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.orvibo;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - javax.xml.bind, - org.apache.commons.lang, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.core.items, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.types, - org.osgi.framework, - org.osgi.service.component, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.orvibo/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.orvibo/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.orvibo/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.orvibo/build.properties b/addons/binding/org.openhab.binding.orvibo/build.properties deleted file mode 100644 index 086f6bde3f1e5..0000000000000 --- a/addons/binding/org.openhab.binding.orvibo/build.properties +++ /dev/null @@ -1,9 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - lib/s20-sdk-0.0.6-SNAPSHOT.jar,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE - diff --git a/addons/binding/org.openhab.binding.orvibo/pom.xml b/addons/binding/org.openhab.binding.orvibo/pom.xml deleted file mode 100644 index 6c8b14c43a376..0000000000000 --- a/addons/binding/org.openhab.binding.orvibo/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.orvibo - eclipse-plugin - - Orvibo Binding - - diff --git a/addons/binding/org.openhab.binding.samsungtv/.classpath b/addons/binding/org.openhab.binding.samsungtv/.classpath deleted file mode 100644 index 7f457fa4138d1..0000000000000 --- a/addons/binding/org.openhab.binding.samsungtv/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.samsungtv/.project b/addons/binding/org.openhab.binding.samsungtv/.project deleted file mode 100644 index 4174cdc806074..0000000000000 --- a/addons/binding/org.openhab.binding.samsungtv/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.samsungtv - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.samsungtv/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.samsungtv/META-INF/MANIFEST.MF deleted file mode 100644 index 1a94264d8b095..0000000000000 --- a/addons/binding/org.openhab.binding.samsungtv/META-INF/MANIFEST.MF +++ /dev/null @@ -1,28 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.samsungtv -Bundle-ClassPath: . -Bundle-ManifestVersion: 2 -Bundle-Name: Samsung Tv Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.samsungtv;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - org.apache.commons.net.util, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.config.discovery.upnp, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.io.transport.upnp, - org.jupnp, - org.jupnp.binding.xml, - org.jupnp.controlpoint, - org.jupnp.model.meta, - org.jupnp.model.types, - org.jupnp.registry, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.samsungtv/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.samsungtv/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.samsungtv/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.samsungtv/build.properties b/addons/binding/org.openhab.binding.samsungtv/build.properties deleted file mode 100644 index 2802d321c602c..0000000000000 --- a/addons/binding/org.openhab.binding.samsungtv/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE - diff --git a/addons/binding/org.openhab.binding.samsungtv/pom.xml b/addons/binding/org.openhab.binding.samsungtv/pom.xml deleted file mode 100644 index c398204816e36..0000000000000 --- a/addons/binding/org.openhab.binding.samsungtv/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.samsungtv - eclipse-plugin - - SamsungTV Binding - - diff --git a/addons/binding/org.openhab.binding.smartmeter.test/.classpath b/addons/binding/org.openhab.binding.smartmeter.test/.classpath deleted file mode 100644 index 64777f25c82a4..0000000000000 --- a/addons/binding/org.openhab.binding.smartmeter.test/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.smartmeter.test/.project b/addons/binding/org.openhab.binding.smartmeter.test/.project deleted file mode 100644 index 80665eefe99fc..0000000000000 --- a/addons/binding/org.openhab.binding.smartmeter.test/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.smartmeter.test - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.smartmeter.test/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.smartmeter.test/META-INF/MANIFEST.MF deleted file mode 100644 index 08b43c1a9ee38..0000000000000 --- a/addons/binding/org.openhab.binding.smartmeter.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,22 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.smartmeter.test -Bundle-ManifestVersion: 2 -Bundle-Name: Smartmeter Binding Tests -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.smartmeter.test;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Fragment-Host: org.openhab.binding.smartmeter -Import-Package: - net.bytebuddy, - org.eclipse.jdt.annotation;resolution:=optional, - org.hamcrest;core=split, - org.junit;version="4.0.0", - org.junit.experimental.categories, - org.junit.matchers, - org.junit.runner, - org.mockito, - org.mockito.stubbing, - org.mockito.verification, - org.objenesis, - org.osgi.framework diff --git a/addons/binding/org.openhab.binding.smartmeter.test/build.properties b/addons/binding/org.openhab.binding.smartmeter.test/build.properties deleted file mode 100644 index 570fdcca0ba20..0000000000000 --- a/addons/binding/org.openhab.binding.smartmeter.test/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -source.. = src/test/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.smartmeter.test/pom.xml b/addons/binding/org.openhab.binding.smartmeter.test/pom.xml deleted file mode 100644 index e4fddffb96202..0000000000000 --- a/addons/binding/org.openhab.binding.smartmeter.test/pom.xml +++ /dev/null @@ -1,110 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.smartmeter.test - eclipse-test-plugin - - Smartmeter Binding Tests - - - - - ${tycho-groupid} - tycho-surefire-plugin - - junit47 - - 4 - true - - - - - eclipse-plugin - org.eclipse.equinox.ds - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.event - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.util - 0.0.0 - - - eclipse-plugin - org.eclipse.osgi.services - 0.0.0 - - - - eclipse-plugin - ch.qos.logback.classic - 0.0.0 - - - eclipse-plugin - ch.qos.logback.core - 0.0.0 - - - eclipse-plugin - ch.qos.logback.slf4j - 0.0.0 - - - - eclipse-plugin - org.openhab.core.config.xml - 0.0.0 - - - eclipse-plugin - org.openhab.core.thing - 0.0.0 - - - eclipse-plugin - org.openhab.core.thing.xml - 0.0.0 - - - eclipse-plugin - org.openhab.core.binding.xml - 0.0.0 - - - - - org.eclipse.equinox.ds - 1 - true - - - org.eclipse.equinox.event - 2 - true - - - - false - - - notSimulator - - - - - - diff --git a/addons/binding/org.openhab.binding.smartmeter/.classpath b/addons/binding/org.openhab.binding.smartmeter/.classpath deleted file mode 100644 index 0a43bc3e46a92..0000000000000 --- a/addons/binding/org.openhab.binding.smartmeter/.classpath +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.smartmeter/.project b/addons/binding/org.openhab.binding.smartmeter/.project deleted file mode 100644 index 2a95c60f994df..0000000000000 --- a/addons/binding/org.openhab.binding.smartmeter/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.smartmeter - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.smartmeter/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.smartmeter/META-INF/MANIFEST.MF deleted file mode 100644 index 62131738ac011..0000000000000 --- a/addons/binding/org.openhab.binding.smartmeter/META-INF/MANIFEST.MF +++ /dev/null @@ -1,44 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.smartmeter -Bundle-ManifestVersion: 2 -Bundle-Name: Smartmeter Binding -Bundle-SymbolicName: org.openhab.binding.smartmeter;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-ClassPath: ., - lib/jsml.jar, - lib/j62056.jar, - lib/rxjava-2.2.3.jar, - lib/reactive-streams-1.0.2.jar -Import-Package: - javax.measure, - javax.measure.format, - javax.measure.quantity, - javax.measure.spi, - org.apache.commons.lang, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.common, - org.eclipse.smarthome.core.items, - org.eclipse.smarthome.core.library, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.library.unit, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.core.types.util, - org.eclipse.smarthome.core.util, - org.eclipse.smarthome.io.transport.serial, - org.osgi.framework, - org.osgi.service.component, - org.osgi.util.tracker, - org.slf4j, - org.openhab.binding.smartmeter, - org.openhab.binding.smartmeter.connectors -Service-Component: OSGI-INF/*.xml -Export-Package: org.openhab.binding.smartmeter;x-internal:=true, - org.openhab.binding.smartmeter.connectors;x-internal:=true -Bundle-ActivationPolicy: lazy diff --git a/addons/binding/org.openhab.binding.smartmeter/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.smartmeter/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.smartmeter/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.smartmeter/build.properties b/addons/binding/org.openhab.binding.smartmeter/build.properties deleted file mode 100644 index d1a32aba06594..0000000000000 --- a/addons/binding/org.openhab.binding.smartmeter/build.properties +++ /dev/null @@ -1,13 +0,0 @@ -source.. = src/main/java/,\ - src/main/java-extern/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - lib/jsml.jar,\ - NOTICE,\ - lib/j62056.jar,\ - lib/rxjava-2.2.3.jar,\ - lib/reactive-streams-1.0.2.jar - diff --git a/addons/binding/org.openhab.binding.smartmeter/lib/j62056.jar b/addons/binding/org.openhab.binding.smartmeter/lib/j62056.jar deleted file mode 100644 index 3a27b2ac7d035..0000000000000 Binary files a/addons/binding/org.openhab.binding.smartmeter/lib/j62056.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.smartmeter/lib/jsml.jar b/addons/binding/org.openhab.binding.smartmeter/lib/jsml.jar deleted file mode 100644 index 8c48d80580ba0..0000000000000 Binary files a/addons/binding/org.openhab.binding.smartmeter/lib/jsml.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.smartmeter/lib/reactive-streams-1.0.2.jar b/addons/binding/org.openhab.binding.smartmeter/lib/reactive-streams-1.0.2.jar deleted file mode 100644 index 8e8a9ce0c5f74..0000000000000 Binary files a/addons/binding/org.openhab.binding.smartmeter/lib/reactive-streams-1.0.2.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.smartmeter/lib/rxjava-2.2.3.jar b/addons/binding/org.openhab.binding.smartmeter/lib/rxjava-2.2.3.jar deleted file mode 100644 index 32e860e0f3db9..0000000000000 Binary files a/addons/binding/org.openhab.binding.smartmeter/lib/rxjava-2.2.3.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.smartmeter/pom.xml b/addons/binding/org.openhab.binding.smartmeter/pom.xml deleted file mode 100644 index de36cd2d1388c..0000000000000 --- a/addons/binding/org.openhab.binding.smartmeter/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.smartmeter - - Smartmeter Binding - eclipse-plugin - - diff --git a/addons/binding/org.openhab.binding.systeminfo.test/.classpath b/addons/binding/org.openhab.binding.systeminfo.test/.classpath deleted file mode 100644 index 64777f25c82a4..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo.test/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.systeminfo.test/.project b/addons/binding/org.openhab.binding.systeminfo.test/.project deleted file mode 100644 index 93b63f92678bd..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo.test/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.systeminfo.test - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.systeminfo.test/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.systeminfo.test/META-INF/MANIFEST.MF deleted file mode 100644 index bc57dc5f0f3fd..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,27 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.systeminfo.test -Bundle-ClassPath: . -Bundle-ManifestVersion: 2 -Bundle-Name: Systeminfo Binding Tests -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.systeminfo.test;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Fragment-Host: org.openhab.binding.systeminfo -Import-Package: - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.config.discovery.inbox, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.items, - org.eclipse.smarthome.core.library.items, - org.eclipse.smarthome.core.thing.link, - org.eclipse.smarthome.test.java, - org.eclipse.smarthome.test.storage, - org.hamcrest;core=split, - org.junit;version="4.0.0", - org.mockito, - org.mockito.stubbing, - org.osgi.framework, - org.osgi.service.component, - org.slf4j diff --git a/addons/binding/org.openhab.binding.systeminfo.test/NOTICE b/addons/binding/org.openhab.binding.systeminfo.test/NOTICE deleted file mode 100644 index 4c20ef446c1e4..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo.test/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -This content is produced and maintained by the openHAB project. - -* Project home: https://www.openhab.org - -== Declared Project Licenses - -This program and the accompanying materials are made available under the terms -of the Eclipse Public License 2.0 which is available at -https://www.eclipse.org/legal/epl-2.0/. - -== Source Code - -https://github.com/openhab/openhab2-addons diff --git a/addons/binding/org.openhab.binding.systeminfo.test/build.properties b/addons/binding/org.openhab.binding.systeminfo.test/build.properties deleted file mode 100644 index 8c2120c833e7a..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo.test/build.properties +++ /dev/null @@ -1,6 +0,0 @@ -source.. = src/test/java -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - NOTICE - diff --git a/addons/binding/org.openhab.binding.systeminfo.test/org.openhab.binding.systeminfo.test.launch b/addons/binding/org.openhab.binding.systeminfo.test/org.openhab.binding.systeminfo.test.launch deleted file mode 100644 index 9996ba2beb464..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo.test/org.openhab.binding.systeminfo.test.launch +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.systeminfo.test/pom.xml b/addons/binding/org.openhab.binding.systeminfo.test/pom.xml deleted file mode 100644 index 774ec471afc9f..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo.test/pom.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.systeminfo.test - eclipse-test-plugin - - Systeminfo Binding Test - - - - - ${tycho-groupid} - tycho-surefire-plugin - - junit47 - - - eclipse-plugin - org.eclipse.equinox.event - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.ds - 0.0.0 - - - eclipse-plugin - org.openhab.core.config.xml - 0.0.0 - - - eclipse-plugin - org.openhab.core.thing.xml - 0.0.0 - - - eclipse-plugin - org.openhab.core.binding.xml - 0.0.0 - - - - eclipse-plugin - ch.qos.logback.classic - 0.0.0 - - - eclipse-plugin - ch.qos.logback.core - 0.0.0 - - - eclipse-plugin - ch.qos.logback.slf4j - 0.0.0 - - - - 4 - true - - false - - - - - - diff --git a/addons/binding/org.openhab.binding.systeminfo/.classpath b/addons/binding/org.openhab.binding.systeminfo/.classpath deleted file mode 100644 index 6c94890f268da..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo/.classpath +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.systeminfo/.project b/addons/binding/org.openhab.binding.systeminfo/.project deleted file mode 100644 index 3d7378ad7520c..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.systeminfo - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.systeminfo/ESH-INF/binding/binding.xml b/addons/binding/org.openhab.binding.systeminfo/ESH-INF/binding/binding.xml deleted file mode 100644 index 2e7926552ed2a..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo/ESH-INF/binding/binding.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - Systeminfo Binding - This binding provides information about the operating system and the hardware. - Svilen Valkanov - - diff --git a/addons/binding/org.openhab.binding.systeminfo/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.systeminfo/META-INF/MANIFEST.MF deleted file mode 100644 index c2b33845ed3c5..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo/META-INF/MANIFEST.MF +++ /dev/null @@ -1,28 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.systeminfo -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: - ., - lib/jna-platform-5.2.0.jar, - lib/jna-5.2.0.jar, - lib/oshi-core-3.13.0.jar -Bundle-ManifestVersion: 2 -Bundle-Name: Systeminfo Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.systeminfo;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - org.apache.commons.lang, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.core.validation, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.systeminfo/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.systeminfo/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.systeminfo/build.properties b/addons/binding/org.openhab.binding.systeminfo/build.properties deleted file mode 100644 index f3b3ec62e305b..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - lib/,\ - NOTICE -source.. = src/main/java/ diff --git a/addons/binding/org.openhab.binding.systeminfo/lib/jna-5.2.0.jar b/addons/binding/org.openhab.binding.systeminfo/lib/jna-5.2.0.jar deleted file mode 100644 index 0b5fabdd8baa1..0000000000000 Binary files a/addons/binding/org.openhab.binding.systeminfo/lib/jna-5.2.0.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.systeminfo/lib/jna-platform-5.2.0.jar b/addons/binding/org.openhab.binding.systeminfo/lib/jna-platform-5.2.0.jar deleted file mode 100644 index 472268182175c..0000000000000 Binary files a/addons/binding/org.openhab.binding.systeminfo/lib/jna-platform-5.2.0.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.systeminfo/lib/oshi-core-3.13.0.jar b/addons/binding/org.openhab.binding.systeminfo/lib/oshi-core-3.13.0.jar deleted file mode 100644 index 8679bbf743010..0000000000000 Binary files a/addons/binding/org.openhab.binding.systeminfo/lib/oshi-core-3.13.0.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.systeminfo/pom.xml b/addons/binding/org.openhab.binding.systeminfo/pom.xml deleted file mode 100644 index 2f6f313fb561d..0000000000000 --- a/addons/binding/org.openhab.binding.systeminfo/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.systeminfo - eclipse-plugin - - Systeminfo Binding - - diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/.classpath b/addons/binding/org.openhab.binding.yamahareceiver.test/.classpath deleted file mode 100644 index a0838479442e1..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver.test/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/.project b/addons/binding/org.openhab.binding.yamahareceiver.test/.project deleted file mode 100644 index 076b6a5c98244..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver.test/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.openhab.binding.yamahareceiver.test - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.yamahareceiver.test/META-INF/MANIFEST.MF deleted file mode 100644 index 62527e62e7468..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,30 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.yamahareceiver.test -Bundle-ManifestVersion: 2 -Bundle-Name: YamahaReceiver Binding Tests -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.yamahareceiver.test;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Fragment-Host: org.openhab.binding.yamahareceiver -Import-Package: - org.apache.commons.io, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.events, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.util, - org.eclipse.smarthome.test.java, - org.eclipse.smarthome.test.storage, - org.hamcrest;core=split, - org.hamcrest.core, - org.junit;version="4.0.0", - org.mockito, - org.mockito.invocation, - org.mockito.stubbing, - org.osgi.framework, - org.osgi.service.device, - org.slf4j diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/NOTICE b/addons/binding/org.openhab.binding.yamahareceiver.test/NOTICE deleted file mode 100644 index 4c20ef446c1e4..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver.test/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -This content is produced and maintained by the openHAB project. - -* Project home: https://www.openhab.org - -== Declared Project Licenses - -This program and the accompanying materials are made available under the terms -of the Eclipse Public License 2.0 which is available at -https://www.eclipse.org/legal/epl-2.0/. - -== Source Code - -https://github.com/openhab/openhab2-addons diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/build.properties b/addons/binding/org.openhab.binding.yamahareceiver.test/build.properties deleted file mode 100644 index 64491e243ba70..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver.test/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -source.. = src/test/java/ -output.. = target/test-classes -bin.includes = META-INF/,\ - .,\ - NOTICE diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/org.openhab.binding.yamahareceiver.test.launch b/addons/binding/org.openhab.binding.yamahareceiver.test/org.openhab.binding.yamahareceiver.test.launch deleted file mode 100644 index 7cc3a1a1f9d06..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver.test/org.openhab.binding.yamahareceiver.test.launch +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/pom.xml b/addons/binding/org.openhab.binding.yamahareceiver.test/pom.xml deleted file mode 100644 index 76b5cb7b1b66f..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver.test/pom.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - 4.0.0 - - - pom - org.openhab.binding - 2.5.0-SNAPSHOT - - - org.openhab.binding - org.openhab.binding.yamahareceiver.test - eclipse-test-plugin - - YamahaReceiver Binding Tests - - - - - ${tycho-groupid} - tycho-surefire-plugin - - - - eclipse-plugin - org.eclipse.equinox.ds - 0.0.0 - - - eclipse-plugin - ch.qos.logback.classic - 0.0.0 - - - eclipse-plugin - ch.qos.logback.core - 0.0.0 - - - eclipse-plugin - ch.qos.logback.slf4j - 0.0.0 - - - - - org.eclipse.equinox.ds - 1 - true - - - org.eclipse.equinox.event - 2 - true - - - org.openhab.core - 4 - true - - - org.openhab.core.thing - 4 - true - - - org.openhab.core.config.xml - 4 - true - - - org.openhab.core.thing.xml - 4 - true - - - org.openhab.core.binding.xml - 4 - true - - - - - - - - diff --git a/addons/binding/org.openhab.binding.yamahareceiver/.classpath b/addons/binding/org.openhab.binding.yamahareceiver/.classpath deleted file mode 100644 index 3876826e3c9a8..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/binding/org.openhab.binding.yamahareceiver/.project b/addons/binding/org.openhab.binding.yamahareceiver/.project deleted file mode 100644 index aef52f3fa774e..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.yamahareceiver - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.yamahareceiver/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.yamahareceiver/META-INF/MANIFEST.MF deleted file mode 100644 index 0e4087213c00f..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver/META-INF/MANIFEST.MF +++ /dev/null @@ -1,31 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.yamahareceiver -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: . -Bundle-ManifestVersion: 2 -Bundle-Name: YamahaReceiver Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.yamahareceiver;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - javax.xml.parsers, - org.apache.commons.lang, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.config.discovery.upnp, - org.eclipse.smarthome.config.xml, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.link, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.jupnp.model.meta, - org.jupnp.model.types, - org.osgi.framework, - org.osgi.service.component, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver/NOTICE b/addons/binding/org.openhab.binding.yamahareceiver/NOTICE deleted file mode 100644 index 4c20ef446c1e4..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -This content is produced and maintained by the openHAB project. - -* Project home: https://www.openhab.org - -== Declared Project Licenses - -This program and the accompanying materials are made available under the terms -of the Eclipse Public License 2.0 which is available at -https://www.eclipse.org/legal/epl-2.0/. - -== Source Code - -https://github.com/openhab/openhab2-addons diff --git a/addons/binding/org.openhab.binding.yamahareceiver/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.yamahareceiver/OSGI-INF/.gitignore deleted file mode 100644 index 2fb582e24a102..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver/OSGI-INF/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/*.xml - diff --git a/addons/binding/org.openhab.binding.yamahareceiver/build.properties b/addons/binding/org.openhab.binding.yamahareceiver/build.properties deleted file mode 100644 index 2802d321c602c..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE - diff --git a/addons/binding/org.openhab.binding.yamahareceiver/pom.xml b/addons/binding/org.openhab.binding.yamahareceiver/pom.xml deleted file mode 100644 index 22f0e96f178b1..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.yamahareceiver - eclipse-plugin - - YamahaReceiver Binding - - diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/ChannelsTypeProviderPreset.java b/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/ChannelsTypeProviderPreset.java deleted file mode 100644 index c2b12a09810a5..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/ChannelsTypeProviderPreset.java +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.yamahareceiver.internal; - -import static java.util.stream.Collectors.toList; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.*; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.stream.IntStream; - -import org.eclipse.smarthome.core.thing.ThingUID; -import org.eclipse.smarthome.core.thing.type.ChannelGroupType; -import org.eclipse.smarthome.core.thing.type.ChannelGroupTypeUID; -import org.eclipse.smarthome.core.thing.type.ChannelType; -import org.eclipse.smarthome.core.thing.type.ChannelTypeBuilder; -import org.eclipse.smarthome.core.thing.type.ChannelTypeProvider; -import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; -import org.eclipse.smarthome.core.types.StateDescription; -import org.eclipse.smarthome.core.types.StateOption; -import org.openhab.binding.yamahareceiver.internal.state.PresetInfoState; - -/** - * Provide a custom channel type for the preset channel - * - * @author David Graeff - Initial controbution - * @author Tomasz Maruszak - RX-V3900 compatibility improvements - */ -public class ChannelsTypeProviderPreset implements ChannelTypeProvider { - private ChannelType channelType; - private final ChannelTypeUID channelTypeUID; - - @Override - public Collection getChannelTypes(Locale locale) { - return Collections.singleton(channelType); - } - - @Override - public ChannelType getChannelType(ChannelTypeUID channelTypeUID, Locale locale) { - if (this.channelTypeUID.equals(channelTypeUID)) { - return channelType; - } else { - return null; - } - } - - @Override - public ChannelGroupType getChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID, Locale locale) { - return null; - } - - @Override - public Collection getChannelGroupTypes(Locale locale) { - return null; - } - - public ChannelTypeUID getChannelTypeUID() { - return channelTypeUID; - } - - public ChannelsTypeProviderPreset(ThingUID thing) { - /** - * We generate a thing specific channelTypeUID, because presets absolutely depends on the thing. - */ - channelTypeUID = new ChannelTypeUID(BINDING_ID, CHANNEL_PLAYBACK_PRESET_TYPE_NAMED + thing.getId()); - - StateDescription state = getDefaultStateDescription(); - createChannelType(state); - } - - private StateDescription getDefaultStateDescription() { - List options = IntStream.rangeClosed(1, 40) - .mapToObj(i -> new StateOption(Integer.toString(i), "Item_" + i)) - .collect(toList()); - - StateDescription state = new StateDescription(null, null, null, "%s", false, options); - return state; - } - - public void changePresetNames(List presets) { - List options = presets.stream() - .map(preset -> new StateOption(String.valueOf(preset.getValue()), preset.getName())) - .collect(toList()); - - StateDescription state = new StateDescription(null, null, null, "%s", false, options); - createChannelType(state); - } - - private void createChannelType(StateDescription state) { - channelType = ChannelTypeBuilder.state(channelTypeUID, "Preset", "Number") - .withDescription("Select a saved channel by its preset number").withStateDescription(state).build(); - } - -} diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/discovery/ZoneDiscoveryService.java b/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/discovery/ZoneDiscoveryService.java deleted file mode 100644 index d01f3fdfa5bb4..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/discovery/ZoneDiscoveryService.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.yamahareceiver.internal.discovery; - -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.ZONE_THING_TYPE; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Configs.CONFIG_ZONE; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; - -import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; -import org.eclipse.smarthome.config.discovery.DiscoveryResult; -import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; -import org.eclipse.smarthome.config.discovery.DiscoveryService; -import org.eclipse.smarthome.core.thing.ThingUID; -import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants; -import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone; -import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; - -/** - * After the AVR bridge thing has been added and a connection could be established, - * the user is presented with the available zones. - * - * @author David Gräff - Initial contribution - * @author Tomasz Maruszak - Introduced config object - */ -public class ZoneDiscoveryService extends AbstractDiscoveryService { - private final ServiceRegistration reg; - - /** - * Constructs a zone discovery service. - * Registers this zone discovery service programmatically. - * Call {@link ZoneDiscoveryService#destroy()} to unregister the service after use. - */ - public ZoneDiscoveryService(BundleContext bundleContext) { - super(YamahaReceiverBindingConstants.ZONE_THING_TYPES_UIDS, 0, false); - // Allow bundleContext to be null for the test suite - if (bundleContext != null) { - reg = bundleContext.registerService(DiscoveryService.class.getName(), this, new Hashtable<>()); - } else { - reg = null; - } - } - - /** - * Unregisters this service from the OSGi service registry. - * This object cannot be used aynmore after calling this method. - */ - public void destroy() { - if (reg != null) { - reg.unregister(); - } - } - - @Override - protected void startScan() { - } - - public static ThingUID zoneThing(ThingUID bridgeUid, String zoneName) { - return new ThingUID(ZONE_THING_TYPE, bridgeUid, zoneName); - } - - /** - * The available zones are within the {@link DeviceInformationState}. Will will publish those - * as things via this discovery service instance. - * - * @param state The device information state - * @param bridgeUid The bridge UID - */ - public void publishZones(DeviceInformationState state, ThingUID bridgeUid) { - // Create a copy of the list to avoid concurrent modification exceptions, because - // the state update takes place in another thread - List zoneCopy = new ArrayList<>(state.zones); - - for (Zone zone : zoneCopy) { - String zoneName = zone.name(); - ThingUID uid = zoneThing(bridgeUid, zoneName); - - Map properties = new HashMap<>(); - properties.put(CONFIG_ZONE, zoneName); - - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(uid).withProperties(properties) - .withLabel(state.name + " " + zoneName).withBridge(bridgeUid) - .withRepresentationProperty(CONFIG_ZONE).build(); - - thingDiscovered(discoveryResult); - } - } -} diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithPresetControl.java b/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithPresetControl.java deleted file mode 100644 index 38b2c15adf10f..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithPresetControl.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.yamahareceiver.internal.protocol; - -import java.io.IOException; -import java.util.Set; -import java.util.stream.Stream; - -import static java.util.stream.Collectors.toSet; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.*; - -/** - * The preset control protocol interface - * - * @author David Graeff - Initial contribution - * @author Tomasz Maruszak - Adding Spotify, Server to supported preset inputs - */ - -public interface InputWithPresetControl extends IStateUpdatable { - - int PRESET_CHANNELS = 40; - - /** - * List all inputs that are compatible with this kind of control - */ - Set SUPPORTED_INPUTS = Stream - .of( - INPUT_TUNER, - INPUT_NET_RADIO, - INPUT_NET_RADIO_LEGACY, - INPUT_USB, - INPUT_IPOD, - INPUT_IPOD_USB, - INPUT_DOCK, - INPUT_PC, - INPUT_NAPSTER, - INPUT_PANDORA, - INPUT_SIRIUS, - INPUT_RHAPSODY, - INPUT_BLUETOOTH, - INPUT_SPOTIFY, - INPUT_SERVER, - INPUT_HD_RADIO - ).collect(toSet()); - - /** - * Select a preset channel. - * - * @param presetChannel The preset position [1,40] - * @throws Exception - */ - void selectItemByPresetNumber(int presetChannel) throws IOException, ReceivedMessageParseException; -} diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ProtocolFactory.java b/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ProtocolFactory.java deleted file mode 100644 index 1d085b255a2fd..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ProtocolFactory.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.yamahareceiver.internal.protocol; - -import org.openhab.binding.yamahareceiver.internal.config.YamahaBridgeConfig; -import org.openhab.binding.yamahareceiver.internal.config.YamahaZoneConfig; -import org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConnection; -import org.openhab.binding.yamahareceiver.internal.state.*; - -import java.util.function.Supplier; - -/** - * Factory to create a {@link AbstractConnection} connection object based on a feature test. - * Also returns implementation objects for all the protocol interfaces. - *

- * At the moment only the XML protocol is supported. - * - * @author David Graeff - Initial contribution - * @author Tomasz Maruszak - Input mapping fix, refactoring - */ -public interface ProtocolFactory { - /** - * Asynchronous method to create and return a connection object. Depending - * on the feature test it might be either a {@link XMLConnection} or a JsonConnection. - * - * @param host The host name - * @param connectionStateListener - */ - void createConnection(String host, ConnectionStateListener connectionStateListener); - - - SystemControl SystemControl(AbstractConnection connection, - SystemControlStateListener listener, - DeviceInformationState deviceInformationState); - - InputWithPlayControl InputWithPlayControl(AbstractConnection connection, - String currentInputID, - PlayInfoStateListener listener, - YamahaBridgeConfig settings, - DeviceInformationState deviceInformationState); - - InputWithPresetControl InputWithPresetControl(AbstractConnection connection, - String currentInputID, - PresetInfoStateListener listener, - DeviceInformationState deviceInformationState); - - InputWithTunerBandControl InputWithDabBandControl(String currentInputID, - AbstractConnection connection, - DabBandStateListener observerForBand, - PresetInfoStateListener observerForPreset, - PlayInfoStateListener observerForPlayInfo, - DeviceInformationState deviceInformationState); - - InputWithNavigationControl InputWithNavigationControl(AbstractConnection connection, - NavigationControlState state, - String inputID, - NavigationControlStateListener observer, - DeviceInformationState deviceInformationState); - - ZoneControl ZoneControl(AbstractConnection connection, - YamahaZoneConfig zoneSettings, - ZoneControlStateListener listener, - Supplier inputConverterSupplier, - DeviceInformationState deviceInformationState); - - ZoneAvailableInputs ZoneAvailableInputs(AbstractConnection connection, - YamahaZoneConfig zoneSettings, - AvailableInputStateListener listener, - Supplier inputConverterSupplier, - DeviceInformationState deviceInformationState); - - DeviceInformation DeviceInformation(AbstractConnection connection, DeviceInformationState state); - - InputConverter InputConverter(AbstractConnection connection, String setting); -} diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLProtocolFactory.java b/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLProtocolFactory.java deleted file mode 100644 index 7d9af4f45a051..0000000000000 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLProtocolFactory.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.yamahareceiver.internal.protocol.xml; - -import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants; -import org.openhab.binding.yamahareceiver.internal.config.YamahaBridgeConfig; -import org.openhab.binding.yamahareceiver.internal.config.YamahaZoneConfig; -import org.openhab.binding.yamahareceiver.internal.protocol.*; -import org.openhab.binding.yamahareceiver.internal.state.*; - -import java.util.function.Supplier; - -/** - * Implementation of {@link ProtocolFactory} for XML protocol. - * - * @author Tomasz Maruszak - Initial contribution. - */ -public class XMLProtocolFactory implements ProtocolFactory { - public void createConnection(String host, ConnectionStateListener connectionStateListener) { - connectionStateListener.connectionEstablished(new XMLConnection(host)); - } - - public SystemControl SystemControl(AbstractConnection connection, - SystemControlStateListener listener, - DeviceInformationState deviceInformationState) { - - return new SystemControlXML(connection, listener, deviceInformationState); - } - - public InputWithPlayControl InputWithPlayControl(AbstractConnection connection, - String currentInputID, - PlayInfoStateListener listener, - YamahaBridgeConfig bridgeConfig, - DeviceInformationState deviceInformationState) { - - return new InputWithPlayControlXML(currentInputID, connection, listener, bridgeConfig, deviceInformationState); - } - - public InputWithPresetControl InputWithPresetControl(AbstractConnection connection, - String currentInputID, - PresetInfoStateListener listener, - DeviceInformationState deviceInformationState) { - - return new InputWithPresetControlXML(currentInputID, connection, listener, deviceInformationState); - } - - public InputWithTunerBandControl InputWithDabBandControl(String currentInputID, - AbstractConnection connection, - DabBandStateListener observerForBand, - PresetInfoStateListener observerForPreset, - PlayInfoStateListener observerForPlayInfo, - DeviceInformationState deviceInformationState) { - - return new InputWithTunerDABControlXML(currentInputID, connection, observerForBand, observerForPreset, observerForPlayInfo, deviceInformationState); - } - - public InputWithNavigationControl InputWithNavigationControl(AbstractConnection connection, - NavigationControlState state, - String inputID, - NavigationControlStateListener observer, - DeviceInformationState deviceInformationState) { - - return new InputWithNavigationControlXML(state, inputID, connection, observer, deviceInformationState); - } - - public ZoneControl ZoneControl(AbstractConnection connection, - YamahaZoneConfig zoneSettings, - ZoneControlStateListener listener, - Supplier inputConverterSupplier, - DeviceInformationState deviceInformationState) { - - if (isZoneB(zoneSettings.getZone(), deviceInformationState)) { - return new ZoneBControlXML(connection, zoneSettings, listener, deviceInformationState, inputConverterSupplier); - } - return new ZoneControlXML(connection, zoneSettings.getZone(), zoneSettings, listener, deviceInformationState, inputConverterSupplier); - } - - public ZoneAvailableInputs ZoneAvailableInputs(AbstractConnection connection, - YamahaZoneConfig zoneSettings, - AvailableInputStateListener listener, - Supplier inputConverterSupplier, - DeviceInformationState deviceInformationState) { - - if (isZoneB(zoneSettings.getZone(), deviceInformationState)) { - return new ZoneBAvailableInputsXML(connection, listener, inputConverterSupplier); - } - return new ZoneAvailableInputsXML(connection, zoneSettings.getZone(), listener, inputConverterSupplier); - } - - /** - * Checks if the specified Zone_2 should be emulated using Zone_B feature. - * - * @param zone - * @param deviceInformationState - * @return - */ - private boolean isZoneB(YamahaReceiverBindingConstants.Zone zone, DeviceInformationState deviceInformationState) { - return YamahaReceiverBindingConstants.Zone.Zone_2.equals(zone) && deviceInformationState.features.contains(YamahaReceiverBindingConstants.Feature.ZONE_B); - } - - public DeviceInformation DeviceInformation(AbstractConnection connection, - DeviceInformationState state) { - return new DeviceInformationXML(connection, state); - } - - public InputConverter InputConverter(AbstractConnection connection, - String setting) { - - return new InputConverterXML(connection, setting); - } - -} diff --git a/addons/binding/org.openhab.binding.zoneminder/.classpath b/addons/binding/org.openhab.binding.zoneminder/.classpath deleted file mode 100644 index 3801e00168a71..0000000000000 --- a/addons/binding/org.openhab.binding.zoneminder/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/addons/binding/org.openhab.binding.zoneminder/.project b/addons/binding/org.openhab.binding.zoneminder/.project deleted file mode 100644 index 514ade439918a..0000000000000 --- a/addons/binding/org.openhab.binding.zoneminder/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.binding.zoneminder - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/binding/org.openhab.binding.zoneminder/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.zoneminder/META-INF/MANIFEST.MF deleted file mode 100644 index d234fa386e02c..0000000000000 --- a/addons/binding/org.openhab.binding.zoneminder/META-INF/MANIFEST.MF +++ /dev/null @@ -1,35 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.binding.zoneminder -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: - ., - lib/gson-2.7.jar, - lib/guava-17.0.jar, - lib/jersey-common-2.4.1.jar, - lib/javax.ws.rs-api-2.0.1.jar, - lib/jsoup-1.10.1.jar, - lib/zoneminder4j-0.9.7.jar -Bundle-ManifestVersion: 2 -Bundle-Name: ZoneMinder Binding -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.binding.zoneminder;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - com.google.common.collect, - javax.ws.rs.client, - javax.ws.rs.core, - org.apache.commons.lang, - org.apache.commons.net.util, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.thing.binding.builder, - org.eclipse.smarthome.core.thing.type, - org.eclipse.smarthome.core.types, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/binding/org.openhab.binding.zoneminder/NOTICE b/addons/binding/org.openhab.binding.zoneminder/NOTICE deleted file mode 100644 index afcb520b20a12..0000000000000 --- a/addons/binding/org.openhab.binding.zoneminder/NOTICE +++ /dev/null @@ -1,45 +0,0 @@ -This content is produced and maintained by the openHAB project. - -* Project home: https://www.openhab.org - -== Declared Project Licenses - -This program and the accompanying materials are made available under the terms -of the Eclipse Public License 2.0 which is available at -https://www.eclipse.org/legal/epl-2.0/. - -== Source Code - -https://github.com/openhab/openhab2-addons - -== Third-party Content - -google-gson -* License: Apache 2.0 License -* Project: https://github.com/google/gson -* Source: https://github.com/google/gson - -guava -* License: Apache 2.0 License -* Project: https://github.com/google/guava -* Source: https://github.com/google/guava - -java.ws.rs-api -* License: CDDL Version 1.1 License + GPL v2 License -* Project: https://projects.eclipse.org/projects/ee4j.jaxrs -* Source: https://github.com/jax-rs/api - -jersey-common -* License: CDDL Version 1.1 License + GPL v2 License -* Project: https://projects.eclipse.org/projects/ee4j.jersey -* Source: https://github.com/jersey/jersey - -jsoup -* License: MIT License -* Project: https://jsoup.org/ -* Source: https://github.com/jhy/jsoup - -zoneminder4j -* License: MIT License -* Project: https://github.com/Mr-Eskildsen/zoneminder4j -* Source: https://github.com/Mr-Eskildsen/zoneminder4j diff --git a/addons/binding/org.openhab.binding.zoneminder/OSGI-INF/.gitignore b/addons/binding/org.openhab.binding.zoneminder/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/binding/org.openhab.binding.zoneminder/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/binding/org.openhab.binding.zoneminder/build.properties b/addons/binding/org.openhab.binding.zoneminder/build.properties deleted file mode 100644 index d44918ae7630d..0000000000000 --- a/addons/binding/org.openhab.binding.zoneminder/build.properties +++ /dev/null @@ -1,9 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - lib/,\ - NOTICE - diff --git a/addons/binding/org.openhab.binding.zoneminder/lib/gson-2.7.jar b/addons/binding/org.openhab.binding.zoneminder/lib/gson-2.7.jar deleted file mode 100644 index be5b59b764b56..0000000000000 Binary files a/addons/binding/org.openhab.binding.zoneminder/lib/gson-2.7.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.zoneminder/lib/guava-17.0.jar b/addons/binding/org.openhab.binding.zoneminder/lib/guava-17.0.jar deleted file mode 100644 index 661fc7473f876..0000000000000 Binary files a/addons/binding/org.openhab.binding.zoneminder/lib/guava-17.0.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.zoneminder/lib/javax.ws.rs-api-2.0.1.jar b/addons/binding/org.openhab.binding.zoneminder/lib/javax.ws.rs-api-2.0.1.jar deleted file mode 100644 index 7eb68b4a0abb8..0000000000000 Binary files a/addons/binding/org.openhab.binding.zoneminder/lib/javax.ws.rs-api-2.0.1.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.zoneminder/lib/jersey-common-2.4.1.jar b/addons/binding/org.openhab.binding.zoneminder/lib/jersey-common-2.4.1.jar deleted file mode 100644 index 50d5e5505b8e3..0000000000000 Binary files a/addons/binding/org.openhab.binding.zoneminder/lib/jersey-common-2.4.1.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.zoneminder/lib/jsoup-1.10.1.jar b/addons/binding/org.openhab.binding.zoneminder/lib/jsoup-1.10.1.jar deleted file mode 100644 index 6415d0c030e31..0000000000000 Binary files a/addons/binding/org.openhab.binding.zoneminder/lib/jsoup-1.10.1.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.zoneminder/pom.xml b/addons/binding/org.openhab.binding.zoneminder/pom.xml deleted file mode 100644 index c7eda54d53c93..0000000000000 --- a/addons/binding/org.openhab.binding.zoneminder/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.binding - pom - 2.5.0-SNAPSHOT - - - org.openhab.binding.zoneminder - eclipse-plugin - - ZoneMinder Binding - - diff --git a/addons/binding/pom.xml b/addons/binding/pom.xml index 5cbdbc8434204..1d64975057854 100644 --- a/addons/binding/pom.xml +++ b/addons/binding/pom.xml @@ -37,19 +37,6 @@ org.openhab.binding.ihc org.openhab.binding.ihc.test org.openhab.binding.innogysmarthome - org.openhab.binding.ipp - org.openhab.binding.jeelink - org.openhab.binding.supla - org.openhab.binding.keba - org.openhab.binding.km200 - org.openhab.binding.knx - org.openhab.binding.knx.test - org.openhab.binding.kodi - org.openhab.binding.konnected - org.openhab.binding.kostalinverter - org.openhab.binding.lametrictime - org.openhab.binding.leapmotion - org.openhab.binding.lgtvserial org.openhab.binding.lgwebos org.openhab.binding.loxone org.openhab.binding.modbus diff --git a/addons/io/org.openhab.io.azureiothub/.classpath b/addons/io/org.openhab.io.azureiothub/.classpath deleted file mode 100644 index b84c008e8112d..0000000000000 --- a/addons/io/org.openhab.io.azureiothub/.classpath +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/addons/io/org.openhab.io.azureiothub/.project b/addons/io/org.openhab.io.azureiothub/.project deleted file mode 100644 index 77749f9022b7f..0000000000000 --- a/addons/io/org.openhab.io.azureiothub/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.io.azureiothub - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/io/org.openhab.io.azureiothub/META-INF/MANIFEST.MF b/addons/io/org.openhab.io.azureiothub/META-INF/MANIFEST.MF deleted file mode 100644 index fa9c216f4ab6e..0000000000000 --- a/addons/io/org.openhab.io.azureiothub/META-INF/MANIFEST.MF +++ /dev/null @@ -1,55 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Azure IoT Hub Connector -Bundle-SymbolicName: org.openhab.io.azureiothub;singleton:=true -Bundle-Version: 2.5.0.qualifier -Service-Component: OSGI-INF/*.xml -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: - com.google.gson, - javax.net, - javax.net.ssl, - org.apache.commons.codec.binary, - org.apache.commons.io, - org.apache.commons.lang, - org.apache.log4j, - org.eclipse.emf.common.notify, - org.eclipse.emf.common.util, - org.eclipse.emf.ecore, - org.eclipse.emf.ecore.resource, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.jetty.client, - org.eclipse.jetty.client.api, - org.eclipse.jetty.client.util, - org.eclipse.jetty.http, - org.eclipse.jetty.io, - org.eclipse.jetty.util, - org.eclipse.jetty.util.component, - org.eclipse.paho.client.mqttv3, - org.eclipse.paho.client.mqttv3.logging, - org.eclipse.paho.client.mqttv3.persist, - org.eclipse.paho.client.mqttv3.util, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.common, - org.eclipse.smarthome.core.events, - org.eclipse.smarthome.core.id, - org.eclipse.smarthome.core.items, - org.eclipse.smarthome.core.items.events, - org.eclipse.smarthome.core.library.items, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.net, - org.eclipse.smarthome.core.persistence, - org.eclipse.smarthome.core.service, - org.eclipse.smarthome.core.types, - org.osgi.framework, - org.osgi.service.cm, - org.osgi.service.event, - org.slf4j -Bundle-ClassPath: ., - lib/azure-eventhubs-0.14.0.jar, - lib/iot-device-client-1.2.27.jar, - lib/iot-service-client-1.4.20.jar, - lib/javax.json-api-1.0.jar -Bundle-Vendor: openHAB -Bundle-ActivationPolicy: lazy -Automatic-Module-Name: org.openhab.io.azureiothub diff --git a/addons/io/org.openhab.io.azureiothub/OSGI-INF/.gitignore b/addons/io/org.openhab.io.azureiothub/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/io/org.openhab.io.azureiothub/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/io/org.openhab.io.azureiothub/build.properties b/addons/io/org.openhab.io.azureiothub/build.properties deleted file mode 100644 index 400b48143339b..0000000000000 --- a/addons/io/org.openhab.io.azureiothub/build.properties +++ /dev/null @@ -1,12 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - lib/,\ - NOTICE,\ - ESH-INF/,\ - lib/azure-eventhubs-0.14.0.jar,\ - lib/iot-device-client-1.2.27.jar,\ - lib/iot-service-client-1.4.20.jar,\ - lib/javax.json-api-1.0.jar diff --git a/addons/io/org.openhab.io.azureiothub/lib/azure-eventhubs-0.14.0.jar b/addons/io/org.openhab.io.azureiothub/lib/azure-eventhubs-0.14.0.jar deleted file mode 100644 index 464b1f1d82534..0000000000000 Binary files a/addons/io/org.openhab.io.azureiothub/lib/azure-eventhubs-0.14.0.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.azureiothub/lib/iot-device-client-1.2.27.jar b/addons/io/org.openhab.io.azureiothub/lib/iot-device-client-1.2.27.jar deleted file mode 100644 index f5aa1303b6bea..0000000000000 Binary files a/addons/io/org.openhab.io.azureiothub/lib/iot-device-client-1.2.27.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.azureiothub/lib/iot-service-client-1.4.20.jar b/addons/io/org.openhab.io.azureiothub/lib/iot-service-client-1.4.20.jar deleted file mode 100644 index c526c7a4141d2..0000000000000 Binary files a/addons/io/org.openhab.io.azureiothub/lib/iot-service-client-1.4.20.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.azureiothub/lib/javax.json-api-1.0.jar b/addons/io/org.openhab.io.azureiothub/lib/javax.json-api-1.0.jar deleted file mode 100644 index d276c793c70ec..0000000000000 Binary files a/addons/io/org.openhab.io.azureiothub/lib/javax.json-api-1.0.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.azureiothub/pom.xml b/addons/io/org.openhab.io.azureiothub/pom.xml deleted file mode 100644 index 428ce76ef0fcc..0000000000000 --- a/addons/io/org.openhab.io.azureiothub/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.io - pom - 2.5.0-SNAPSHOT - - - org.openhab.io.azureiothub - eclipse-plugin - - Azure IoT Hub Connector - - diff --git a/addons/io/org.openhab.io.homekit/.classpath b/addons/io/org.openhab.io.homekit/.classpath deleted file mode 100644 index 81b29a5ed88dc..0000000000000 --- a/addons/io/org.openhab.io.homekit/.classpath +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/addons/io/org.openhab.io.homekit/.project b/addons/io/org.openhab.io.homekit/.project deleted file mode 100644 index ba4fc9426c13d..0000000000000 --- a/addons/io/org.openhab.io.homekit/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.io.homekit - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/io/org.openhab.io.homekit/META-INF/MANIFEST.MF b/addons/io/org.openhab.io.homekit/META-INF/MANIFEST.MF deleted file mode 100644 index eb4994c429eee..0000000000000 --- a/addons/io/org.openhab.io.homekit/META-INF/MANIFEST.MF +++ /dev/null @@ -1,45 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.io.homekit -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: - ., - lib/com.beowulfe.hap.hap-1.1.4.jar, - lib/com.nimbusds.srp6a-1.5.2.jar, - lib/io.netty.netty-all-4.0.32.Final.jar, - lib/javax.json.javax.json-api-1.0.jar, - lib/net.i2p.crypto.eddsa-0.0.1-SNAPSHOT.jar, - lib/org.bouncycastle.bcprov-jdk15on-1.51.jar, - lib/org.glassfish.javax.json-1.0.4.jar, - lib/org.zeromq.curve25519-java-0.1.0.jar -Bundle-ManifestVersion: 2 -Bundle-Name: HomeKit Integration -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.io.homekit;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Export-Package: org.openhab.io.homekit -Import-Package: - com.google.common.collect, - org.apache.commons.io, - org.apache.commons.lang.builder, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.events, - org.eclipse.smarthome.core.items, - org.eclipse.smarthome.core.items.events, - org.eclipse.smarthome.core.library.items, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.storage, - org.eclipse.smarthome.core.thing, - org.eclipse.smarthome.core.thing.binding, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.io.console, - org.eclipse.smarthome.io.console.extensions, - org.openhab.io.homekit, - org.osgi.framework, - org.osgi.service.component, - org.osgi.service.component.annotations;resolution:=optional, - org.osgi.service.event, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/io/org.openhab.io.homekit/OSGI-INF/.gitignore b/addons/io/org.openhab.io.homekit/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/io/org.openhab.io.homekit/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/io/org.openhab.io.homekit/build.properties b/addons/io/org.openhab.io.homekit/build.properties deleted file mode 100644 index f3be433de89ae..0000000000000 --- a/addons/io/org.openhab.io.homekit/build.properties +++ /dev/null @@ -1,16 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - lib/com.beowulfe.hap.hap-1.1.4.jar,\ - lib/com.nimbusds.srp6a-1.5.2.jar,\ - lib/io.netty.netty-all-4.0.32.Final.jar,\ - lib/javax.json.javax.json-api-1.0.jar,\ - lib/net.i2p.crypto.eddsa-0.0.1-SNAPSHOT.jar,\ - lib/org.bouncycastle.bcprov-jdk15on-1.51.jar,\ - lib/org.glassfish.javax.json-1.0.4.jar,\ - lib/org.zeromq.curve25519-java-0.1.0.jar,\ - NOTICE,\ - ESH-INF/ - diff --git a/addons/io/org.openhab.io.homekit/lib/com.beowulfe.hap.hap-1.1.4.jar b/addons/io/org.openhab.io.homekit/lib/com.beowulfe.hap.hap-1.1.4.jar deleted file mode 100644 index eb96097fb3dd2..0000000000000 Binary files a/addons/io/org.openhab.io.homekit/lib/com.beowulfe.hap.hap-1.1.4.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.homekit/lib/com.nimbusds.srp6a-1.5.2.jar b/addons/io/org.openhab.io.homekit/lib/com.nimbusds.srp6a-1.5.2.jar deleted file mode 100644 index df777eb589fd1..0000000000000 Binary files a/addons/io/org.openhab.io.homekit/lib/com.nimbusds.srp6a-1.5.2.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.homekit/lib/io.netty.netty-all-4.0.32.Final.jar b/addons/io/org.openhab.io.homekit/lib/io.netty.netty-all-4.0.32.Final.jar deleted file mode 100644 index 37acee598a20b..0000000000000 Binary files a/addons/io/org.openhab.io.homekit/lib/io.netty.netty-all-4.0.32.Final.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.homekit/lib/javax.json.javax.json-api-1.0.jar b/addons/io/org.openhab.io.homekit/lib/javax.json.javax.json-api-1.0.jar deleted file mode 100644 index d276c793c70ec..0000000000000 Binary files a/addons/io/org.openhab.io.homekit/lib/javax.json.javax.json-api-1.0.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.homekit/lib/net.i2p.crypto.eddsa-0.0.1-SNAPSHOT.jar b/addons/io/org.openhab.io.homekit/lib/net.i2p.crypto.eddsa-0.0.1-SNAPSHOT.jar deleted file mode 100644 index 2d01daa223d4a..0000000000000 Binary files a/addons/io/org.openhab.io.homekit/lib/net.i2p.crypto.eddsa-0.0.1-SNAPSHOT.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.homekit/lib/org.bouncycastle.bcprov-jdk15on-1.51.jar b/addons/io/org.openhab.io.homekit/lib/org.bouncycastle.bcprov-jdk15on-1.51.jar deleted file mode 100644 index 4076e11661ae2..0000000000000 Binary files a/addons/io/org.openhab.io.homekit/lib/org.bouncycastle.bcprov-jdk15on-1.51.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.homekit/lib/org.glassfish.javax.json-1.0.4.jar b/addons/io/org.openhab.io.homekit/lib/org.glassfish.javax.json-1.0.4.jar deleted file mode 100644 index 09967d8158efd..0000000000000 Binary files a/addons/io/org.openhab.io.homekit/lib/org.glassfish.javax.json-1.0.4.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.homekit/lib/org.zeromq.curve25519-java-0.1.0.jar b/addons/io/org.openhab.io.homekit/lib/org.zeromq.curve25519-java-0.1.0.jar deleted file mode 100644 index 94785c53fc858..0000000000000 Binary files a/addons/io/org.openhab.io.homekit/lib/org.zeromq.curve25519-java-0.1.0.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.homekit/pom.xml b/addons/io/org.openhab.io.homekit/pom.xml deleted file mode 100644 index e5776a15c8c5f..0000000000000 --- a/addons/io/org.openhab.io.homekit/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.io - pom - 2.5.0-SNAPSHOT - - - org.openhab.io.homekit - eclipse-plugin - - HomeKit Integration - - diff --git a/addons/io/org.openhab.io.hueemulation.test/.classpath b/addons/io/org.openhab.io.hueemulation.test/.classpath deleted file mode 100644 index 64777f25c82a4..0000000000000 --- a/addons/io/org.openhab.io.hueemulation.test/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/addons/io/org.openhab.io.hueemulation.test/.project b/addons/io/org.openhab.io.hueemulation.test/.project deleted file mode 100644 index 60eb0243e64cf..0000000000000 --- a/addons/io/org.openhab.io.hueemulation.test/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.openhab.io.hueemulation.test - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/io/org.openhab.io.hueemulation.test/META-INF/MANIFEST.MF b/addons/io/org.openhab.io.hueemulation.test/META-INF/MANIFEST.MF deleted file mode 100644 index 95b8f619d3004..0000000000000 --- a/addons/io/org.openhab.io.hueemulation.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,32 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.io.hueemulation.test -Bundle-ManifestVersion: 2 -Bundle-Name: Hue Emulation Service Tests -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.io.hueemulation.test;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Fragment-Host: org.openhab.io.hueemulation -Import-Package: - com.google.gson, - com.google.gson.reflect, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.jetty.http, - org.eclipse.jetty.util.component, - org.eclipse.smarthome.core.common, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.events, - org.eclipse.smarthome.core.items, - org.eclipse.smarthome.core.library.items, - org.eclipse.smarthome.test, - org.eclipse.smarthome.test.java, - org.eclipse.smarthome.test.storage, - org.hamcrest;core=split, - org.hamcrest.core, - org.junit, - org.mockito, - org.mockito.invocation, - org.mockito.stubbing, - org.mockito.verification, - org.osgi.service.device, - org.osgi.service.http diff --git a/addons/io/org.openhab.io.hueemulation.test/NOTICE b/addons/io/org.openhab.io.hueemulation.test/NOTICE deleted file mode 100644 index 4c20ef446c1e4..0000000000000 --- a/addons/io/org.openhab.io.hueemulation.test/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -This content is produced and maintained by the openHAB project. - -* Project home: https://www.openhab.org - -== Declared Project Licenses - -This program and the accompanying materials are made available under the terms -of the Eclipse Public License 2.0 which is available at -https://www.eclipse.org/legal/epl-2.0/. - -== Source Code - -https://github.com/openhab/openhab2-addons diff --git a/addons/io/org.openhab.io.hueemulation.test/build.properties b/addons/io/org.openhab.io.hueemulation.test/build.properties deleted file mode 100644 index f5e8e6f0430f8..0000000000000 --- a/addons/io/org.openhab.io.hueemulation.test/build.properties +++ /dev/null @@ -1,6 +0,0 @@ -source.. = src/test/java/ -output.. = target/classes/ -bin.includes = META-INF/,\ - .,\ - NOTICE,\ - NOTICE diff --git a/addons/io/org.openhab.io.hueemulation.test/org.openhab.io.hueemulation.test.launch b/addons/io/org.openhab.io.hueemulation.test/org.openhab.io.hueemulation.test.launch deleted file mode 100644 index a48234bc1a6c9..0000000000000 --- a/addons/io/org.openhab.io.hueemulation.test/org.openhab.io.hueemulation.test.launch +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/io/org.openhab.io.hueemulation.test/pom.xml b/addons/io/org.openhab.io.hueemulation.test/pom.xml deleted file mode 100644 index bb8bda29cf1b3..0000000000000 --- a/addons/io/org.openhab.io.hueemulation.test/pom.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - 4.0.0 - - - org.openhab.io - pom - 2.5.0-SNAPSHOT - - - org.openhab.io.hueemulation.test - eclipse-test-plugin - Hue Emulation Service Tests - - - - - ${tycho-groupid} - tycho-surefire-plugin - - junit47 - - 4 - true - - - - - eclipse-plugin - org.eclipse.equinox.ds - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.event - 0.0.0 - - - eclipse-plugin - org.eclipse.osgi.services - 0.0.0 - - - - eclipse-plugin - ch.qos.logback.classic - 0.0.0 - - - eclipse-plugin - ch.qos.logback.core - 0.0.0 - - - eclipse-plugin - ch.qos.logback.slf4j - 0.0.0 - - - - eclipse-plugin - org.eclipse.equinox.http.jetty - 0.0.0 - - - eclipse-plugin - org.eclipse.equinox.http.servlet - 0.0.0 - - - - eclipse-plugin - org.openhab.core.config.xml - 0.0.0 - - - eclipse-plugin - org.openhab.core.thing - 0.0.0 - - - eclipse-plugin - org.openhab.core.thing.xml - 0.0.0 - - - eclipse-plugin - org.openhab.core.binding.xml - 0.0.0 - - - - - org.eclipse.equinox.ds - 1 - true - - - org.eclipse.equinox.http.servlet - 3 - true - - - org.eclipse.equinox.http.jetty - 3 - true - - - org.eclipse.equinox.event - 2 - true - - - -Dorg.osgi.service.http.port=9090 -Dprm.platform.capability.ClientPlatformID=notSimulator - false - - notSimulator - - - - - - diff --git a/addons/io/org.openhab.io.hueemulation.test/src/test/java/org/openhab/io/hueemulation/internal/HueEmulationServiceOSGiTest.java b/addons/io/org.openhab.io.hueemulation.test/src/test/java/org/openhab/io/hueemulation/internal/HueEmulationServiceOSGiTest.java deleted file mode 100644 index 1b10afe1c8e4f..0000000000000 --- a/addons/io/org.openhab.io.hueemulation.test/src/test/java/org/openhab/io/hueemulation/internal/HueEmulationServiceOSGiTest.java +++ /dev/null @@ -1,346 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal; - -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.assertThat; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.*; - -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.InetAddress; -import java.net.URL; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -import org.eclipse.smarthome.core.events.EventPublisher; -import org.eclipse.smarthome.core.items.GroupItem; -import org.eclipse.smarthome.core.items.Item; -import org.eclipse.smarthome.core.items.ItemRegistry; -import org.eclipse.smarthome.core.items.events.ItemCommandEvent; -import org.eclipse.smarthome.core.library.types.HSBType; -import org.eclipse.smarthome.core.library.types.OnOffType; -import org.eclipse.smarthome.core.service.ReadyMarker; -import org.eclipse.smarthome.core.service.ReadyService; -import org.eclipse.smarthome.test.java.JavaOSGiTest; -import org.eclipse.smarthome.test.storage.VolatileStorageService; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.openhab.io.hueemulation.internal.dto.HueDevice; -import org.openhab.io.hueemulation.internal.dto.HueStateColorBulb; -import org.openhab.io.hueemulation.internal.dto.HueUnauthorizedConfig; -import org.openhab.io.hueemulation.internal.dto.HueUserAuth; -import org.osgi.service.cm.ConfigurationAdmin; - -import com.google.gson.Gson; - -/** - * Integration tests for {@link HueEmulationService}. - * - * @author David Graeff - Initial contribution - */ -public class HueEmulationServiceOSGiTest extends JavaOSGiTest { - private HueEmulationService hueService; - VolatileStorageService volatileStorageService = new VolatileStorageService(); - - ItemRegistry itemRegistry; - @Mock - ConfigurationAdmin configurationAdmin; - - @Mock - EventPublisher eventPublisher; - - @Mock - Item item; - - String host; - - @SuppressWarnings("null") - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - registerService(volatileStorageService); - registerService(configurationAdmin); - - itemRegistry = getService(ItemRegistry.class, ItemRegistry.class); - assertThat(itemRegistry, notNullValue()); - ReadyService readyService = getService(ReadyService.class, ReadyService.class); - assertThat(readyService, notNullValue()); - hueService = getService(HueEmulationService.class, HueEmulationService.class); - assertThat(hueService, notNullValue()); - - when(item.getName()).thenReturn("itemname"); - - hueService.setEventPublisher(eventPublisher); - - readyService.markReady(new ReadyMarker("fake", "org.eclipse.smarthome.model.core")); - waitFor(() -> hueService.discovery != null, 5000, 100); - assertThat(hueService.started, is(true)); - - InetAddress address = hueService.discovery.getAddress(); - host = "http://" + address.getHostAddress() + ":" + String.valueOf(hueService.discovery.getWebPort()); - } - - @After - public void tearDown() { - unregisterService(volatileStorageService); - } - - @SuppressWarnings("null") - private String read(HttpURLConnection urlConnection) throws IOException { - String result = ""; - final InputStream _is; - if (urlConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) { - _is = urlConnection.getInputStream(); - } else { - /* error from server */ - _is = urlConnection.getErrorStream(); - } - try (InputStream in = new BufferedInputStream(_is)) { - if (in != null) { - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in)); - String line = ""; - - while ((line = bufferedReader.readLine()) != null) { - result += line; - } - } - } - return result; - } - - @Test - public void UnauthorizedAccessTest() - throws InterruptedException, ExecutionException, TimeoutException, IOException { - - // upnp response - HttpURLConnection c = (HttpURLConnection) new URL(host + "/description.xml").openConnection(); - assertThat(c.getResponseCode(), is(200)); - String body = read(c); - assertThat(body, containsString(hueService.ds.config.uuid)); - - // Unauthorized config access - c = (HttpURLConnection) new URL(host + "/api/config").openConnection(); - assertThat(c.getResponseCode(), is(200)); - body = read(c); - HueUnauthorizedConfig config = new Gson().fromJson(body, HueUnauthorizedConfig.class); - assertThat(config.bridgeid, is(hueService.ds.config.bridgeid)); - assertThat(config.name, is(hueService.ds.config.name)); - - // Invalid user name - c = (HttpURLConnection) new URL(host + "/api/invalid/lights").openConnection(); - assertThat(c.getResponseCode(), is(403)); - body = read(c); - assertThat(body, containsString("error")); - - // Add user name (no link button) - body = "{'username':'testuser','devicetype':'label'}"; - c = (HttpURLConnection) new URL(host + "/api").openConnection(); - c.setRequestProperty("Content-Type", "application/json"); - c.setRequestMethod("POST"); - c.setDoOutput(true); - c.getOutputStream().write(body.getBytes(), 0, body.getBytes().length); - assertThat(c.getResponseCode(), is(403)); - - // Add user name (link button) - hueService.ds.config.linkbutton = true; - c = (HttpURLConnection) new URL(host + "/api").openConnection(); - c.setRequestProperty("Content-Type", "application/json"); - c.setRequestMethod("POST"); - c.setDoOutput(true); - c.getOutputStream().write(body.getBytes(), 0, body.getBytes().length); - assertThat(c.getResponseCode(), is(200)); - body = read(c); - assertThat(body, containsString("success")); - assertThat(hueService.ds.config.whitelist.get("testuser").name, is("label")); - hueService.ds.config.whitelist.clear(); - - // Add user name without proposing one (the bridge generates one) - body = "{'devicetype':'label'}"; - c = (HttpURLConnection) new URL(host + "/api").openConnection(); - c.setRequestProperty("Content-Type", "application/json"); - c.setRequestMethod("POST"); - c.setDoOutput(true); - c.getOutputStream().write(body.getBytes(), 0, body.getBytes().length); - assertThat(c.getResponseCode(), is(200)); - body = read(c); - assertThat(body, containsString("success")); - assertThat(body, containsString(hueService.ds.config.whitelist.keySet().iterator().next())); - } - - @Test - public void LightsTest() throws InterruptedException, ExecutionException, TimeoutException, IOException { - HttpURLConnection c; - String body; - - hueService.ds.config.whitelist.put("testuser", new HueUserAuth("testUserLabel")); - - c = (HttpURLConnection) new URL(host + "/api/testuser/lights").openConnection(); - assertThat(c.getResponseCode(), is(200)); - body = read(c); - assertThat(body, containsString("{}")); - - hueService.ds.lights.put(1, new HueDevice(item, "switch", DeviceType.SwitchType)); - hueService.ds.lights.put(2, new HueDevice(item, "color", DeviceType.ColorType)); - hueService.ds.lights.put(3, new HueDevice(item, "white", DeviceType.WhiteTemperatureType)); - - // Full access test - c = (HttpURLConnection) new URL(host + "/api/testuser/lights").openConnection(); - assertThat(c.getResponseCode(), is(200)); - body = read(c); - assertThat(body, containsString("switch")); - assertThat(body, containsString("color")); - assertThat(body, containsString("white")); - - // Single light access test - c = (HttpURLConnection) new URL(host + "/api/testuser/lights/2").openConnection(); - assertThat(c.getResponseCode(), is(200)); - body = read(c); - assertThat(body, containsString("color")); - } - - @Test - public void DebugTest() throws InterruptedException, ExecutionException, TimeoutException, IOException { - HttpURLConnection c; - String body; - - hueService.ds.config.whitelist.put("testuser", new HueUserAuth("testUserLabel")); - hueService.ds.lights.put(2, new HueDevice(item, "color", DeviceType.ColorType)); - - c = (HttpURLConnection) new URL(host + "/api/testuser/lights?debug=true").openConnection(); - assertThat(c.getResponseCode(), is(200)); - body = read(c); - assertThat(body, containsString("Exposed lights")); - } - - @Test - public void LightGroupItemSwitchTest() - throws InterruptedException, ExecutionException, TimeoutException, IOException { - HttpURLConnection c; - String body; - - GroupItem gitem = new GroupItem("group", item); - hueService.ds.config.whitelist.put("testuser", new HueUserAuth("testUserLabel")); - hueService.ds.lights.put(7, new HueDevice(gitem, "switch", DeviceType.SwitchType)); - - body = "{'on':true}"; - c = (HttpURLConnection) new URL(host + "/api/testuser/lights/7/state").openConnection(); - c.setRequestProperty("Content-Type", "application/json"); - c.setRequestMethod("PUT"); - c.setDoOutput(true); - c.getOutputStream().write(body.getBytes(), 0, body.getBytes().length); - assertThat(c.getResponseCode(), is(200)); - body = read(c); - assertThat(body, containsString("success")); - assertThat(body, containsString("on")); - - verify(eventPublisher).post(argThat(ce -> assertOnValue((ItemCommandEvent) ce, true))); - } - - @Test - public void LightHueTest() throws InterruptedException, ExecutionException, TimeoutException, IOException { - HttpURLConnection c; - String body; - - hueService.ds.config.whitelist.put("testuser", new HueUserAuth("testUserLabel")); - hueService.ds.lights.put(2, new HueDevice(item, "color", DeviceType.ColorType)); - - body = "{'hue':1000}"; - c = (HttpURLConnection) new URL(host + "/api/testuser/lights/2/state").openConnection(); - c.setRequestProperty("Content-Type", "application/json"); - c.setRequestMethod("PUT"); - c.setDoOutput(true); - c.getOutputStream().write(body.getBytes(), 0, body.getBytes().length); - assertThat(c.getResponseCode(), is(200)); - body = read(c); - assertThat(body, containsString("success")); - assertThat(body, containsString("hue")); - - verify(eventPublisher).post(argThat(ce -> assertHueValue((ItemCommandEvent) ce, 1000))); - } - - @Test - public void LightSaturationTest() throws InterruptedException, ExecutionException, TimeoutException, IOException { - HttpURLConnection c; - String body; - - hueService.ds.config.whitelist.put("testuser", new HueUserAuth("testUserLabel")); - hueService.ds.lights.put(2, new HueDevice(item, "color", DeviceType.ColorType)); - - body = "{'sat':50}"; - c = (HttpURLConnection) new URL(host + "/api/testuser/lights/2/state").openConnection(); - c.setRequestProperty("Content-Type", "application/json"); - c.setRequestMethod("PUT"); - c.setDoOutput(true); - c.getOutputStream().write(body.getBytes(), 0, body.getBytes().length); - assertThat(c.getResponseCode(), is(200)); - body = read(c); - assertThat(body, containsString("success")); - assertThat(body, containsString("sat")); - - verify(eventPublisher).post(argThat(ce -> assertSatValue((ItemCommandEvent) ce, 50))); - } - - /** - * Amazon echos are setting ct only, if commanded to turn a light white. - */ - @Test - public void LightToWhiteTest() throws InterruptedException, ExecutionException, TimeoutException, IOException { - HttpURLConnection c; - String body; - - // We start with a coloured state - when(item.getState()).thenReturn(new HSBType("100,100,100")); - hueService.ds.config.whitelist.put("testuser", new HueUserAuth("testUserLabel")); - hueService.ds.lights.put(2, new HueDevice(item, "color", DeviceType.ColorType)); - - body = "{'ct':500}"; - c = (HttpURLConnection) new URL(host + "/api/testuser/lights/2/state").openConnection(); - c.setRequestProperty("Content-Type", "application/json"); - c.setRequestMethod("PUT"); - c.setDoOutput(true); - c.getOutputStream().write(body.getBytes(), 0, body.getBytes().length); - assertThat(c.getResponseCode(), is(200)); - body = read(c); - assertThat(body, containsString("success")); - assertThat(body, containsString("sat")); - assertThat(body, containsString("ct")); - - // Saturation is expected to be 0 -> white light - verify(eventPublisher).post(argThat(ce -> assertSatValue((ItemCommandEvent) ce, 0))); - } - - private boolean assertHueValue(ItemCommandEvent ce, int hueValue) { - assertThat(((HSBType) ce.getItemCommand()).getHue().intValue(), is(hueValue * 360 / HueStateColorBulb.MAX_HUE)); - return true; - } - - private boolean assertSatValue(ItemCommandEvent ce, int satValue) { - assertThat(((HSBType) ce.getItemCommand()).getSaturation().intValue(), - is(satValue * 100 / HueStateColorBulb.MAX_SAT)); - return true; - } - - private boolean assertOnValue(ItemCommandEvent ce, boolean value) { - assertThat(ce.getItemCommand(), is(OnOffType.from(value))); - return true; - } -} diff --git a/addons/io/org.openhab.io.hueemulation.test/src/test/java/org/openhab/io/hueemulation/internal/HueRestAPITest.java b/addons/io/org.openhab.io.hueemulation.test/src/test/java/org/openhab/io/hueemulation/internal/HueRestAPITest.java deleted file mode 100644 index 727df5f1ef435..0000000000000 --- a/addons/io/org.openhab.io.hueemulation.test/src/test/java/org/openhab/io/hueemulation/internal/HueRestAPITest.java +++ /dev/null @@ -1,190 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal; - -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.*; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.nio.file.Paths; - -import javax.servlet.http.HttpServletRequest; - -import org.eclipse.smarthome.core.events.Event; -import org.eclipse.smarthome.core.events.EventPublisher; -import org.eclipse.smarthome.core.items.GroupItem; -import org.eclipse.smarthome.core.library.items.ColorItem; -import org.eclipse.smarthome.core.library.items.SwitchItem; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.openhab.io.hueemulation.internal.RESTApi.HttpMethod; -import org.openhab.io.hueemulation.internal.dto.HueDataStore; -import org.openhab.io.hueemulation.internal.dto.HueDevice; -import org.openhab.io.hueemulation.internal.dto.HueStateColorBulb; -import org.openhab.io.hueemulation.internal.dto.HueStatePlug; -import org.openhab.io.hueemulation.internal.dto.HueUserAuth; - -import com.google.gson.Gson; - -/** - * Tests for {@link RESTApi}. - * - * @author David Graeff - Initial contribution - */ -public class HueRestAPITest { - - private Gson gson; - private HueDataStore ds; - private RESTApi restAPI; - private UserManagement userManagement; - private ConfigManagement configManagement; - @Mock - private EventPublisher eventPublisher; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - gson = new Gson(); - ds = new HueDataStore(); - userManagement = spy(new UserManagement(ds)); - configManagement = spy(new ConfigManagement(ds)); - restAPI = spy(new RESTApi(ds, userManagement, configManagement, gson)); - restAPI.setEventPublisher(eventPublisher); - - // Add simulated lights - ds.lights.put(1, new HueDevice(new SwitchItem("switch"), "switch", DeviceType.SwitchType)); - ds.lights.put(2, new HueDevice(new ColorItem("color"), "color", DeviceType.ColorType)); - ds.lights.put(3, new HueDevice(new ColorItem("white"), "white", DeviceType.WhiteTemperatureType)); - - // Add group item - ds.lights.put(10, - new HueDevice(new GroupItem("white", new SwitchItem("switch")), "white", DeviceType.SwitchType)); - } - - @Test - public void invalidUser() throws IOException { - PrintWriter out = mock(PrintWriter.class); - int result = restAPI.handleUser(HttpMethod.GET, "", out, "testuser", Paths.get(""), Paths.get(""), false); - assertEquals(403, result); - } - - @Test - public void validUser() throws IOException { - PrintWriter out = mock(PrintWriter.class); - ds.config.whitelist.put("testuser", new HueUserAuth("testuser")); - int result = restAPI.handleUser(HttpMethod.GET, "", out, "testuser", Paths.get("/"), Paths.get(""), false); - assertEquals(200, result); - } - - @Test - public void addUser() throws IOException { - PrintWriter out = mock(PrintWriter.class); - HttpServletRequest req = mock(HttpServletRequest.class); - - // GET should fail - int result = restAPI.handle(HttpMethod.GET, "", out, Paths.get("/api"), false); - assertEquals(405, result); - - // Post should create a user, except: if linkbutton not enabled - result = restAPI.handle(HttpMethod.POST, "", out, Paths.get("/api"), false); - assertEquals(10403, result); - - // Post should create a user - ds.config.linkbutton = true; - when(req.getMethod()).thenReturn("POST"); - String body = "{'username':'testuser','devicetype':'user-label'}"; - result = restAPI.handle(HttpMethod.POST, body, out, Paths.get("/api"), false); - assertEquals(result, 200); - assertThat(ds.config.whitelist.get("testuser").name, is("user-label")); - } - - @Test - public void changeSwitchState() throws IOException { - ds.config.whitelist.put("testuser", new HueUserAuth("testuser")); - - assertThat(((HueStatePlug) ds.lights.get(1).state).on, is(false)); - - StringWriter out = new StringWriter(); - String body = "{'on':true}"; - int result = restAPI.handle(HttpMethod.PUT, body, out, Paths.get("/api/testuser/lights/1/state"), false); - assertEquals(200, result); - assertThat(out.toString(), containsString("success")); - assertThat(((HueStatePlug) ds.lights.get(1).state).on, is(true)); - verify(eventPublisher).post(argThat((Event t) -> { - assertThat(t.getPayload(), is("{\"type\":\"OnOff\",\"value\":\"ON\"}")); - return true; - })); - } - - @Test - public void changeGroupItemSwitchState() throws IOException { - ds.config.whitelist.put("testuser", new HueUserAuth("testuser")); - - assertThat(((HueStatePlug) ds.lights.get(10).state).on, is(false)); - - StringWriter out = new StringWriter(); - String body = "{'on':true}"; - int result = restAPI.handle(HttpMethod.PUT, body, out, Paths.get("/api/testuser/lights/10/state"), false); - assertEquals(200, result); - assertThat(out.toString(), containsString("success")); - assertThat(((HueStatePlug) ds.lights.get(10).state).on, is(true)); - verify(eventPublisher).post(argThat((Event t) -> { - assertThat(t.getPayload(), is("{\"type\":\"OnOff\",\"value\":\"ON\"}")); - return true; - })); - } - - @Test - public void changeOnAndBriValues() throws IOException { - ds.config.whitelist.put("testuser", new HueUserAuth("testuser")); - - assertThat(((HueStateColorBulb) ds.lights.get(2).state).on, is(false)); - assertThat(((HueStateColorBulb) ds.lights.get(2).state).bri, is(0)); - - String body = "{'on':true,'bri':200}"; - StringWriter out = new StringWriter(); - int result = restAPI.handle(HttpMethod.PUT, body, out, Paths.get("/api/testuser/lights/2/state"), false); - assertEquals(200, result); - assertThat(out.toString(), containsString("success")); - assertThat(((HueStateColorBulb) ds.lights.get(2).state).on, is(true)); - assertThat(((HueStateColorBulb) ds.lights.get(2).state).bri, is(200)); - } - - @Test - public void switchOnWithXY() throws IOException { - ds.config.whitelist.put("testuser", new HueUserAuth("testuser")); - - assertThat(((HueStateColorBulb) ds.lights.get(2).state).on, is(false)); - assertThat(((HueStateColorBulb) ds.lights.get(2).state).bri, is(0)); - - String body = "{'on':true,'bri':200,'xy':[0.5119,0.4147]}"; - StringWriter out = new StringWriter(); - int result = restAPI.handle(HttpMethod.PUT, body, out, Paths.get("/api/testuser/lights/2/state"), false); - assertEquals(200, result); - assertThat(out.toString(), containsString("success")); - assertThat(((HueStateColorBulb) ds.lights.get(2).state).on, is(true)); - assertThat(((HueStateColorBulb) ds.lights.get(2).state).bri, is(200)); - assertThat(((HueStateColorBulb) ds.lights.get(2).state).xy[0], is(0.5119)); - assertThat(((HueStateColorBulb) ds.lights.get(2).state).xy[1], is(0.4147)); - assertThat(((HueStateColorBulb) ds.lights.get(2).state).colormode, is(HueStateColorBulb.ColorMode.xy)); - assertThat(((HueStateColorBulb) ds.lights.get(2).state).toHSBType().getHue().intValue(), is((int)27.47722590981918)); - assertThat(((HueStateColorBulb) ds.lights.get(2).state).toHSBType().getSaturation().intValue(), is(88)); - assertThat(((HueStateColorBulb) ds.lights.get(2).state).toHSBType().getBrightness().intValue(), is(78)); - } -} diff --git a/addons/io/org.openhab.io.hueemulation.test/src/test/java/org/openhab/io/hueemulation/internal/LightItemsTest.java b/addons/io/org.openhab.io.hueemulation.test/src/test/java/org/openhab/io/hueemulation/internal/LightItemsTest.java deleted file mode 100644 index adc6e6c2c7635..0000000000000 --- a/addons/io/org.openhab.io.hueemulation.test/src/test/java/org/openhab/io/hueemulation/internal/LightItemsTest.java +++ /dev/null @@ -1,166 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal; - -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.assertThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -import java.io.IOException; -import java.util.Collections; -import java.util.Map; -import java.util.TreeMap; - -import org.eclipse.smarthome.core.items.GroupItem; -import org.eclipse.smarthome.core.items.ItemRegistry; -import org.eclipse.smarthome.core.library.items.SwitchItem; -import org.eclipse.smarthome.core.storage.Storage; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.openhab.io.hueemulation.internal.dto.HueDataStore; -import org.openhab.io.hueemulation.internal.dto.HueDevice; -import org.openhab.io.hueemulation.internal.dto.HueStatePlug; - -import com.google.gson.Gson; - -/** - * Tests for {@link LightItems}. - * - * @author David Graeff - Initial contribution - */ -public class LightItemsTest { - private Gson gson; - private HueDataStore ds; - private LightItems lightItems; - - @Mock - private ItemRegistry itemRegistry; - - @Mock - Storage storage; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - when(itemRegistry.getItems()).thenReturn(Collections.emptyList()); - gson = new Gson(); - ds = new HueDataStore(); - lightItems = spy(new LightItems(ds)); - lightItems.setItemRegistry(itemRegistry); - lightItems.setFilterTags(Collections.singleton("Switchable"), Collections.singleton("ColorLighting"), - Collections.singleton("Lighting")); - verify(itemRegistry).getItems(); - } - - @Test - public void loadStorage() throws IOException { - Map itemUIDtoHueID = new TreeMap<>(); - itemUIDtoHueID.put("switch1", 12); - when(storage.getKeys()).thenReturn(itemUIDtoHueID.keySet()); - when(storage.get(eq("switch1"))).thenReturn(itemUIDtoHueID.get("switch1")); - lightItems.loadMappingFromFile(storage); - } - - @Test - public void addSwitchableByCategory() throws IOException { - SwitchItem item = new SwitchItem("switch1"); - item.setCategory("Light"); - lightItems.added(item); - HueDevice device = ds.lights.get(lightItems.itemUIDtoHueID.get("switch1")); - assertThat(device.item, is(item)); - assertThat(device.state, is(instanceOf(HueStatePlug.class))); - - } - - @Test - public void addSwitchableByTag() throws IOException { - SwitchItem item = new SwitchItem("switch1"); - item.addTag("Switchable"); - lightItems.added(item); - HueDevice device = ds.lights.get(lightItems.itemUIDtoHueID.get("switch1")); - assertThat(device.item, is(item)); - assertThat(device.state, is(instanceOf(HueStatePlug.class))); - } - - @Test - public void addGroupSwitchableByTag() throws IOException { - GroupItem item = new GroupItem("group1", new SwitchItem("switch1")); - item.addTag("Switchable"); - lightItems.added(item); - HueDevice device = ds.lights.get(lightItems.itemUIDtoHueID.get("group1")); - assertThat(device.item, is(item)); - assertThat(device.state, is(instanceOf(HueStatePlug.class))); - } - - @Test - public void addGroupWithoutTypeByTag() throws IOException { - GroupItem item = new GroupItem("group1", null); - item.addTag("Switchable"); - - lightItems.added(item); - - HueDevice device = ds.lights.get(lightItems.itemUIDtoHueID.get("group1")); - assertThat(device.item, is(item)); - assertThat(device.state, is(instanceOf(HueStatePlug.class))); - assertThat(ds.groups.get(lightItems.itemUIDtoHueID.get("group1")).groupItem, is(item)); - } - - @Test - public void removeGroupWithoutTypeAndTag() throws IOException { - String groupName = "group1"; - GroupItem item = new GroupItem(groupName, null); - item.addTag("Switchable"); - lightItems.added(item); - Integer hueId = lightItems.itemUIDtoHueID.get(groupName); - - lightItems.updated(item, new GroupItem(groupName, null)); - - assertThat(lightItems.itemUIDtoHueID.get(groupName), nullValue()); - assertThat(ds.lights.get(hueId), nullValue()); - assertThat(ds.groups.get(hueId), nullValue()); - } - - @Test - public void updateSwitchable() throws IOException { - SwitchItem item = new SwitchItem("switch1"); - item.setLabel("labelOld"); - item.addTag("Switchable"); - lightItems.added(item); - Integer hueID = lightItems.itemUIDtoHueID.get("switch1"); - HueDevice device = ds.lights.get(hueID); - assertThat(device.item, is(item)); - assertThat(device.state, is(instanceOf(HueStatePlug.class))); - assertThat(device.name, is("labelOld")); - - SwitchItem newitem = new SwitchItem("switch1"); - newitem.setLabel("labelNew"); - newitem.addTag("Switchable"); - lightItems.updated(item, newitem); - device = ds.lights.get(hueID); - assertThat(device.item, is(newitem)); - assertThat(device.state, is(instanceOf(HueStatePlug.class))); - assertThat(device.name, is("labelNew")); - - // Update with an item that has no tags anymore -> should be removed - SwitchItem newitemWithoutTag = new SwitchItem("switch1"); - newitemWithoutTag.setLabel("labelNew2"); - lightItems.updated(newitem, newitemWithoutTag); - - device = ds.lights.get(hueID); - assertThat(device, nullValue()); - assertThat(lightItems.itemUIDtoHueID.get("switch1"), nullValue()); - } -} diff --git a/addons/io/org.openhab.io.hueemulation/.classpath b/addons/io/org.openhab.io.hueemulation/.classpath deleted file mode 100644 index 91f97a0e4bf00..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/addons/io/org.openhab.io.hueemulation/.project b/addons/io/org.openhab.io.hueemulation/.project deleted file mode 100644 index a2c0f47b0832e..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.io.hueemulation - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/io/org.openhab.io.hueemulation/ESH-INF/config/config.xml b/addons/io/org.openhab.io.hueemulation/ESH-INF/config/config.xml deleted file mode 100644 index 4b57e251b10ed..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/ESH-INF/config/config.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - Pairing must be enabled to connect a new device. Pairing is automatically disabled after the configured pairing time (usually 60 seconds). - false - - - - Pairing is automatically disabled after the given time in seconds. - 60 - true - - - - The Amazon echos have no means to recreate a new api key and they don't care about the 403-forbidden http status code. If the addon has pruned its known users (reinstall etc), echos will not be able to discover new devices. Set this option to just create new users on the fly on the next pairing mode period. - false - - - - The HUE emulation can either publish Switch items if this is set to an empty string or filter items by tags. You can supply multiple tags by separating them with a comma. - Switchable - - - - The HUE emulation can either publish all Color items if this is set to an empty string or filter items by tags. You can supply multiple tags by separating them with a comma. - ColorLighting - - - - The HUE emulation can either publish all Dimmer items if this is set to an empty string or filter items by tags. You can supply multiple tags by separating them with a comma. - Lighting - - - - If your host has multiple IP addresses you may specify the IP you would like to advertise in the UPNP discovery process. You may safely leave this empty on most systems. - - - - Some Hue applications require a different port (80) then what openHAB runs on by default (8080). This option will only advertise a different port then what we are listening on. Useful if you have an iptables rule redirect traffic from this port to the openHAB port. - true - - - diff --git a/addons/io/org.openhab.io.hueemulation/META-INF/MANIFEST.MF b/addons/io/org.openhab.io.hueemulation/META-INF/MANIFEST.MF deleted file mode 100644 index 374a554e43c5d..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/META-INF/MANIFEST.MF +++ /dev/null @@ -1,34 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.io.hueemulation -Bundle-ClassPath: . -Bundle-ManifestVersion: 2 -Bundle-Name: Hue Emulation Service -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.io.hueemulation;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Import-Package: - com.google.gson, - com.google.gson.reflect, - com.google.gson.stream, - javax.servlet, - javax.servlet.http, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.common.registry, - org.eclipse.smarthome.core.events, - org.eclipse.smarthome.core.items, - org.eclipse.smarthome.core.items.events, - org.eclipse.smarthome.core.library, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.net, - org.eclipse.smarthome.core.service, - org.eclipse.smarthome.core.storage, - org.eclipse.smarthome.core.types, - org.osgi.framework, - org.osgi.service.cm, - org.osgi.service.component, - org.osgi.service.event, - org.osgi.service.http, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/io/org.openhab.io.hueemulation/NOTICE b/addons/io/org.openhab.io.hueemulation/NOTICE deleted file mode 100644 index 4c20ef446c1e4..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -This content is produced and maintained by the openHAB project. - -* Project home: https://www.openhab.org - -== Declared Project Licenses - -This program and the accompanying materials are made available under the terms -of the Eclipse Public License 2.0 which is available at -https://www.eclipse.org/legal/epl-2.0/. - -== Source Code - -https://github.com/openhab/openhab2-addons diff --git a/addons/io/org.openhab.io.hueemulation/OSGI-INF/.gitignore b/addons/io/org.openhab.io.hueemulation/OSGI-INF/.gitignore deleted file mode 100644 index 6722cd96e785a..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.xml diff --git a/addons/io/org.openhab.io.hueemulation/README.md b/addons/io/org.openhab.io.hueemulation/README.md deleted file mode 100644 index 301790dcf7ed8..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/README.md +++ /dev/null @@ -1,124 +0,0 @@ -# openHAB Hue Emulation Service - -Hue Emulation exposes openHAB items as Hue devices to other Hue HTTP API compatible applications like an Amazon Echo, Google Home or -any Hue compatible application. - -Because Amazon Echo and Google Home control openHAB locally this way, it is a fast and reliable way -to voice control your installation. See the Troubleshoot section down below though. - -## Discovery: - -As soon as the binding is enabled, it will announce the presence of an (emulated) HUE bridge of the second generation (square bridge). -Hue bridges are using the Universal Plug and Play (UPnP) protocol for discovery. - -Like the real HUE bridge the service must be put into pairing mode before other applications can access it. -By default the pairing mode disables itself after 1 minute (can be configured). - -## Exposed devices - -It is important to note that you are exposing *Items* not *Things* or *Channels*. -Only Color, Dimmer and Switch type *Items* are supported. - -This service can emulate 3 different devices: - -* An OSRAM SMART+ Plug, -* a dimmable white color Philips A19 bulb and -* an a Philips Gen 3 LCT010 extended color bulb. - -The exposed Hue-type depends on some criteria: - -* If the item has the category "ColorLight": It will be exposed as a color bulb -* If the item has the category "Light": It will be exposed as a switch. - -This initial type determination is overridden if the item is tagged. -Tags can be configured in Paper UI, please refer to the next section. - -The following default tags are setup: -* "Switchable": Item will be exposed as an OSRAM SMART+ Plug -* "Lighting": Item will be exposed as a dimmable white bulb -* "ColorLighting": Item will be exposed as a color bulb - -It is the responsibility of binding developers to categories and default tag their -available *Channels*, so that linked Items are automatically exposed with this service. - -You can tag items manually though as well. - -## Exposed names - -Your items labels are used for exposing! The default naming schema in Paper UI -for automatically linked items unfortunately names *Items* like their Channel names, -so usually "Brightness" or "Color". You want to rename those. - -## Configuration: - -All options are available in Paper UI. - -Pairing can be turned on and off: - -``` -org.openhab.hueemulation:pairingEnabled=false -``` - -You can define a pairing timeout in seconds. -After that timeout, the `pairingEnabled` is automatically set to `false`. - -``` -org.openhab.hueemulation:pairingTimeout=60 -``` - -To create an api key on the fly, you can set the following option. - -Necessary for Amazon Echos and other devices where the API key cannot be reset. -After a new installation of openHAB or a configuration pruning the old -API keys are gone but the Echos will keep trying with their invalid keys. - -``` -org.openhab.hueemulation:createNewUserOnEveryEndpoint=false -``` - -For systems with multiple IP addresses the IP to expose via UPNP may optionally be specified. -Otherwise the openHAB configured primary address will be used. -Usually you do not want to set this option, but change the primary address configuration of openHAB. - -``` -org.openhab.hueemulation:discoveryIp=192.168.1.100 -``` - -One of the comma separated tags must match for the item to be exposed. -Can be empty to match an item based on the other criteria. - -``` -org.openhab.hueemulation:restrictToTagsSwitches=Switchable -org.openhab.hueemulation:restrictToTagsWhiteLights=Lighting -org.openhab.hueemulation:restrictToTagsColorLights=ColorLighting -``` - -## Troubleshooting - -Some devices like the Amazon Echo, Google Home and all Philips devices expect a Hue bridge to -run on port 80. You must either port forward your openHAB installation to port 80, install -a reverse proxy on port 80 or let openHAB run on port 80. - -You can test if the hue emulation does its job by enabling pairing mode including the option -"Amazon Echo device discovery fix". - -1. Navigate with your browser to "http://your-openhab-ip/description.xml" to check the discovery - response. Check the IP address in there. -2. Navigate with your browser to "http://your-openhab-ip/api/testuser/lights?debug=true" - to check all exposed lights and switches. - -## Text configuration example - -The item label will be used as the Hue device name. - -``` -Switch TestSwitch "Kitchen Switch" [ "Switchable" ] -Color TestColorBulb "Bathroom" [ "ColorLighting" ] -Dimmer TestDimmer "Hallway" [ "Lighting" ] -``` - -If you have an item with a channel or binding attached the tag needs to be applied before the channel. - -``` -Switch BLamp1 "Bedroom Lamp 1" (FirstFloor) [ "Lighting" ] {mqtt=">[mosquitto:cmnd/sonoff-BLamp1/POWER:command:*:default],<[mosquitto:stat/sonoff-BLamp1/POWER:state:default]"} -``` diff --git a/addons/io/org.openhab.io.hueemulation/build.properties b/addons/io/org.openhab.io.hueemulation/build.properties deleted file mode 100644 index 5b7c7c06cbd92..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/build.properties +++ /dev/null @@ -1,9 +0,0 @@ -source.. = src/main/java/,\ - src/main/resources/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE - diff --git a/addons/io/org.openhab.io.hueemulation/pom.xml b/addons/io/org.openhab.io.hueemulation/pom.xml deleted file mode 100644 index 58bd6b46d8536..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.io - pom - 2.5.0-SNAPSHOT - - - org.openhab.io.hueemulation - eclipse-plugin - - Hue Emulation Service - - diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/ConfigManagement.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/ConfigManagement.java deleted file mode 100644 index 7180c82c84bd1..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/ConfigManagement.java +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal; - -import java.io.IOException; -import java.util.Dictionary; -import java.util.UUID; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.storage.Storage; -import org.openhab.io.hueemulation.internal.dto.HueDataStore; -import org.osgi.service.cm.ConfigurationAdmin; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Manages config values of this emulated HUE bridge. - * - * @author David Graeff - Initial contribution - */ -@NonNullByDefault -public class ConfigManagement { - private final Logger logger = LoggerFactory.getLogger(ConfigManagement.class); - private final HueDataStore dataStore; - private @Nullable Storage storage; - private @Nullable Thread pairingTimeoutThread; - private @NonNullByDefault({}) ConfigurationAdmin configAdmin; - - public ConfigManagement(HueDataStore ds) { - dataStore = ds; - } - - public void setConfigAdmin(@Nullable ConfigurationAdmin configAdmin) { - this.configAdmin = configAdmin; - } - - /** - * Load modified config from disk - */ - public void loadConfigFromFile(Storage storage) { - boolean storageChanged = this.storage != null && this.storage != storage; - this.storage = storage; - String devicename = storage.get("devicename"); - if (devicename == null) { - devicename = dataStore.config.devicename; - } - dataStore.config.devicename = devicename; - - String udnString = storage.get("udn"); - if (udnString == null) { - udnString = UUID.randomUUID().toString(); - storage.put("udn", udnString); - } - - dataStore.config.uuid = udnString; - dataStore.config.bridgeid = dataStore.config.uuid.replace("-", "").substring(0, 12).toUpperCase(); - - if (storageChanged) { - writeToFile(); - } - } - - /** - * Persist to storage. - */ - void writeToFile() { - Storage storage = this.storage; - if (storage == null) { - return; - } - storage.put("devicename", dataStore.config.devicename); - storage.put("udn", dataStore.config.uuid); - } - - public void resetStorage() { - this.storage = null; - } - - /** - * Starts a pairing timeout thread if dataStore.config.linkbutton is set to true. - * Stops any already setup timer. - */ - void checkPairingTimeout() { - stopPairingTimeoutThread(); - if (dataStore.config.linkbutton) { - logger.info("Hue Emulation pairing enabled for {}s at {}", dataStore.config.networkopenduration, - RESTApi.PATH); - Thread thread = new Thread(() -> { - try { - Thread.sleep(dataStore.config.networkopenduration * 1000); - org.osgi.service.cm.Configuration configuration = configAdmin - .getConfiguration("org.openhab.hueemulation"); - Dictionary dictionary = configuration.getProperties(); - dictionary.put(HueEmulationConfig.CONFIG_PAIRING_ENABLED, false); - dictionary.put(HueEmulationConfig.CONFIG_CREATE_NEW_USER_ON_THE_FLY, false); - configuration.update(dictionary); - } catch (IOException | InterruptedException ignore) { - } - }); - pairingTimeoutThread = thread; - thread.start(); - } else { - logger.info("Hue Emulation pairing disabled. Service available under {}", RESTApi.PATH); - } - } - - void stopPairingTimeoutThread() { - Thread thread = pairingTimeoutThread; - if (thread != null) { - thread.interrupt(); - try { - thread.join(2000); - } catch (InterruptedException e) { - } - pairingTimeoutThread = null; - } - } -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/DeviceType.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/DeviceType.java deleted file mode 100644 index b0b603835d589..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/DeviceType.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal; - -/** - * Device type - * - * @author David Graeff - Initial contribution - */ -public enum DeviceType { - SwitchType, - WhiteType, - WhiteTemperatureType, - ColorType -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationConfig.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationConfig.java deleted file mode 100644 index e328270fc6bb8..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationConfig.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal; - -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * The configuration for {@link HueEmulationService}. - * - * @author David Graeff - Initial Contribution - */ -@NonNullByDefault -public class HueEmulationConfig { - public boolean pairingEnabled = false; - public static final String CONFIG_PAIRING_ENABLED = "pairingEnabled"; - /** - * The Amazon echos have no means to recreate a new api key and they don't care about the 403-forbidden http status - * code. If the addon has pruned its api-key list, echos will not be able to discover new devices. Set this option - * to just create a new user on the fly. - */ - public boolean createNewUserOnEveryEndpoint = true; - public static final String CONFIG_CREATE_NEW_USER_ON_THE_FLY = "createNewUserOnEveryEndpoint"; - /** Pairing timeout in seconds */ - public int pairingTimeout = 60; - public @Nullable String discoveryIp; - public int discoveryHttpPort = 0; - /** Comma separated list of tags */ - public String restrictToTagsSwitches = "Switchable"; - /** Comma separated list of tags */ - public String restrictToTagsColorLights = "ColorLighting"; - /** Comma separated list of tags */ - public String restrictToTagsWhiteLights = "Lighting"; - - public Set switchTags() { - return Stream.of(restrictToTagsSwitches.split(",")).map(String::trim).collect(Collectors.toSet()); - } - - public Set colorTags() { - return Stream.of(restrictToTagsColorLights.split(",")).map(String::trim).collect(Collectors.toSet()); - } - - public Set whiteTags() { - return Stream.of(restrictToTagsWhiteLights.split(",")).map(String::trim).collect(Collectors.toSet()); - } -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationService.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationService.java deleted file mode 100644 index c04100197adcd..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationService.java +++ /dev/null @@ -1,446 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.Writer; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collections; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.config.core.ConfigurableService; -import org.eclipse.smarthome.config.core.Configuration; -import org.eclipse.smarthome.core.events.EventPublisher; -import org.eclipse.smarthome.core.items.ItemRegistry; -import org.eclipse.smarthome.core.net.NetworkAddressService; -import org.eclipse.smarthome.core.service.ReadyMarker; -import org.eclipse.smarthome.core.service.ReadyService; -import org.eclipse.smarthome.core.service.ReadyService.ReadyTracker; -import org.eclipse.smarthome.core.storage.StorageService; -import org.openhab.io.hueemulation.internal.RESTApi.HttpMethod; -import org.openhab.io.hueemulation.internal.dto.HueDataStore; -import org.openhab.io.hueemulation.internal.dto.HueGroup; -import org.openhab.io.hueemulation.internal.dto.response.HueResponse; -import org.openhab.io.hueemulation.internal.dto.response.HueResponse.HueErrorMessage; -import org.openhab.io.hueemulation.internal.dto.response.HueSuccessResponseStateChanged; -import org.osgi.service.cm.ConfigurationAdmin; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Deactivate; -import org.osgi.service.component.annotations.Modified; -import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.annotations.ReferencePolicy; -import org.osgi.service.http.HttpContext; -import org.osgi.service.http.HttpService; -import org.osgi.service.http.NamespaceException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonParseException; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonWriter; - -/** - * Emulates a Hue compatible HTTP API server. Provides /description.xml for upnp (see {@link HueEmulationUpnpServer} - * and /api for a hue compatible REST API. - * - *

    - *
  • Find the user management (/api/{username}/config/whitelist) in {@link UserManagement}. - *
  • The unique device ID is managed by the {@link ConfigManagement}. A user is able to rename his brigde via the API. - *
  • ESH items via the {@link ItemRegistry} are mapped to /api/{username}/lights and /api/{username}/groups in - * {@link LightItems}. - *
  • The REST processing is done in {@link RESTApi}. - *
- * - * @author Dan Cunningham - Initial Contribution - * @author Kai Kreuzer - Improved resource handling to avoid leaks - * @author David Graeff - Rewritten - * - */ -@SuppressWarnings("serial") -@NonNullByDefault -@Component(immediate = true, service = { - HueEmulationService.class }, configurationPid = "org.openhab.hueemulation", property = { - org.osgi.framework.Constants.SERVICE_PID + "=org.openhab.hueemulation", - ConfigurableService.SERVICE_PROPERTY_DESCRIPTION_URI + "=io:hueemulation", - ConfigurableService.SERVICE_PROPERTY_CATEGORY + "=io", - ConfigurableService.SERVICE_PROPERTY_LABEL + "=Hue Emulation" }) -public class HueEmulationService implements ReadyTracker { - - private final Path DISCOVERY_PATH = Paths.get(RESTApi.PATH + "/description.xml"); - private final Path DISCOVERY_PATH_ROOT = Paths.get("/description.xml"); - - private final Logger logger = LoggerFactory.getLogger(HueEmulationService.class); - private final Gson gson = new GsonBuilder() - .registerTypeAdapter(HueSuccessResponseStateChanged.class, new HueSuccessResponseStateChanged.Serializer()) - .registerTypeAdapter(HueGroup.class, new HueGroup.Serializer()).create(); - - //// Required services //// - private @NonNullByDefault({}) HttpService httpService; - private @NonNullByDefault({}) NetworkAddressService networkAddressService; - private @NonNullByDefault({}) ReadyService readyService; - protected @NonNullByDefault({}) HueEmulationUpnpServer discovery; - - //// objects, set within activate() - private @NonNullByDefault({}) HueEmulationConfig config; - private @NonNullByDefault({}) String xmlDoc; - - protected final HueDataStore ds = new HueDataStore(); - protected final UserManagement userManagement = new UserManagement(ds); - protected final LightItems lightItems = new LightItems(ds); - protected final ConfigManagement configManagement = new ConfigManagement(ds); - protected final RESTApi restAPI = new RESTApi(ds, userManagement, configManagement, gson); - protected boolean started = false; - - /** - * A servlet for providing /api/discovery.xml and the REST API - */ - HttpServlet restAPIservlet = new HttpServlet() { - @NonNullByDefault({}) - @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - Utils.setHeaders(resp); - final Path path = Paths.get(req.getRequestURI()); - final boolean isDebug = "debug=true".equals(req.getQueryString()); - String postBody; - final HttpMethod method; - - try (PrintWriter httpOut = resp.getWriter()) { - // UPNP discovery document - if (path.equals(DISCOVERY_PATH)) { - sendDiscoveryXML(resp, httpOut); - return; - } - - StringWriter out = new StringWriter(); - - if (!isDebug) { - resp.setContentType("application/json"); - } else { - resp.setContentType("text/plain"); - } - - try { - method = Enum.valueOf(HttpMethod.class, req.getMethod()); - } catch (IllegalArgumentException e) { - resp.setStatus(405); - apiServerError(req, out, HueResponse.METHOD_NOT_ALLOWED, - req.getMethod() + " not allowed for this resource"); - httpOut.print(out.toString()); - return; - } - - if (method == HttpMethod.POST || method == HttpMethod.PUT) { - try { - postBody = req.getReader().lines().collect(Collectors.joining(System.lineSeparator())); - } catch (IllegalStateException e) { - apiServerError(req, out, HueResponse.INTERNAL_ERROR, - "Could not read http body. Jetty failure."); - resp.setStatus(500); - return; - } - } else { - postBody = ""; - } - - int statuscode = 0; - try { - statuscode = restAPI.handle(method, postBody, out, path, isDebug); - switch (statuscode) { - case 10403: // Fake status code -> translate to real one - statuscode = 403; - apiServerError(req, out, HueResponse.LINK_BUTTON_NOT_PRESSED, "link button not pressed"); - break; - case 403: - logger.debug("Unauthorized access to {} from {}:{}!\n", req.getRequestURI(), - req.getRemoteAddr(), req.getRemotePort()); - apiServerError(req, out, HueResponse.UNAUTHORIZED, "Not Authorized"); - break; - case 404: - apiServerError(req, out, HueResponse.NOT_AVAILABLE, "Hue resource not available"); - break; - case 405: - apiServerError(req, out, HueResponse.METHOD_NOT_ALLOWED, - req.getMethod() + " not allowed for this resource"); - break; - } - } catch (JsonParseException e) { - statuscode = 400; - apiServerError(req, out, HueResponse.INVALID_JSON, "Invalid request: " + e.getMessage()); - } - - resp.setStatus(statuscode); - httpOut.print(out.toString()); - - } - } - }; - - /** - * A second servlet for providing /discovery.xml next to /api/discovery.xml - */ - HttpServlet discoveryXMLservlet = new HttpServlet() { - @NonNullByDefault({}) - @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - final Path path = Paths.get(req.getRequestURI()); - try (PrintWriter out = resp.getWriter()) { - // UPNP discovery document - if (path.equals(DISCOVERY_PATH) || path.equals(DISCOVERY_PATH_ROOT)) { - sendDiscoveryXML(resp, out); - return; - } - } - resp.setStatus(404); - } - }; - - @Activate - protected void activate(Map properties) { - InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("discovery.xml"); - if (resourceAsStream == null) { - logger.warn("Could not start Hue Emulation service: discovery.xml not found"); - return; - } - xmlDoc = new BufferedReader(new InputStreamReader(resourceAsStream, StandardCharsets.UTF_8)).lines() - .collect(Collectors.joining("\n")); - - started = false; - modified(properties); - - // The hue emulation need to start very late in the start up process. The - // ready marker service is used to make sure all items and item descriptions are loaded. - readyService.registerTracker(this); - } - - @Modified - protected void modified(Map properties) { - // Get and apply configurations - this.config = new Configuration(properties).as(HueEmulationConfig.class); - lightItems.setFilterTags(config.switchTags(), config.colorTags(), config.whiteTags()); - ds.config.linkbutton = config.pairingEnabled; - ds.config.createNewUserOnEveryEndpoint = config.createNewUserOnEveryEndpoint; - ds.config.networkopenduration = config.pairingTimeout; - - // If started: restart all parts of this service that depend on configuration - if (!started) { - return; - } - - restartDiscovery(); - configManagement.checkPairingTimeout(); - } - - @Deactivate - protected void deactivate() { - readyService.unregisterTracker(this); - configManagement.stopPairingTimeoutThread(); - lightItems.close(); - userManagement.writeToFile(); - configManagement.writeToFile(); - - try { - httpService.unregister(RESTApi.PATH); - httpService.unregister("/description.xml"); - } catch (IllegalArgumentException ignored) { - } - if (discovery != null) { - discovery.shutdown(); - } - } - - @Override - public void onReadyMarkerRemoved(ReadyMarker readyMarker) { - } - - @Override - public synchronized void onReadyMarkerAdded(ReadyMarker readyMarker) { - if (started || !"org.eclipse.smarthome.model.core".equals(readyMarker.getIdentifier())) { - return; - } - - started = true; - HttpContext httpContext = httpService.createDefaultHttpContext(); - try { - httpService.registerServlet(RESTApi.PATH, restAPIservlet, new Hashtable(), httpContext); - } catch (ServletException | NamespaceException e) { - logger.warn("Could not start Hue Emulation service: {}", e.getMessage(), e); - return; - } - - try { // This may fail, but it is not essential for the emulation (just for non-standard devices) - httpService.registerServlet("/description.xml", discoveryXMLservlet, new Hashtable(), - httpContext); - } catch (ServletException | NamespaceException e) { - logger.debug("Hue Emulation: Cannot register /description.xml"); - } - - configManagement.checkPairingTimeout(); - restartDiscovery(); - - // Announce that this service is ready and unregister from the tracker - readyService.markReady(new ReadyMarker("Online", "org.openhab.hueemulation")); - CompletableFuture.runAsync(() -> readyService.unregisterTracker(this)); - } - - @Reference - protected void setStateDescriptionService(ReadyService readyService) { - this.readyService = readyService; - } - - protected void unsetStateDescriptionService(ReadyService readyService) { - this.readyService = null; - } - - @Reference - protected void setConfigurationAdmin(ConfigurationAdmin configAdmin) { - configManagement.setConfigAdmin(configAdmin); - } - - protected void unsetConfigurationAdmin(ConfigurationAdmin configAdmin) { - configManagement.setConfigAdmin(null); - } - - @Reference - protected void setNetworkAddressService(NetworkAddressService netUtils) { - this.networkAddressService = netUtils; - } - - protected void unsetNetworkAddressService(NetworkAddressService netUtils) { - this.networkAddressService = null; - } - - @Reference - protected void setItemRegistry(ItemRegistry itemRegistry) { - lightItems.setItemRegistry(itemRegistry); - } - - protected void unsetItemRegistry(ItemRegistry itemRegistry) { - lightItems.setItemRegistry(null); - } - - @Reference - protected void setEventPublisher(EventPublisher eventPublisher) { - restAPI.setEventPublisher(eventPublisher); - } - - protected void unsetEventPublisher(EventPublisher eventPublisher) { - restAPI.setEventPublisher(null); - } - - @Reference - protected void setHttpService(HttpService httpService) { - this.httpService = httpService; - } - - protected void unsetHttpService(HttpService httpService) { - this.httpService = null; - } - - @Reference(policy = ReferencePolicy.DYNAMIC) - protected void setStorageService(StorageService storageService) { - ClassLoader loader = this.getClass().getClassLoader(); - userManagement.loadUsersFromFile(storageService.getStorage("hue.emulation.users", loader)); - lightItems.loadMappingFromFile(storageService.getStorage("hue.emulation.lights", loader)); - configManagement.loadConfigFromFile(storageService.getStorage("hue.emulation.config", loader)); - } - - protected void unsetStorageService(StorageService storageService) { - userManagement.resetStorage(); - lightItems.resetStorage(); - configManagement.resetStorage(); - } - - void restartDiscovery() { - if (discovery != null) { - discovery.shutdown(); - discovery = null; - } - - if (config.discoveryHttpPort == 0) { - config.discoveryHttpPort = Integer.getInteger("org.osgi.service.http.port"); - } - - String discoveryIp = config.discoveryIp; - if (discoveryIp == null) { - discoveryIp = networkAddressService.getPrimaryIpv4HostAddress(); - } - - if (discoveryIp == null) { - logger.warn("No primary IP address configured. Discovery disabled!"); - return; - } - - InetAddress address; - try { - address = InetAddress.getByName(discoveryIp); - } catch (UnknownHostException e) { - logger.warn("No primary IP address configured. Discovery disabled!", e); - return; - } - - discovery = new HueEmulationUpnpServer(RESTApi.PATH + "/description.xml", ds.config, address, - config.discoveryHttpPort); - discovery.start(); - - ds.config.mac = Utils.getMAC(address); - ds.config.ipaddress = address.getHostAddress(); - } - - private void sendDiscoveryXML(HttpServletResponse resp, PrintWriter out) throws IOException { - resp.setContentType("application/xml"); - String address = discovery.getAddress().getHostAddress(); - out.write( - String.format(xmlDoc, address, config.discoveryHttpPort, address, ds.config.bridgeid, ds.config.uuid)); - } - - /** - * Hue API error response - */ - public void apiServerError(HttpServletRequest req, Writer out, int error, String description) throws IOException { - if (error == HueResponse.UNAUTHORIZED) { - - } else { - logger.debug("'{}' for: {}\nRequest from: {}:{}\n", description, req.getRequestURI(), req.getRemoteAddr(), - req.getRemotePort()); - } - - try (JsonWriter writer = new JsonWriter(out)) { - HueResponse e = new HueResponse( - new HueErrorMessage(error, req.getRequestURI().replace("/api", ""), description)); - gson.toJson(Collections.singleton(e), new TypeToken>() { - }.getType(), writer); - } - } -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationUpnpServer.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationUpnpServer.java deleted file mode 100644 index 00c8f1e3639ad..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationUpnpServer.java +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.MulticastSocket; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.net.SocketTimeoutException; -import java.net.UnknownHostException; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.io.hueemulation.internal.dto.HueAuthorizedConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Advertises a Hue UPNP compatible bridge - * - * @author Dan Cunningham - Initial contribution - * @author David Graeff - Refactored - */ -@NonNullByDefault -public class HueEmulationUpnpServer implements Runnable { - private final Logger logger = LoggerFactory.getLogger(HueEmulationUpnpServer.class); - - /** - * According to the UPnP specification, the minimum MaxAge is 1800 seconds. - */ - static final int MIN_MAX_AGE_MSECS = 1800000; - - // jUPNP shares port 1900, but since this is multicast, we can also bind to it - private static final int UPNP_PORT_RECV = 1900; - private static final String MULTI_ADDR = "239.255.255.250"; - private final InetAddress MULTI_ADDR_IP; - private boolean running; - private final InetAddress address; - private final String[] stVersions = { "", "", "" }; - private @Nullable Thread thread; - private @Nullable DatagramSocket socket; - private int webPort; - - /** - * Server to send UDP packets onto the network when requested by a Hue API compatible device. - * - * @param relativePath The URI path where the discovery xml document can be retrieved - * @param config The hue datastore. Contains the bridgeid and uuid. - * @param address IP to advertise for UPNP - */ - public HueEmulationUpnpServer(String relativePath, HueAuthorizedConfig config, InetAddress address, int webPort) { - this.webPort = webPort; - this.running = true; - this.address = address; - try { - MULTI_ADDR_IP = InetAddress.getByName(MULTI_ADDR); - } catch (UnknownHostException e) { - throw new IllegalStateException(e); - } - - final String[] stVersions = { "upnp:rootdevice", "urn:schemas-upnp-org:device:basic:1", "uuid:" + config.uuid }; - for (int i = 0; i < stVersions.length; ++i) { - this.stVersions[i] = String.format( - "HTTP/1.1 200 OK\r\n" + "HOST: %s:%d\r\n" + "EXT:\r\n" + "CACHE-CONTROL: max-age=100\r\n" - + "LOCATION: %s\r\n" + "SERVER: FreeRTOS/7.4.2, UPnP/1.0, IpBridge/1.15.0\r\n" - + "hue-bridgeid: %s\r\n" + "ST: %s\r\n" + "USN: uuid:%s::upnp:rootdevice\r\n\r\n", - MULTI_ADDR, UPNP_PORT_RECV, - "http://" + address.getHostAddress().toString() + ":" + webPort + relativePath, config.bridgeid, - stVersions[i], config.uuid); - } - - } - - public void start() { - if (socket != null) { - return; - } - - running = true; - Thread thread = new Thread(this); - this.thread = thread; - thread.start(); - } - - /** - * Stops the upnp server from running - * - * @throws InterruptedException - */ - public void shutdown() { - Thread thread = this.thread; - DatagramSocket socket = this.socket; - if (thread == null || socket == null) { - return; - } - - this.running = false; - socket.close(); - - try { - thread.join(); - } catch (InterruptedException ignore) { - Thread.currentThread().interrupt(); - } - thread = null; - } - - @Override - public void run() { - byte[] buf = new byte[1000]; - DatagramPacket recv = new DatagramPacket(buf, buf.length); - try (MulticastSocket recvSocket = new MulticastSocket(UPNP_PORT_RECV); - DatagramSocket sendSocket = new DatagramSocket()) { - recvSocket.setReuseAddress(true); - recvSocket.setLoopbackMode(true); - recvSocket.setSoTimeout(MIN_MAX_AGE_MSECS); - this.socket = recvSocket; - recvSocket.joinGroup(new InetSocketAddress(MULTI_ADDR, UPNP_PORT_RECV), - NetworkInterface.getByInetAddress(address)); - - while (running) { - try { - recvSocket.receive(recv); - } catch (SocketTimeoutException ignore) { - // Broadcast every MIN_MAX_AGE_MSECS - sendUPNPDatagrams(recvSocket, MULTI_ADDR_IP, UPNP_PORT_RECV); - continue; - } - if (recv.getLength() == 0 - || (recv.getAddress() == address && recv.getPort() == sendSocket.getLocalPort())) { - continue; - } - String data = new String(recv.getData()); - if (!data.startsWith("M-SEARCH")) { - continue; - } - - sendUPNPDatagrams(sendSocket, recv.getAddress(), recv.getPort()); - } - } catch (SocketException e) { - if (running) { - logger.warn("Socket error with UPNP server", e); - } - } catch (IOException e) { - if (running) { - logger.warn("IO Error with UPNP server", e); - } - } - this.socket = null; - } - - private void sendUPNPDatagrams(DatagramSocket sendSocket, InetAddress address, int port) { - for (String msg : stVersions) { - DatagramPacket response = new DatagramPacket(msg.getBytes(), msg.length(), address, port); - try { - logger.trace("Sending to {} : {}", address.getHostAddress(), msg); - sendSocket.send(response); - } catch (IOException e) { - logger.warn("Could not send UPNP response: {}", e.getMessage()); - } - } - } - - InetAddress getAddress() { - return address; - } - - public int getWebPort() { - return webPort; - } -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/LightItems.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/LightItems.java deleted file mode 100644 index 4e1ca4cc37910..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/LightItems.java +++ /dev/null @@ -1,363 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal; - -import java.util.Collections; -import java.util.Set; -import java.util.TreeMap; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.common.registry.RegistryChangeListener; -import org.eclipse.smarthome.core.items.GroupItem; -import org.eclipse.smarthome.core.items.Item; -import org.eclipse.smarthome.core.items.ItemRegistry; -import org.eclipse.smarthome.core.library.CoreItemFactory; -import org.eclipse.smarthome.core.storage.Storage; -import org.openhab.io.hueemulation.internal.dto.HueDataStore; -import org.openhab.io.hueemulation.internal.dto.HueDevice; -import org.openhab.io.hueemulation.internal.dto.HueGroup; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Listens to the ItemRegistry for items that fulfill one of these criteria: - *
    - *
  • Type is any of SWITCH, DIMMER, COLOR, or Group - *
  • The category is "ColorLight" for coloured lights or "Light" for switchables. - *
  • The item is tagged, according to what is set with {@link #setFilterTags(Set, Set, Set)}. - *
- * - *

- * A {@link HueDevice} instances is created for each found item. - * Those are kept in the given {@link HueDataStore}. - *

- * - *

- * Implementing scenes should be done here as well. - *

- * - *

- * The HUE Rest API requires a unique integer ID for every listed device. A storage service - * is used to store and load this mapping. A storage is not required for this class to work, - * but without it the mapping will be temporary only and ids may change on every boot up. - *

- * - *

- *

- * - * @author David Graeff - Initial contribution - * @author Florian Schmidt - Removed base type restriction from Group items - */ -@NonNullByDefault -public class LightItems implements RegistryChangeListener { - private final Logger logger = LoggerFactory.getLogger(LightItems.class); - private static final String ITEM_TYPE_GROUP = "Group"; - private static final Set ALLOWED_ITEM_TYPES = Stream - .of(CoreItemFactory.COLOR, CoreItemFactory.DIMMER, CoreItemFactory.ROLLERSHUTTER, CoreItemFactory.SWITCH, ITEM_TYPE_GROUP) - .collect(Collectors.toSet()); - - // deviceMap maps a unique Item id to a Hue numeric id - final TreeMap itemUIDtoHueID = new TreeMap<>(); - private final HueDataStore dataStore; - private Set switchFilter = Collections.emptySet(); - private Set colorFilter = Collections.emptySet(); - private Set whiteFilter = Collections.emptySet(); - private @Nullable Storage storage; - private boolean initDone = false; - private @NonNullByDefault({}) ItemRegistry itemRegistry; - - public LightItems(HueDataStore ds) { - dataStore = ds; - } - - /** - * Set filter tags. Empty sets are allowed, items will not be filtered by tags but other criteria then. - * - *

- * Calling this method will reset the {@link HueDataStore} and parse items from the item registry again. - *

- * - * @param switchFilter The switch filter tags - * @param colorFilter The color filter tags - * @param whiteFilter The white bulb filter tags - */ - public void setFilterTags(Set switchFilter, Set colorFilter, Set whiteFilter) { - this.switchFilter = switchFilter; - this.colorFilter = colorFilter; - this.whiteFilter = whiteFilter; - fetchItems(); - } - - /** - * Sets the item registry. Used to load up items and register to changes - * - * @param itemRegistry The item registry - */ - public void setItemRegistry(@Nullable ItemRegistry itemRegistry) { - this.itemRegistry = itemRegistry; - } - - /** - * Load the {@link #itemUIDtoHueID} mapping from the given storage. - * This method can also be called when the storage service changes. - * A changed storage causes an immediately write request. - *

- * Because storage services are dynamically bound and may appear late, - * it may happen that the mapping is only loaded after items have been parsed already. - * In that case the {@link HueDataStore} is reset and items from the item registry are parsed again. - * It is important to keep the once exposed ids though. - *

- * - * @param storage A storage service - */ - public void loadMappingFromFile(@Nullable Storage storage) { - boolean storageChanged = this.storage != null && this.storage != storage; - this.storage = storage; - if (storage == null) { - return; - } - - for (String itemUID : storage.getKeys()) { - Integer hueID = storage.get(itemUID); - if (hueID == null) { - continue; - } - itemUIDtoHueID.put(itemUID, hueID); - } - - if (storageChanged) { - writeToFile(); - } else if (initDone) { // storage comes late to the game -> reassign all items - fetchItems(); - } - } - - /** - * Registers to the {@link ItemRegistry} and enumerates currently existing items. - * Call {@link #close(ItemRegistry)} when you are done with this object. - * - * Only call this after you have set the filter tags with {@link #setFilterTags(Set, Set, Set)}. - */ - public synchronized void fetchItems() { - initDone = false; - - dataStore.resetGroupsAndLights(); - - itemRegistry.removeRegistryChangeListener(this); - itemRegistry.addRegistryChangeListener(this); - - boolean changed = false; - for (Item item : itemRegistry.getItems()) { - changed |= addItem(item); - } - initDone = true; - - logger.debug("Added items: {}", - dataStore.lights.values().stream().map(l -> l.name).collect(Collectors.joining(", "))); - if (changed) { - writeToFile(); - } - } - - /** - * Saves the ID->Item association to the storage. - */ - private void writeToFile() { - Storage storage = this.storage; - if (storage == null) { - return; - } - storage.getKeys().forEach(key -> storage.remove(key)); - itemUIDtoHueID.forEach((itemUID, hueID) -> storage.put(itemUID, hueID)); - } - - public void resetStorage() { - this.storage = null; - } - - /** - * Unregisters from the {@link ItemRegistry}. - */ - public void close() { - writeToFile(); - itemRegistry.removeRegistryChangeListener(this); - } - - private @Nullable DeviceType determineTargetType(@Nullable String category, String type, Set tags) { - // Determine type, heuristically - DeviceType t = null; - - // First consider the category - if (category != null) { - switch (category) { - case "ColorLight": - t = DeviceType.ColorType; - break; - case "Light": - t = DeviceType.SwitchType; - } - } - - // Then the tags - if (switchFilter.stream().anyMatch(tags::contains)) { - t = DeviceType.SwitchType; - } - if (whiteFilter.stream().anyMatch(tags::contains)) { - t = DeviceType.WhiteTemperatureType; - } - if (colorFilter.stream().anyMatch(tags::contains)) { - t = DeviceType.ColorType; - } - - // Last but not least, the item type - if (t == null) { - switch (type) { - case CoreItemFactory.COLOR: - if (colorFilter.size() == 0) { - t = DeviceType.ColorType; - } - break; - case CoreItemFactory.DIMMER: - if (whiteFilter.size() == 0) { - t = DeviceType.WhiteTemperatureType; - } - break; - case CoreItemFactory.SWITCH: - if (switchFilter.size() == 0) { - t = DeviceType.SwitchType; - } - break; - } - } - return t; - } - - @Override - public synchronized void added(Item element) { - addItem(element); - } - - String getType(Item element) { - if (element instanceof GroupItem) { - return ITEM_TYPE_GROUP; - } - return element.getType(); - } - - @SuppressWarnings({ "unused", "null" }) - public boolean addItem(Item element) { - // Only allowed types - String type = getType(element); - - if (!ALLOWED_ITEM_TYPES.contains(type)) { - return false; - } - - DeviceType t = determineTargetType(element.getCategory(), type, element.getTags()); - if (t == null) { - return false; - } - - Integer hueID = itemUIDtoHueID.get(element.getUID()); - - boolean itemAssociationCreated = false; - if (hueID == null) { - hueID = dataStore.generateNextLightHueID(); - itemAssociationCreated = true; - } - - HueDevice device = new HueDevice(element, dataStore.config.uuid + "-" + hueID.toString(), t); - device.item = element; - dataStore.lights.put(hueID, device); - if (element instanceof GroupItem) { - GroupItem g = (GroupItem) element; - g.getMembers(); - HueGroup group = new HueGroup(g.getName(), g, itemUIDtoHueID); - dataStore.groups.put(hueID, group); - } - updateGroup0(); - itemUIDtoHueID.put(element.getUID(), hueID); - if (initDone) { - logger.debug("Add item {}", element.getUID()); - if (itemAssociationCreated) { - writeToFile(); - } - } - return itemAssociationCreated; - } - - /** - * The HUE API enforces a Group 0 that contains all lights. - */ - private void updateGroup0() { - dataStore.groups.get(0).lights = dataStore.lights.keySet().stream().map(v -> String.valueOf(v)) - .collect(Collectors.toList()); - } - - @SuppressWarnings({ "null", "unused" }) - @Override - public synchronized void removed(Item element) { - Integer hueID = itemUIDtoHueID.get(element.getUID()); - if (hueID == null) { - return; - } - logger.debug("Remove item {}", element.getUID()); - dataStore.lights.remove(hueID); - dataStore.groups.remove(hueID); - updateGroup0(); - itemUIDtoHueID.remove(element.getUID()); - writeToFile(); - } - - /** - * The tags might have changed - */ - @SuppressWarnings({ "null", "unused" }) - @Override - public synchronized void updated(Item oldElement, Item element) { - Integer hueID = itemUIDtoHueID.get(element.getUID()); - if (hueID == null) { - // If the correct tags got added -> use the logic within added() - added(element); - return; - } - - HueGroup hueGroup = dataStore.groups.get(hueID); - if (hueGroup != null) { - if (element instanceof GroupItem) { - hueGroup.updateItem((GroupItem) element); - } else { - dataStore.groups.remove(hueID); - } - } - - HueDevice hueDevice = dataStore.lights.get(hueID); - if (hueDevice == null) { - // If the correct tags got added -> use the logic within added() - added(element); - return; - } - - // Check if type can still be determined (tags and category is still sufficient) - DeviceType t = determineTargetType(element.getCategory(), getType(element), element.getTags()); - if (t == null) { - removed(element); - return; - } - - hueDevice.updateItem(element); - } -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/RESTApi.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/RESTApi.java deleted file mode 100644 index b0007c33824af..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/RESTApi.java +++ /dev/null @@ -1,434 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal; - -import java.io.IOException; -import java.io.Writer; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.UUID; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.events.EventPublisher; -import org.eclipse.smarthome.core.items.events.ItemEventFactory; -import org.eclipse.smarthome.core.types.Command; -import org.openhab.io.hueemulation.internal.dto.HueDataStore; -import org.openhab.io.hueemulation.internal.dto.HueDevice; -import org.openhab.io.hueemulation.internal.dto.HueNewLights; -import org.openhab.io.hueemulation.internal.dto.HueStateChange; -import org.openhab.io.hueemulation.internal.dto.HueUnauthorizedConfig; -import org.openhab.io.hueemulation.internal.dto.changerequest.HueChangeRequest; -import org.openhab.io.hueemulation.internal.dto.changerequest.HueCreateUser; -import org.openhab.io.hueemulation.internal.dto.response.HueResponse; -import org.openhab.io.hueemulation.internal.dto.response.HueResponse.HueErrorMessage; -import org.openhab.io.hueemulation.internal.dto.response.HueSuccessCreateGroup; -import org.openhab.io.hueemulation.internal.dto.response.HueSuccessResponseCreateUser; -import org.openhab.io.hueemulation.internal.dto.response.HueSuccessResponseStartSearchLights; -import org.openhab.io.hueemulation.internal.dto.response.HueSuccessResponseStateChanged; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.Gson; -import com.google.gson.JsonParseException; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonWriter; - -/** - * Handles all REST API Requests - * - * @author David Graeff - Initial contribution - */ -@NonNullByDefault -public class RESTApi { - public static final String PATH = "/api"; - private final Logger logger = LoggerFactory.getLogger(HueEmulationService.class); - private final HueDataStore ds; - private final Gson gson; - private final UserManagement userManagement; - private final ConfigManagement configManagement; - private @NonNullByDefault({}) EventPublisher eventPublisher; - - public static enum HttpMethod { - GET, - POST, - PUT, - DELETE - } - - public RESTApi(HueDataStore ds, UserManagement userManagement, ConfigManagement configManagement, Gson gson) { - this.ds = ds; - this.userManagement = userManagement; - this.configManagement = configManagement; - this.gson = gson; - } - - public void setEventPublisher(@Nullable EventPublisher eventPublisher) { - this.eventPublisher = eventPublisher; - } - - /** - * Cuts of the first part of a path and returns the remaining one. - */ - private Path remaining(Path path) { - if (path.getNameCount() > 1) { - return path.subpath(1, path.getNameCount()); - } else { - return Paths.get("/"); - } - } - - /** - * Handles /api and forwards any deeper path - * - * @param isDebug - */ - @SuppressWarnings("null") - public int handle(HttpMethod method, String body, Writer out, Path path, boolean isDebug) - throws IOException, JsonParseException { - if (!"api".equals(path.getName(0).toString())) { - return 404; - } - - if (path.getNameCount() == 1) { // request for API key - if (method != HttpMethod.POST) { - return 405; - } - if (!ds.config.linkbutton) { - return 10403; - } - - final HueCreateUser userRequest; - userRequest = gson.fromJson(body, HueCreateUser.class); - if (userRequest.devicetype == null || userRequest.devicetype.isEmpty()) { - throw new JsonParseException("devicetype not given"); - } - - String apiKey = userRequest.username; - if (apiKey == null || apiKey.length() == 0) { - apiKey = UUID.randomUUID().toString(); - } - userManagement.addUser(apiKey, userRequest.devicetype); - - try (JsonWriter writer = new JsonWriter(out)) { - HueSuccessResponseCreateUser h = new HueSuccessResponseCreateUser(apiKey); - gson.toJson(Collections.singleton(new HueResponse(h)), new TypeToken>() { - }.getType(), writer); - } - return 200; - } - - updateDataStore(); - - Path userPath = remaining(path); - - return handleUser(method, body, out, userPath.getName(0).toString(), remaining(userPath), path, isDebug); - } - - /** - * Handles /api/config and /api/{user-name} and forwards any deeper path - */ - public int handleUser(HttpMethod method, String body, Writer out, String userName, Path remainingPath, Path fullURI, - boolean isDebug) throws IOException, JsonParseException { - - if ("config".equals(userName)) { // Reduced config - try (JsonWriter writer = new JsonWriter(out)) { - gson.toJson(ds.config, new TypeToken() { - }.getType(), writer); - } - return 200; - } - - if (!userManagement.authorizeUser(userName)) { - if (ds.config.linkbutton && ds.config.createNewUserOnEveryEndpoint) { - userManagement.addUser(userName, "Formerly authorized device"); - } else { - return 403; - } - } - - if (remainingPath.getNameCount() == 0) { /** /api/{username} */ - switch (method) { - case GET: - out.write(gson.toJson(ds)); - return 200; - default: - return 405; - } - - } - - String function = remainingPath.getName(0).toString(); - - switch (function) { - case "lights": - return handleLights(method, body, out, remaining(remainingPath), fullURI, isDebug); - case "groups": - return handleGroups(method, body, out, remaining(remainingPath)); - case "config": - return handleConfig(method, body, out, remaining(remainingPath), userName); - default: - return 404; - } - } - - /** - * Handles /api/{user-name}/config and /api/{user-name}/config/whitelist - * The own whitelisted user can remove itself with a DELETE - */ - public int handleConfig(HttpMethod method, String body, Writer out, Path remainingPath, String authorizedUser) - throws IOException, JsonParseException { - if (remainingPath.getNameCount() == 0) { - switch (method) { - case GET: - out.write(gson.toJson(ds.config)); - return 200; - case PUT: - final HueChangeRequest changes; - changes = gson.fromJson(body, HueChangeRequest.class); - if (changes.devicename != null) { - ds.config.devicename = changes.devicename; - } - if (changes.dhcp != null) { - ds.config.dhcp = changes.dhcp; - } - if (changes.linkbutton != null) { - ds.config.linkbutton = changes.linkbutton; - configManagement.checkPairingTimeout(); - } - configManagement.writeToFile(); - return 200; - default: - return 405; - } - } else if (remainingPath.getNameCount() >= 1 && "whitelist".equals(remainingPath.getName(0).toString())) { - return handleConfigWhitelist(method, out, remaining(remainingPath), authorizedUser); - } else { - return 404; - } - } - - public int handleConfigWhitelist(HttpMethod method, Writer out, Path remainingPath, String authorizedUser) - throws IOException { - switch (remainingPath.getNameCount()) { - case 0: - switch (method) { - case GET: - out.write(gson.toJson(ds.config.whitelist)); - return 200; - default: - return 405; - } - case 1: - String username = remainingPath.getName(0).toString(); - switch (method) { - case GET: - ds.config.whitelist.get(username); - out.write(gson.toJson(ds.config.whitelist)); - return 200; - case DELETE: - // Only own user can be removed - if (username.equals(authorizedUser)) { - userManagement.removeUser(authorizedUser); - return 200; - } else { - return 403; - } - default: - return 405; - } - default: - return 405; - } - } - - @SuppressWarnings({ "null", "unused" }) - public int handleLights(HttpMethod method, String body, Writer out, Path remainingPath, Path fullURI, - boolean isDebug) throws IOException, JsonParseException { - /** /api/{username}/lights */ - if (remainingPath.getNameCount() == 0) { - switch (method) { - case GET: - if (isDebug) { - out.write("Exposed lights:\n\n"); - for (HueDevice hueDevice : ds.lights.values()) { - out.write(hueDevice.toString()); - out.write("\n"); - } - } else { - ds.lights.values().forEach(v -> v.updateState()); - out.write(gson.toJson(ds.lights)); - } - return 200; - case POST: - try (JsonWriter writer = new JsonWriter(out)) { - List responses = new ArrayList<>(); - responses.add(new HueResponse(new HueSuccessResponseStartSearchLights())); - gson.toJson(responses, new TypeToken>() { - }.getType(), writer); - } - return 200; - default: - return 405; - } - } - - String id = remainingPath.getName(0).toString(); - - /** /api/{username}/lights/new */ - if ("new".equals(id)) { - switch (method) { - case GET: - out.write(gson.toJson(new HueNewLights())); - return 200; - default: - return 405; - } - } - - final int hueID; - try { - hueID = new Integer(id); - } catch (NumberFormatException e) { - return 404; - } - - HueDevice hueDevice = ds.lights.get(hueID); - if (hueDevice == null) { - return 404; - } - - /** /api/{username}/lights/{id} */ - if (remainingPath.getNameCount() == 1) { - hueDevice.updateState(); - out.write(gson.toJson(hueDevice)); - return 200; - } - - if (remainingPath.getNameCount() == 2) { - switch (method) { - case PUT: - return handleLightChangeState(fullURI, method, body, out, hueID, hueDevice); - default: - return 405; - } - } - - return 404; - } - - @SuppressWarnings({ "null", "unused" }) - public int handleGroups(HttpMethod method, String body, Writer out, Path remainingPath) throws IOException { - /** /api/{username}/groups */ - if (remainingPath.getNameCount() == 0) { - switch (method) { - case GET: - out.write(gson.toJson(ds.groups)); - return 200; - case POST: - int hueid = ds.generateNextGroupHueID(); - try (JsonWriter writer = new JsonWriter(out)) { - List responses = new ArrayList<>(); - responses.add(new HueResponse(new HueSuccessCreateGroup(hueid))); - gson.toJson(responses, new TypeToken>() { - }.getType(), writer); - } - return 200; - default: - return 405; - } - } - - String id = remainingPath.getName(0).toString(); - - final int hueID; - try { - hueID = new Integer(id); - } catch (NumberFormatException e) { - return 404; - } - - /** /api/{username}/groups/{id} */ - if (remainingPath.getNameCount() == 1) { - Object value = ds.groups.get(hueID); - if (value == null) { - return 404; - } else { - out.write(gson.toJson(value)); - return 200; - } - } - return 404; - } - - /** - * Hue API call to set the state of a light. - * Enpoint: /api/{username}/lights/{id}/state - */ - @SuppressWarnings({ "null", "unused" }) - private int handleLightChangeState(Path fullURI, HttpMethod method, String body, Writer out, int hueID, - HueDevice hueDevice) throws IOException, JsonParseException { - HueStateChange state = gson.fromJson(body, HueStateChange.class); - if (state == null) { - throw new JsonParseException("No state change data received!"); - } - - // logger.debug("Received state change: {}", gson.toJson(state)); - - // Apply new state and collect success, error items - Map successApplied = new TreeMap<>(); - List errorApplied = new ArrayList<>(); - Command command = hueDevice.applyState(state, successApplied, errorApplied); - - // If a command could be created, post it to the framework now - if (command != null) { - logger.debug("sending {} to {}", command, hueDevice.item.getName()); - eventPublisher.post(ItemEventFactory.createCommandEvent(hueDevice.item.getName(), command, "hueemulation")); - } - - // Generate the response. The response consists of a list with an entry each for all - // submitted change requests. If for example "on" and "bri" was send, 2 entries in the response are - // expected. - Path contextPath = fullURI.subpath(2, fullURI.getNameCount() - 1); - List responses = new ArrayList<>(); - successApplied.forEach((t, v) -> { - responses.add(new HueResponse(new HueSuccessResponseStateChanged(contextPath.resolve(t).toString(), v))); - }); - errorApplied.forEach(v -> { - responses.add(new HueResponse(new HueErrorMessage(HueResponse.NOT_AVAILABLE, - contextPath.resolve(v).toString(), "Could not set"))); - }); - - try (JsonWriter writer = new JsonWriter(out)) { - gson.toJson(responses, new TypeToken>() { - }.getType(), writer); - } - return 200; - } - - /** - * Update changing parameters of the data store like the time. - */ - public void updateDataStore() { - ds.config.UTC = LocalDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); - ds.config.localtime = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); - } -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/UserManagement.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/UserManagement.java deleted file mode 100644 index b68e3a3a0684b..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/UserManagement.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal; - -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.storage.Storage; -import org.openhab.io.hueemulation.internal.dto.HueDataStore; -import org.openhab.io.hueemulation.internal.dto.HueUserAuth; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Manages users of this emulated HUE bridge. - * - * @author David Graeff - Initial contribution - */ -@NonNullByDefault -public class UserManagement { - private final Logger logger = LoggerFactory.getLogger(UserManagement.class); - private final HueDataStore dataStore; - private @Nullable Storage storage; - - public UserManagement(HueDataStore ds) { - dataStore = ds; - } - - /** - * Load users from disk - */ - public void loadUsersFromFile(Storage storage) { - boolean storageChanged = this.storage != null && this.storage != storage; - this.storage = storage; - for (String id : storage.getKeys()) { - HueUserAuth userAuth = storage.get(id); - if (userAuth == null) { - continue; - } - dataStore.config.whitelist.put(id, userAuth); - } - if (storageChanged) { - writeToFile(); - } - } - - /** - * Checks if the username exists in the whitelist - */ - @SuppressWarnings("null") - public boolean authorizeUser(String userName) throws IOException { - HueUserAuth userAuth = dataStore.config.whitelist.get(userName); - if (userAuth != null) { - userAuth.lastUseDate = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); - } - - return userAuth != null; - } - - /** - * Adds a user to the whitelist and persist the user file - */ - public synchronized void addUser(String apiKey, String label) throws IOException { - if (!dataStore.config.whitelist.containsKey(apiKey)) { - logger.debug("APIKey {} added", apiKey); - dataStore.config.whitelist.put(apiKey, new HueUserAuth(label)); - writeToFile(); - } - } - - @SuppressWarnings("null") - public synchronized void removeUser(String apiKey) { - HueUserAuth userAuth = dataStore.config.whitelist.remove(apiKey); - if (userAuth != null) { - logger.debug("APIKey {} removed", apiKey); - writeToFile(); - } - } - - /** - * Persist users to storage. - */ - void writeToFile() { - Storage storage = this.storage; - if (storage == null) { - return; - } - storage.getKeys().forEach(key -> storage.remove(key)); - dataStore.config.whitelist.forEach((id, userAuth) -> storage.put(id, userAuth)); - } - - public void resetStorage() { - this.storage = null; - } -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/Utils.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/Utils.java deleted file mode 100644 index 84f67dbe66897..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/Utils.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal; - -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.nio.charset.StandardCharsets; - -import javax.servlet.http.HttpServletResponse; - -import org.eclipse.jdt.annotation.NonNullByDefault; - -/** - * Network utility methods - * - * @author David Graeff - Initial contribution - */ -@NonNullByDefault -public class Utils { - /** - * Sets Hue API Headers - */ - static void setHeaders(HttpServletResponse response) { - response.setCharacterEncoding(StandardCharsets.UTF_8.name()); - response.setHeader("Access-Control-Allow-Origin", "*"); - response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT"); - response.setHeader("Access-Control-Max-Age", "3600"); - response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); - } - - /** - * Try to get the ethernet interface MAC for the network interface that belongs to the given IP address. - * Returns a default MAC on any failure. - * - * @param address IP address - * @return A MAC of the form "00:00:88:00:bb:ee" - */ - static String getMAC(InetAddress address) { - NetworkInterface networkInterface; - final byte[] mac; - try { - networkInterface = NetworkInterface.getByInetAddress(address); - if (networkInterface == null) { - return "00:00:88:00:bb:ee"; - } - mac = networkInterface.getHardwareAddress(); - if (mac == null) { - return "00:00:88:00:bb:ee"; - } - } catch (SocketException e) { - return "00:00:88:00:bb:ee"; - } - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < mac.length; i++) { - sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? ":" : "")); - } - return sb.toString(); - } - -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueAuthorizedConfig.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueAuthorizedConfig.java deleted file mode 100644 index ff922d537f8f5..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueAuthorizedConfig.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal.dto; - -import java.time.ZonedDateTime; -import java.util.Map; -import java.util.TreeMap; - -import org.eclipse.jdt.annotation.NonNullByDefault; - -/** - * Hue API config object - * - * @author David Graeff - Initial contribution - */ -@NonNullByDefault -public class HueAuthorizedConfig extends HueUnauthorizedConfig { - public String uuid = "5673dfa7-272c-4315-9955-252cdd86131c"; - - public String timeformat = "24h"; - public String timezone = ZonedDateTime.now().getOffset().getId().replace("Z", "+00:00"); - public String UTC = "2018-11-10T15:24:23"; - public String localtime = "2018-11-10T16:24:23"; - - public String devicename = "Philips Hue"; - - public String fwversion = "0x262e0500"; - - public boolean rfconnected = true; - public int zigbeechannel = 15; - public boolean linkbutton = false; - public transient boolean createNewUserOnEveryEndpoint = false; - public int panid = 19367; - - public boolean dhcp = true; - public String gateway = "192.168.0.1"; - public String ipaddress = "192.168.0.46"; - public String netmask = "255.255.255.0"; - public int networkopenduration = 60; - - public String proxyaddress = "none"; - public int proxyport = 0; - - public final Map whitelist = new TreeMap<>(); -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueDataStore.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueDataStore.java deleted file mode 100644 index 7c2d077b68f24..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueDataStore.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal.dto; - -import java.util.Collections; -import java.util.Map; -import java.util.TreeMap; - -import org.eclipse.jdt.annotation.NonNullByDefault; - -/** - * Hue data store object. Contains all lights, configuration, user whitelist etc. - * Is used as a data store but also as API DTO. - * - * @author Dan Cunningham - Initial contribution - * @author David Graeff - Add groups,scenes,rules,sensors,resourcelinks and config entries - * - */ -@NonNullByDefault -public class HueDataStore { - public HueAuthorizedConfig config = new HueAuthorizedConfig(); - public TreeMap lights = new TreeMap<>(); - public TreeMap groups = new TreeMap<>(); - public Map scenes = new TreeMap<>(); - public Map rules = new TreeMap<>(); - public Map sensors = new TreeMap<>(); - public Map schedules = new TreeMap<>(); - public Map resourcelinks = Collections.emptyMap(); - - public HueDataStore() { - resetGroupsAndLights(); - } - - public void resetGroupsAndLights() { - groups.clear(); - lights.clear(); - // There must be a group 0 all the time! - groups.put(0, new HueGroup("All lights", null, Collections.emptyMap())); - } - - public int generateNextLightHueID() { - return lights.size() == 0 ? 1 : new Integer(lights.lastKey().intValue() + 1); - } - - public int generateNextGroupHueID() { - return groups.size() == 0 ? 1 : new Integer(groups.lastKey().intValue() + 1); - } - - public static class Dummy { - } -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueDevice.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueDevice.java deleted file mode 100644 index b016e2c8f67d9..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueDevice.java +++ /dev/null @@ -1,419 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal.dto; - -import java.util.List; -import java.util.Map; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.items.Item; -import org.eclipse.smarthome.core.library.types.HSBType; -import org.eclipse.smarthome.core.library.types.OnOffType; -import org.eclipse.smarthome.core.library.types.PercentType; -import org.eclipse.smarthome.core.types.Command; -import org.eclipse.smarthome.core.types.State; -import org.openhab.io.hueemulation.internal.DeviceType; -import org.openhab.io.hueemulation.internal.dto.HueStateColorBulb.ColorMode; - -/** - * Hue API device object - * - * @author Dan Cunningham - Initial contribution - * @author David Graeff - Color lights and plugs - * @author Florian Lentz - XY Support - */ -@NonNullByDefault -public class HueDevice { - public AbstractHueState state = new AbstractHueState(); - public final String type; - public final String modelid; - public final String uniqueid; - public final String manufacturername; - public final @Nullable String productname; - public final String swversion; - public final @Nullable String luminaireuniqueid = null; - public final @Nullable String swconfigid; - public final @Nullable String productid; - public @Nullable Boolean friendsOfHue = true; - public final @Nullable String colorGamut; - public @Nullable Boolean hascolor = null; - - public String name; - /** Associated item UID */ - public transient Item item; - public transient DeviceType deviceType; - - public static class Config { - public final String archetype = "classicbulb"; - public final String function = "functional"; - public final String direction = "omnidirectional"; - }; - - public Config config = new Config(); - - public static class Streaming { - public boolean renderer = false; - public boolean proxy = false; - }; - - public static class Capabilities { - public boolean certified = false; - public final Streaming streaming = new Streaming(); - public final Object control = new Object(); - }; - - public Capabilities capabilities = new Capabilities(); - - /** - * Create a hue device. - * - * @param targetType The state type - * @param item - * @param name - * @param uniqueid - * @param deviceType - */ - public HueDevice(Item item, String uniqueid, DeviceType deviceType) { - String label = item.getLabel(); - this.item = item; - this.deviceType = deviceType; - this.uniqueid = uniqueid; - switch (deviceType) { - case ColorType: - this.name = label != null ? label : ""; - this.type = "Extended color light"; - this.modelid = "LCT010"; - this.colorGamut = "C"; - this.manufacturername = "Philips"; - this.swconfigid = "F921C859"; - this.swversion = "1.15.2_r19181"; - this.productid = "Philips-LCT010-1-A19ECLv4"; - this.productname = null; - this.hascolor = true; - this.capabilities.certified = true; - break; - case WhiteType: - /** Hue White A19 - 3nd gen - white, 2700K only */ - this.name = label != null ? label : ""; - this.type = "Dimmable Light"; - this.modelid = "LWB006"; - this.colorGamut = null; - this.manufacturername = "Philips"; - this.swconfigid = null; - this.swversion = "66012040"; - this.productid = null; - this.hascolor = false; - this.productname = null; - this.capabilities.certified = true; - break; - case WhiteTemperatureType: - this.name = label != null ? label : ""; - this.type = "Color Temperature Light"; - this.modelid = "LTW001"; - this.colorGamut = "2200K-6500K"; - this.manufacturername = "Philips"; - this.swconfigid = null; - this.swversion = "66012040"; - this.productid = null; - this.hascolor = false; - this.productname = null; - this.capabilities.certified = true; - break; - default: - case SwitchType: - /** - * Pretend to be an OSRAM plug, there is no native Philips Hue plug on the market. - * Those are supported by most of the external apps and Alexa. - */ - this.name = label != null ? label : ""; - this.type = "On/Off plug-in unit"; - this.modelid = "Plug 01"; - this.colorGamut = null; - this.manufacturername = "OSRAM"; - this.productname = "On/Off plug"; - this.swconfigid = null; - this.swversion = "V1.04.12"; - this.productid = null; - this.hascolor = false; - this.friendsOfHue = null; - break; - } - - setState(item.getState()); - } - - private void setState(State itemState) { - switch (deviceType) { - case ColorType: - if (itemState instanceof HSBType) { - state = new HueStateColorBulb((HSBType) itemState); - } else if (itemState instanceof PercentType) { - state = new HueStateColorBulb((PercentType) itemState, ((PercentType) itemState).intValue() > 0); - } else if (itemState instanceof OnOffType) { - OnOffType t = (OnOffType) itemState; - state = new HueStateColorBulb(t == OnOffType.ON); - } else { - state = new HueStateColorBulb(new HSBType()); - } - break; - case WhiteType: - case WhiteTemperatureType: - if (itemState instanceof HSBType) { - PercentType brightness = ((HSBType) itemState).getBrightness(); - state = new HueStateBulb(brightness, brightness.intValue() > 0); - } else if (itemState instanceof PercentType) { - PercentType brightness = (PercentType) itemState; - state = new HueStateBulb(brightness, brightness.intValue() > 0); - } else if (itemState instanceof OnOffType) { - OnOffType t = (OnOffType) itemState; - state = new HueStateBulb(t == OnOffType.ON); - } else { - state = new HueStateBulb(new PercentType(0), false); - } - break; - case SwitchType: - default: - if (itemState instanceof OnOffType) { - OnOffType t = (OnOffType) itemState; - state = new HueStatePlug(t == OnOffType.ON); - } else { - state = new HueStatePlug(false); - } - } - } - - private T as(Class type) throws ClassCastException { - return type.cast(state); - } - - /** - * Apply the new received state from the REST PUT request. - * - * @param newState New state - * @param successApplied Output map "state-name"->value: All successfully applied items are added in here - * @param errorApplied Output: All erroneous items are added in here - * @return Return a command computed via the incoming state object. - */ - public @Nullable Command applyState(HueStateChange newState, Map successApplied, - List errorApplied) { - // First synchronize the internal state information with the framework - setState(item.getState()); - - Command command = null; - if (newState.on != null) { - try { - as(HueStatePlug.class).on = newState.on; - command = OnOffType.from(newState.on); - successApplied.put("on", newState.on); - } catch (ClassCastException e) { - errorApplied.add("on"); - } - } - - if (newState.bri != null) { - try { - as(HueStateBulb.class).bri = newState.bri; - command = new PercentType((int) (newState.bri * 100.0 / HueStateBulb.MAX_BRI + 0.5)); - successApplied.put("bri", newState.bri); - } catch (ClassCastException e) { - errorApplied.add("bri"); - } - } - - if (newState.bri_inc != null) { - try { - int newBri = as(HueStateBulb.class).bri + newState.bri_inc; - if (newBri < 0 || newBri > HueStateBulb.MAX_BRI) { - throw new IllegalArgumentException(); - } - command = new PercentType((int) (newBri * 100.0 / HueStateBulb.MAX_BRI + 0.5)); - successApplied.put("bri", newState.bri); - } catch (ClassCastException e) { - errorApplied.add("bri_inc"); - } catch (IllegalArgumentException e) { - errorApplied.add("bri_inc"); - } - } - - if (newState.sat != null) { - - try { - HueStateColorBulb c = as(HueStateColorBulb.class); - c.sat = newState.sat; - c.colormode = ColorMode.hs; - command = c.toHSBType(); - successApplied.put("sat", newState.sat); - } catch (ClassCastException e) { - errorApplied.add("sat"); - } - } - - if (newState.sat_inc != null) { - try { - HueStateColorBulb c = as(HueStateColorBulb.class); - int newV = c.sat + newState.sat_inc; - if (newV < 0 || newV > HueStateColorBulb.MAX_SAT) { - throw new IllegalArgumentException(); - } - c.colormode = ColorMode.hs; - c.sat = newV; - command = c.toHSBType(); - successApplied.put("sat", newState.sat); - } catch (ClassCastException e) { - errorApplied.add("sat_inc"); - } catch (IllegalArgumentException e) { - errorApplied.add("sat_inc"); - } - } - - if (newState.hue != null) { - try { - HueStateColorBulb c = as(HueStateColorBulb.class); - c.colormode = ColorMode.hs; - c.hue = newState.hue; - command = c.toHSBType(); - successApplied.put("hue", newState.hue); - } catch (ClassCastException e) { - errorApplied.add("hue"); - } - } - - if (newState.hue_inc != null) { - try { - HueStateColorBulb c = as(HueStateColorBulb.class); - int newV = c.hue + newState.hue_inc; - if (newV < 0 || newV > HueStateColorBulb.MAX_HUE) { - throw new IllegalArgumentException(); - } - c.colormode = ColorMode.hs; - c.hue = newV; - command = c.toHSBType(); - successApplied.put("hue", newState.hue); - } catch (ClassCastException e) { - errorApplied.add("hue_inc"); - } catch (IllegalArgumentException e) { - errorApplied.add("hue_inc"); - } - } - - if (newState.ct != null) { - try { - // We can't do anything here with a white color temperature. - // The core ESH color type does not support setting it. - - // Adjusting the color temperature implies setting the mode to ct - if (state instanceof HueStateColorBulb) { - HueStateColorBulb c = as(HueStateColorBulb.class); - c.sat = 0; - c.colormode = ColorMode.ct; - command = c.toHSBType(); - } - successApplied.put("colormode", ColorMode.ct); - successApplied.put("sat", 0); - successApplied.put("ct", newState.ct); - } catch (ClassCastException e) { - errorApplied.add("ct"); - } - } - - if (newState.ct_inc != null) { - try { - // We can't do anything here with a white color temperature. - // The core ESH color type does not support setting it. - - // Adjusting the color temperature implies setting the mode to ct - if (state instanceof HueStateColorBulb) { - HueStateColorBulb c = as(HueStateColorBulb.class); - if (c.colormode != ColorMode.ct) { - c.sat = 0; - command = c.toHSBType(); - successApplied.put("colormode", c.colormode); - } - } - successApplied.put("ct", newState.ct); - } catch (ClassCastException e) { - errorApplied.add("ct_inc"); - } - } - - if (newState.transitiontime != null) { - successApplied.put("transitiontime", newState.transitiontime); // Pretend that worked - } - if (newState.alert != null) { - successApplied.put("alert", newState.alert); // Pretend that worked - } - if (newState.effect != null) { - successApplied.put("effect", newState.effect); // Pretend that worked - } - if (newState.xy != null) { - try { - HueStateColorBulb c = as(HueStateColorBulb.class); - c.colormode = ColorMode.xy; - c.bri = as(HueStateBulb.class).bri; - c.xy[0] = newState.xy.get(0); - c.xy[1] = newState.xy.get(1); - command = c.toHSBType(); - successApplied.put("xy", newState.xy); - } catch (ClassCastException e) { - errorApplied.add("xy"); - } - } - if (newState.xy_inc != null) { - try { - HueStateColorBulb c = as(HueStateColorBulb.class); - double newX = c.xy[0] + newState.xy_inc.get(0); - double newY = c.xy[1] + newState.xy_inc.get(1); - if (newX < 0 || newX > 1 || newY < 0 || newY > 1) { - throw new IllegalArgumentException(); - } - c.colormode = ColorMode.xy; - c.bri = as(HueStateBulb.class).bri; - c.xy[0] = newX; - c.xy[1] = newY; - command = c.toHSBType(); - successApplied.put("xy", newState.xy_inc); - } catch (ClassCastException e) { - errorApplied.add("xy_inc"); - } catch (IllegalArgumentException e) { - errorApplied.add("xy_inc"); - } - } - - return command; - } - - public void updateItem(Item element) { - item = element; - setState(item.getState()); - - // Just update the item label and item reference - String label = element.getLabel(); - if (label != null) { - name = label; - } - } - - /** - * Synchronizes the item state with the hue state object - */ - public void updateState() { - setState(item.getState()); - } - - @Override - public String toString() { - StringBuilder b = new StringBuilder(); - return b.append(name).append(": ").append(type).append("\n\t").append(state.toString()).toString(); - } -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueGroup.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueGroup.java deleted file mode 100644 index 4f76ca8800cee..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueGroup.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal.dto; - -import java.lang.reflect.Type; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.items.GroupItem; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; - -/** - * Hue API group object - * - * @author Dan Cunningham - Initial contribution - */ -@NonNullByDefault -public class HueGroup { - public HueStateColorBulb action = new HueStateColorBulb(); - public String type = "LightGroup"; - public String name; - public List lights = Collections.emptyList(); - - public transient @Nullable GroupItem groupItem; - public transient Map itemUIDtoHueID; - - public HueGroup(String name, @Nullable GroupItem groupItem, Map itemUIDtoHueID) { - this.name = name; - this.groupItem = groupItem; - this.itemUIDtoHueID = itemUIDtoHueID; - } - - public void updateItem(GroupItem element) { - groupItem = element; - } - - /** - * This custom serializer computes the {@link HueGroup#lights} list, before serializing. - * It does so, by looking up all item members of the references groupItem and map them to - * either a known hue ID or filtering them out. - * - */ - @NonNullByDefault({}) - public static class Serializer implements JsonSerializer { - - @SuppressWarnings("null") - @Override - public JsonElement serialize(HueGroup product, Type type, JsonSerializationContext jsc) { - GroupItem item = product.groupItem; - if (item != null) { - product.lights = item.getMembers().stream().map(gitem -> product.itemUIDtoHueID.get(gitem.getUID())) - .filter(id -> id != null).map(e -> String.valueOf(e)).collect(Collectors.toList()); - } - - JsonObject o = new JsonObject(); - o.addProperty("name", product.name); - o.addProperty("type", product.type); - o.add("action", jsc.serialize(product.action)); - o.add("lights", jsc.serialize(product.lights)); - return o; - } - } - -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueResponse.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueResponse.java deleted file mode 100644 index ebbc7ffc28e50..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueResponse.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal.dto.response; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * Hue API response base type - * - * @author David Graeff - Initial contribution - */ -@NonNullByDefault -public class HueResponse { - public static final int UNAUTHORIZED = 1; - public static final int INVALID_JSON = 2; - public static final int NOT_AVAILABLE = 3; - public static final int METHOD_NOT_ALLOWED = 4; - public static final int LINK_BUTTON_NOT_PRESSED = 101; - public static final int INTERNAL_ERROR = 901; - - public final @Nullable HueErrorMessage error; - public final @Nullable HueSuccessResponse success; - - public HueResponse(HueErrorMessage error) { - this.error = error; - this.success = null; - } - - public HueResponse(HueSuccessResponse success) { - this.error = null; - this.success = success; - } - - public static class HueErrorMessage { - public final String address; - public final String description; - public final int type; - - public HueErrorMessage(int type, String address, String description) { - this.type = type; - this.address = address; - this.description = description; - } - } -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseCreateUser.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseCreateUser.java deleted file mode 100644 index d0624e2bd3694..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseCreateUser.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal.dto.response; - -/** - * This object describes the right hand side of "success". - * The response looks like this: - * - *
- * {
- *   "success":{
- *      "username": "-the-username-"
- *   }
- * }
- * 
- * - * @author David Graeff - Initial contribution - */ -public class HueSuccessResponseCreateUser implements HueSuccessResponse { - public String username; - - public HueSuccessResponseCreateUser(String username) { - this.username = username; - } -} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseStartSearchLights.java b/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseStartSearchLights.java deleted file mode 100644 index 2de79c2f9df99..0000000000000 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseStartSearchLights.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.io.hueemulation.internal.dto.response; - -/** - * This object describes the right hand side of "success". - * - * @author David Graeff - Initial contribution - */ -public class HueSuccessResponseStartSearchLights implements HueSuccessResponse { - public HueSuccessResponseStartSearchLights() { - } -} diff --git a/addons/io/org.openhab.io.openhabcloud/.classpath b/addons/io/org.openhab.io.openhabcloud/.classpath deleted file mode 100644 index 606af384c6334..0000000000000 --- a/addons/io/org.openhab.io.openhabcloud/.classpath +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/addons/io/org.openhab.io.openhabcloud/.project b/addons/io/org.openhab.io.openhabcloud/.project deleted file mode 100644 index 0d4217198f5cd..0000000000000 --- a/addons/io/org.openhab.io.openhabcloud/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.io.openhabcloud - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/io/org.openhab.io.openhabcloud/META-INF/MANIFEST.MF b/addons/io/org.openhab.io.openhabcloud/META-INF/MANIFEST.MF deleted file mode 100644 index 3ad991699f396..0000000000000 --- a/addons/io/org.openhab.io.openhabcloud/META-INF/MANIFEST.MF +++ /dev/null @@ -1,56 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.io.openhabcloud -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: - ., - lib/engine.io-client-1.0.0.jar, - lib/json-20180813.jar, - lib/okhttp-3.12.1.jar, - lib/okio-1.15.0.jar, - lib/socket.io-client-1.0.0.jar -Bundle-ManifestVersion: 2 -Bundle-Name: openHAB Cloud Connector Bundle -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.io.openhabcloud -Bundle-Vendor: openHAB.org -Bundle-Version: 2.5.0.qualifier -Export-Package: org.openhab.io.openhabcloud -Ignore-Package: org.openhab.io.openhabcloud.internal -Import-Package: - javax.net, - javax.net.ssl, - org.apache.commons.io, - org.apache.commons.lang, - org.eclipse.emf.common.notify, - org.eclipse.emf.common.util, - org.eclipse.emf.ecore, - org.eclipse.emf.ecore.resource, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.jetty.client, - org.eclipse.jetty.client.api, - org.eclipse.jetty.client.util, - org.eclipse.jetty.http, - org.eclipse.jetty.io, - org.eclipse.jetty.util, - org.eclipse.jetty.util.component, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.common, - org.eclipse.smarthome.core.events, - org.eclipse.smarthome.core.id, - org.eclipse.smarthome.core.items, - org.eclipse.smarthome.core.items.events, - org.eclipse.smarthome.core.library.items, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.net, - org.eclipse.smarthome.core.persistence, - org.eclipse.smarthome.core.service, - org.eclipse.smarthome.core.types, - org.eclipse.smarthome.model.script.engine.action, - org.openhab.core, - org.openhab.io.openhabcloud, - org.osgi.framework, - org.osgi.service.cm, - org.osgi.service.event, - org.slf4j -Private-Package: org.openhab.io.openhabcloud.internal -Service-Component: OSGI-INF/*.xml diff --git a/addons/io/org.openhab.io.openhabcloud/OSGI-INF/.gitignore b/addons/io/org.openhab.io.openhabcloud/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/io/org.openhab.io.openhabcloud/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/io/org.openhab.io.openhabcloud/README.md b/addons/io/org.openhab.io.openhabcloud/README.md deleted file mode 100644 index 22888ed455b4d..0000000000000 --- a/addons/io/org.openhab.io.openhabcloud/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# openHAB Cloud Connector - -The openHAB Cloud Connector allows connecting the local openHAB runtime to a remote [openHAB Cloud](https://github.com/openhab/openhab-cloud/blob/master/README.md) instance, such as [myopenHAB.org](http://www.myopenHAB.org), which is an instance of the -openHAB Cloud service hosted by the [openHAB Foundation](http://www.openhabfoundation.org/). - -## Features - -The openHAB Cloud service (and thus the connector to it) is useful for different use cases: - -* It allows remote access to local openHAB instances without having to expose ports to the Internet or to require a complex VPN setup. -* It serves as a connector to Google Cloud Messaging (GCM) and Apple Push Notifications (APN) for pushing notifications to mobile phone apps. -* It brings integration possibilities with services that require an OAuth2 authentication against a web server, such as IFTTT or Amazon Alexa Skills. - -## UUID and Secret - -To authenticate with the openHAB Cloud your local openHAB runtime generates two values, which need to be entered in your account settings of the openHAB Cloud service. The first one is a unique identifier, which allows to identify your runtime. One can think of it as something similar like a username for the cloud authentication. The second one is a random secret key which serves as a password. Both values are written to the local file system. If you loose these files for some reason, openHAB will automatically generates new ones. You will then have to reconfigure UUID and secret in the openHAB Cloud service under the _My account_ section. - -Location of UUID and Secret: - -|File | regular Installation | APT Installation | -|-----|----------------------|------------------| -|UUID | userdata/uuid | /var/lib/openhab2/uuid | -|Secret | userdata/openhabcloud/secret | /var/lib/openhab2/openhabcloud/secret | - -## Configuration - -After installing this add-on, you will find configuration options in the Paper UI under _Configuration->Services->IO->openHAB Cloud_: - -![Configuration](doc/cfg.png) - -Please note, that you don't need to expose all your items in this settings dialog. -You just need to expose those items, which you want to be accessible by a third party service. - -Alternatively, you can configure the settings in the file `conf/services/openhabcloud.cfg`: - -``` -############################## openHAB Cloud Connector ############################# - -# The URL of the openHAB Cloud service to connect to. -# Optional, default is set to the service offered by the openHAB Foundation -# (https://myopenhab.org/) -#baseURL= - -# Defines the mode in which you want to operate the connector. -# Possible values are: -# - notification: Only push notifications are enabled, no remote access is allowed. -# - remote: Push notifications and remote access are enabled. -# Optional, default is 'remote'. -#mode= - -# A comma-separated list of items to be exposed to external services like IFTTT. -# Events of those items are pushed to the openHAB Cloud and commands received for -# these items from the openHAB Cloud service are accepted and sent to the local bus. -# Optional, default is an empty list. -#expose= -``` - -Note: The exposed items will show up after they receive an update to their state. diff --git a/addons/io/org.openhab.io.openhabcloud/build.properties b/addons/io/org.openhab.io.openhabcloud/build.properties deleted file mode 100644 index 29a8f0d57b619..0000000000000 --- a/addons/io/org.openhab.io.openhabcloud/build.properties +++ /dev/null @@ -1,9 +0,0 @@ -source.. = src/main/java -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - lib/,\ - NOTICE - diff --git a/addons/io/org.openhab.io.openhabcloud/lib/engine.io-client-1.0.0.jar b/addons/io/org.openhab.io.openhabcloud/lib/engine.io-client-1.0.0.jar deleted file mode 100644 index 386538fd71832..0000000000000 Binary files a/addons/io/org.openhab.io.openhabcloud/lib/engine.io-client-1.0.0.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.openhabcloud/lib/json-20180813.jar b/addons/io/org.openhab.io.openhabcloud/lib/json-20180813.jar deleted file mode 100644 index 592f08c4d6904..0000000000000 Binary files a/addons/io/org.openhab.io.openhabcloud/lib/json-20180813.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.openhabcloud/lib/okhttp-3.12.1.jar b/addons/io/org.openhab.io.openhabcloud/lib/okhttp-3.12.1.jar deleted file mode 100644 index 730ca5499bbae..0000000000000 Binary files a/addons/io/org.openhab.io.openhabcloud/lib/okhttp-3.12.1.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.openhabcloud/lib/okio-1.15.0.jar b/addons/io/org.openhab.io.openhabcloud/lib/okio-1.15.0.jar deleted file mode 100644 index ab8ab73045bf5..0000000000000 Binary files a/addons/io/org.openhab.io.openhabcloud/lib/okio-1.15.0.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.openhabcloud/lib/socket.io-client-1.0.0.jar b/addons/io/org.openhab.io.openhabcloud/lib/socket.io-client-1.0.0.jar deleted file mode 100644 index 0ed4bade7bfd4..0000000000000 Binary files a/addons/io/org.openhab.io.openhabcloud/lib/socket.io-client-1.0.0.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.openhabcloud/pom.xml b/addons/io/org.openhab.io.openhabcloud/pom.xml deleted file mode 100644 index c0067144dcb52..0000000000000 --- a/addons/io/org.openhab.io.openhabcloud/pom.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - 4.0.0 - - - org.openhab.io - pom - 2.5.0-SNAPSHOT - - - org.openhab.io.openhabcloud - eclipse-plugin - - openHAB Cloud Connector - - - - org.openhab.core.bundles - org.openhab.core - ${ohc.version} - - - - diff --git a/addons/io/org.openhab.io.transport.feed/.classpath b/addons/io/org.openhab.io.transport.feed/.classpath deleted file mode 100644 index 3777561240103..0000000000000 --- a/addons/io/org.openhab.io.transport.feed/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/addons/io/org.openhab.io.transport.feed/.project b/addons/io/org.openhab.io.transport.feed/.project deleted file mode 100644 index f5799628194ea..0000000000000 --- a/addons/io/org.openhab.io.transport.feed/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.openhab.io.transport.feed - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/io/org.openhab.io.transport.feed/META-INF/MANIFEST.MF b/addons/io/org.openhab.io.transport.feed/META-INF/MANIFEST.MF deleted file mode 100644 index bed8b856cf970..0000000000000 --- a/addons/io/org.openhab.io.transport.feed/META-INF/MANIFEST.MF +++ /dev/null @@ -1,28 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.io.transport.feed -Bundle-Classpath: lib/jdom2-2.0.6.jar,lib/rome-1.7.0.jar,lib/rome-utils- - 1.7.0.jar,. -Bundle-ManifestVersion: 2 -Bundle-Name: Feed Transport bundle -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.io.transport.feed -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Export-Package: - com.rometools.rome.feed, - com.rometools.rome.feed.atom, - com.rometools.rome.feed.module, - com.rometools.rome.feed.rss, - com.rometools.rome.feed.synd, - com.rometools.rome.io, - com.rometools.utils -Import-Package: - com.rometools.rome.feed, - com.rometools.rome.feed.atom, - com.rometools.rome.feed.module, - com.rometools.rome.feed.rss, - com.rometools.rome.feed.synd, - com.rometools.rome.io, - com.rometools.utils, - org.eclipse.jdt.annotation;resolution:=optional, - org.slf4j diff --git a/addons/io/org.openhab.io.transport.feed/NOTICE b/addons/io/org.openhab.io.transport.feed/NOTICE deleted file mode 100644 index 861a8ff648ba2..0000000000000 --- a/addons/io/org.openhab.io.transport.feed/NOTICE +++ /dev/null @@ -1,25 +0,0 @@ -This content is produced and maintained by the openHAB project. - -* Project home: https://www.openhab.org - -== Declared Project Licenses - -This program and the accompanying materials are made available under the terms -of the Eclipse Public License 2.0 which is available at -https://www.eclipse.org/legal/epl-2.0/. - -== Source Code - -https://github.com/openhab/openhab2-addons - -== Third-party Content - -jdom -* License: BSD/Apache style License -* Project: http://www.jdom.org/ -* Source: https://github.com/hunterhacker/jdom/ - -rome -* License: Apache 2.0 License -* Project: http://rometools.github.io/rome/ -* Source: https://github.com/rometools/rome diff --git a/addons/io/org.openhab.io.transport.feed/README.md b/addons/io/org.openhab.io.transport.feed/README.md deleted file mode 100644 index 0bad0a5b8d7ef..0000000000000 --- a/addons/io/org.openhab.io.transport.feed/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# RSS/Atom Feed transport - -This transport provides the ROME APIs to interact and publish RSS/Atom Feeds. -From the ROME Website: - -> ROME is a Java framework for RSS and Atom feeds. -> It's open source and licensed under the Apache 2.0 license. - -> ROME includes a set of parsers and generators for the various flavors of syndication feeds, -> as well as converters to convert from one format to another. -> The parsers can give you back Java objects that are either specific for the format you want to work with, -> or a generic normalized SyndFeed class that lets you work on with the data without bothering about the incoming or outgoing feed type. - -Example usage: - -```java -FeedFetcherCache feedInfoCache = HashMapFeedInfoCache.getInstance(); -FeedFetcher feedFetcher = new HttpURLFeedFetcher(feedInfoCache); -SyndFeed feed = feedFetcher.retrieveFeed(new URL("http://blogs.sun.com/roller/rss/pat")); -System.out.println(feed); -``` diff --git a/addons/io/org.openhab.io.transport.feed/build.properties b/addons/io/org.openhab.io.transport.feed/build.properties deleted file mode 100644 index 7b672b650199c..0000000000000 --- a/addons/io/org.openhab.io.transport.feed/build.properties +++ /dev/null @@ -1,4 +0,0 @@ -bin.includes = META-INF/,\ - .,\ - lib/,\ - NOTICE diff --git a/addons/io/org.openhab.io.transport.feed/lib/jdom2-2.0.6.jar b/addons/io/org.openhab.io.transport.feed/lib/jdom2-2.0.6.jar deleted file mode 100644 index 2850ca10cf9c9..0000000000000 Binary files a/addons/io/org.openhab.io.transport.feed/lib/jdom2-2.0.6.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.transport.feed/lib/rome-1.7.0.jar b/addons/io/org.openhab.io.transport.feed/lib/rome-1.7.0.jar deleted file mode 100644 index dd0891ba74b88..0000000000000 Binary files a/addons/io/org.openhab.io.transport.feed/lib/rome-1.7.0.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.transport.feed/lib/rome-utils-1.7.0.jar b/addons/io/org.openhab.io.transport.feed/lib/rome-utils-1.7.0.jar deleted file mode 100644 index ef5e3e0014a4a..0000000000000 Binary files a/addons/io/org.openhab.io.transport.feed/lib/rome-utils-1.7.0.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.transport.feed/pom.xml b/addons/io/org.openhab.io.transport.feed/pom.xml deleted file mode 100644 index 09c0ee9d90c06..0000000000000 --- a/addons/io/org.openhab.io.transport.feed/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 4.0.0 - - - org.openhab.io - pom - 2.5.0-SNAPSHOT - - - org.openhab.io.transport.feed - eclipse-plugin - - Transport Feed bundle - - diff --git a/addons/io/org.openhab.io.transport.modbus.test/.classpath b/addons/io/org.openhab.io.transport.modbus.test/.classpath deleted file mode 100644 index 95c509aebd2e1..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus.test/.classpath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/addons/io/org.openhab.io.transport.modbus.test/.gitignore b/addons/io/org.openhab.io.transport.modbus.test/.gitignore deleted file mode 100644 index d027396de4e40..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus.test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/.settings/ diff --git a/addons/io/org.openhab.io.transport.modbus.test/.project b/addons/io/org.openhab.io.transport.modbus.test/.project deleted file mode 100644 index 903b3dac7fbd7..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus.test/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.io.transport.modbus.test - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/io/org.openhab.io.transport.modbus.test/META-INF/MANIFEST.MF b/addons/io/org.openhab.io.transport.modbus.test/META-INF/MANIFEST.MF deleted file mode 100644 index f3639bb5a6d75..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus.test/META-INF/MANIFEST.MF +++ /dev/null @@ -1,37 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.io.transport.modbus.test -Bundle-ManifestVersion: 2 -Fragment-Host: org.openhab.io.transport.modbus -Bundle-Name: Modbus Binding Tests -Bundle-SymbolicName: org.openhab.io.transport.modbus.test;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: com.google.common.collect, - net.bytebuddy.dynamic.loading, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.discovery, - org.eclipse.smarthome.config.discovery.inbox, - org.eclipse.smarthome.core.items, - org.eclipse.smarthome.core.library.items, - org.eclipse.smarthome.core.scheduler, - org.eclipse.smarthome.core.thing.link, - org.eclipse.smarthome.test, - org.eclipse.smarthome.test.java, - org.eclipse.smarthome.test.storage, - org.hamcrest;core=split, - org.hamcrest.collection, - org.junit, - org.junit.experimental.categories, - org.junit.rules, - org.junit.runner, - org.junit.runners, - org.mockito, - org.mockito.invocation, - org.mockito.mock, - org.mockito.stubbing, - org.objenesis, - org.openhab.io.transport.modbus, - org.osgi.framework, - org.osgi.service.component, - org.slf4j diff --git a/addons/io/org.openhab.io.transport.modbus.test/NOTICE b/addons/io/org.openhab.io.transport.modbus.test/NOTICE deleted file mode 100644 index 4c20ef446c1e4..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus.test/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -This content is produced and maintained by the openHAB project. - -* Project home: https://www.openhab.org - -== Declared Project Licenses - -This program and the accompanying materials are made available under the terms -of the Eclipse Public License 2.0 which is available at -https://www.eclipse.org/legal/epl-2.0/. - -== Source Code - -https://github.com/openhab/openhab2-addons diff --git a/addons/io/org.openhab.io.transport.modbus.test/build.properties b/addons/io/org.openhab.io.transport.modbus.test/build.properties deleted file mode 100644 index e4b53fa906f0e..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus.test/build.properties +++ /dev/null @@ -1,4 +0,0 @@ -source.. = src/test/java/ -bin.includes = META-INF/,\ - .,\ - NOTICE \ No newline at end of file diff --git a/addons/io/org.openhab.io.transport.modbus.test/org.openhab.io.transport.modbus.test.launch b/addons/io/org.openhab.io.transport.modbus.test/org.openhab.io.transport.modbus.test.launch deleted file mode 100644 index 45b25cc0e8c18..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus.test/org.openhab.io.transport.modbus.test.launch +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/addons/io/org.openhab.io.transport.modbus.test/pom.xml b/addons/io/org.openhab.io.transport.modbus.test/pom.xml deleted file mode 100644 index 031d84996543e..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus.test/pom.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - 4.0.0 - - - org.openhab.io - pom - 2.5.0-SNAPSHOT - - - org.openhab.io.transport.modbus.test - openHAB Modbus Transport Test - eclipse-test-plugin - - - - - ${tycho-groupid} - tycho-surefire-plugin - - junit47 - - - - eclipse-plugin - ch.qos.logback.classic - 0.0.0 - - - eclipse-plugin - ch.qos.logback.core - 0.0.0 - - - eclipse-plugin - ch.qos.logback.slf4j - 0.0.0 - - - - 4 - true - - false - - - - - - diff --git a/addons/io/org.openhab.io.transport.modbus/.classpath b/addons/io/org.openhab.io.transport.modbus/.classpath deleted file mode 100644 index 2699eee530af1..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/addons/io/org.openhab.io.transport.modbus/.gitignore b/addons/io/org.openhab.io.transport.modbus/.gitignore deleted file mode 100644 index d027396de4e40..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/.settings/ diff --git a/addons/io/org.openhab.io.transport.modbus/.project b/addons/io/org.openhab.io.transport.modbus/.project deleted file mode 100644 index 4fd8ce8439c7b..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.io.transport.modbus - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/io/org.openhab.io.transport.modbus/META-INF/MANIFEST.MF b/addons/io/org.openhab.io.transport.modbus/META-INF/MANIFEST.MF deleted file mode 100644 index b7610961dd763..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus/META-INF/MANIFEST.MF +++ /dev/null @@ -1,29 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.io.transport.modbus -Bundle-ManifestVersion: 2 -Bundle-Name: openHAB MODBUS Transport Bundle -Bundle-SymbolicName: org.openhab.io.transport.modbus -Bundle-Version: 2.5.0.qualifier -Bundle-Vendor: openHAB -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Export-Package: - org.openhab.io.transport.modbus, - org.openhab.io.transport.modbus.endpoint, - org.openhab.io.transport.modbus.json -Import-Package: com.google.gson, - gnu.io, - org.apache.commons.io, - org.apache.commons.lang, - org.apache.commons.lang.builder, - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.core.common, - org.eclipse.smarthome.core.library.types, - org.eclipse.smarthome.core.scheduler, - org.eclipse.smarthome.core.types, - org.slf4j -Bundle-DocURL: http://www.openhab.org -Service-Component: OSGI-INF/*.xml -Bundle-ClassPath: ., - lib/commons-pool2-2.4.3.jar, - lib/jamod-1.2.3.OH.jar -Bundle-ActivationPolicy: lazy diff --git a/addons/io/org.openhab.io.transport.modbus/NOTICE b/addons/io/org.openhab.io.transport.modbus/NOTICE deleted file mode 100644 index 4c20ef446c1e4..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -This content is produced and maintained by the openHAB project. - -* Project home: https://www.openhab.org - -== Declared Project Licenses - -This program and the accompanying materials are made available under the terms -of the Eclipse Public License 2.0 which is available at -https://www.eclipse.org/legal/epl-2.0/. - -== Source Code - -https://github.com/openhab/openhab2-addons diff --git a/addons/io/org.openhab.io.transport.modbus/OSGI-INF/.gitignore b/addons/io/org.openhab.io.transport.modbus/OSGI-INF/.gitignore deleted file mode 100644 index b878e882aca37..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.xml diff --git a/addons/io/org.openhab.io.transport.modbus/build.properties b/addons/io/org.openhab.io.transport.modbus/build.properties deleted file mode 100644 index ca741876a5159..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus/build.properties +++ /dev/null @@ -1,11 +0,0 @@ -bin.includes = META-INF/,\ - OSGI-INF/,\ - .,\ - lib/commons-pool2-2.4.3.jar,\ - lib/jamod-1.2.3.OH.jar,\ - NOTICE -jars.compile.order = . -source.. = src/main/java/ -output.. = target/classes -jars.extra.classpath = lib/commons-pool2-2.4.3.jar,\ - lib/jamod-1.2.3.OH.jar diff --git a/addons/io/org.openhab.io.transport.modbus/lib/commons-pool2-2.4.3.jar b/addons/io/org.openhab.io.transport.modbus/lib/commons-pool2-2.4.3.jar deleted file mode 100644 index d951e1cd6dff2..0000000000000 Binary files a/addons/io/org.openhab.io.transport.modbus/lib/commons-pool2-2.4.3.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.transport.modbus/lib/jamod-1.2.3.OH.jar b/addons/io/org.openhab.io.transport.modbus/lib/jamod-1.2.3.OH.jar deleted file mode 100644 index c04ef315e64a3..0000000000000 Binary files a/addons/io/org.openhab.io.transport.modbus/lib/jamod-1.2.3.OH.jar and /dev/null differ diff --git a/addons/io/org.openhab.io.transport.modbus/pom.xml b/addons/io/org.openhab.io.transport.modbus/pom.xml deleted file mode 100644 index 05bbab67c938f..0000000000000 --- a/addons/io/org.openhab.io.transport.modbus/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - 4.0.0 - - - org.openhab.io - pom - 2.5.0-SNAPSHOT - - - org.openhab.io.transport.modbus - - openHAB Modbus Transport - - eclipse-plugin - - diff --git a/addons/io/pom.xml b/addons/io/pom.xml deleted file mode 100644 index b6cac6e907c5a..0000000000000 --- a/addons/io/pom.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - 4.0.0 - - - org.openhab.addons - pom - 2.5.0-SNAPSHOT - - - org.openhab.io - pom - - pom - - openHAB I/O Add-Ons - - - org.openhab.io.azureiothub - org.openhab.io.homekit - org.openhab.io.hueemulation - org.openhab.io.hueemulation.test - org.openhab.io.openhabcloud - org.openhab.io.transport.feed - org.openhab.io.transport.modbus - org.openhab.io.transport.modbus.test - - - diff --git a/addons/pom.xml b/addons/pom.xml deleted file mode 100644 index 493a87ac4b02b..0000000000000 --- a/addons/pom.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - 4.0.0 - - - org.openhab - pom-addons2 - 2.5.0-SNAPSHOT - ../poms/tycho - - - org.openhab.addons - pom - - pom - - openHAB Add-ons - - - binding - io - voice - - diff --git a/addons/voice/org.openhab.voice.kaldi/.classpath b/addons/voice/org.openhab.voice.kaldi/.classpath deleted file mode 100644 index 22dd4e4fab6de..0000000000000 --- a/addons/voice/org.openhab.voice.kaldi/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/addons/voice/org.openhab.voice.kaldi/.project b/addons/voice/org.openhab.voice.kaldi/.project deleted file mode 100644 index 9495da6dc6ab4..0000000000000 --- a/addons/voice/org.openhab.voice.kaldi/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.voice.kaldi - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/voice/org.openhab.voice.kaldi/META-INF/MANIFEST.MF b/addons/voice/org.openhab.voice.kaldi/META-INF/MANIFEST.MF deleted file mode 100644 index 91a20542cf681..0000000000000 --- a/addons/voice/org.openhab.voice.kaldi/META-INF/MANIFEST.MF +++ /dev/null @@ -1,19 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.voice.kaldi -Bundle-ActivationPolicy: lazy -Bundle-ClassPath: - ., - lib/net-speech-api-0.2.0.jar -Bundle-ManifestVersion: 2 -Bundle-Name: Kaldi Speech-to-Text -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-SymbolicName: org.openhab.voice.kaldi;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.0.0.qualifier -Import-Package: - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.core.audio, - org.eclipse.smarthome.core.voice, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/voice/org.openhab.voice.kaldi/NOTICE b/addons/voice/org.openhab.voice.kaldi/NOTICE deleted file mode 100644 index 994ecef4993e3..0000000000000 --- a/addons/voice/org.openhab.voice.kaldi/NOTICE +++ /dev/null @@ -1,20 +0,0 @@ -This content is produced and maintained by the openHAB project. - -* Project home: https://www.openhab.org - -== Declared Project Licenses - -This program and the accompanying materials are made available under the terms -of the Eclipse Public License 2.0 which is available at -https://www.eclipse.org/legal/epl-2.0/. - -== Source Code - -https://github.com/openhab/openhab2-addons - -== Third-party Content - -net-speech-api -* License: LGPL v3.0 License -* Project: https://github.com/Kaljurand/net-speech-api -* Source: https://github.com/Kaljurand/net-speech-api diff --git a/addons/voice/org.openhab.voice.kaldi/OSGI-INF/.gitignore b/addons/voice/org.openhab.voice.kaldi/OSGI-INF/.gitignore deleted file mode 100644 index 6722cd96e785a..0000000000000 --- a/addons/voice/org.openhab.voice.kaldi/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.xml diff --git a/addons/voice/org.openhab.voice.kaldi/README.md b/addons/voice/org.openhab.voice.kaldi/README.md deleted file mode 100644 index 48e29e172e078..0000000000000 --- a/addons/voice/org.openhab.voice.kaldi/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Kaldi TTS - -Kaldi is a speech recognition toolkit, freely available under the Apache License. - -Kaldi aims to provide software that is flexible and extensible. -It supports linear transforms, MMI, boosted MMI and MCE discriminative training, -feature-space discriminative training, and deep neural networks. - -Find Kaldi here: http://kaldi-asr.org/. -The github project is: https://github.com/kaldi-asr/kaldi. diff --git a/addons/voice/org.openhab.voice.kaldi/build.properties b/addons/voice/org.openhab.voice.kaldi/build.properties deleted file mode 100644 index 2ea2b43456ba0..0000000000000 --- a/addons/voice/org.openhab.voice.kaldi/build.properties +++ /dev/null @@ -1,8 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - lib/net-speech-api-0.2.0.jar,\ - NOTICE - diff --git a/addons/voice/org.openhab.voice.kaldi/lib/net-speech-api-0.2.0.jar b/addons/voice/org.openhab.voice.kaldi/lib/net-speech-api-0.2.0.jar deleted file mode 100644 index e391ba19777b3..0000000000000 Binary files a/addons/voice/org.openhab.voice.kaldi/lib/net-speech-api-0.2.0.jar and /dev/null differ diff --git a/addons/voice/org.openhab.voice.kaldi/pom.xml b/addons/voice/org.openhab.voice.kaldi/pom.xml deleted file mode 100644 index f4b4c70c4446e..0000000000000 --- a/addons/voice/org.openhab.voice.kaldi/pom.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - 4.0.0 - - - pom - org.openhab.voice - 2.3.0-SNAPSHOT - - - org.openhab.voice.kaldi - eclipse-plugin - - Kaldi Speech-to-Text - - diff --git a/addons/voice/org.openhab.voice.kaldi/src/main/java/org/openhab/voice/kaldi/internal/KaldiSTTService.java b/addons/voice/org.openhab.voice.kaldi/src/main/java/org/openhab/voice/kaldi/internal/KaldiSTTService.java deleted file mode 100644 index a9a12c9f4d9f8..0000000000000 --- a/addons/voice/org.openhab.voice.kaldi/src/main/java/org/openhab/voice/kaldi/internal/KaldiSTTService.java +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.voice.kaldi.internal; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.HashSet; -import java.util.Locale; -import java.util.Set; - -import org.eclipse.smarthome.core.audio.AudioFormat; -import org.eclipse.smarthome.core.audio.AudioStream; -import org.eclipse.smarthome.core.voice.STTException; -import org.eclipse.smarthome.core.voice.STTListener; -import org.eclipse.smarthome.core.voice.STTService; -import org.eclipse.smarthome.core.voice.STTServiceHandle; -import org.osgi.service.component.annotations.Component; - -import ee.ioc.phon.netspeechapi.duplex.WsDuplexRecognitionSession; - -/** - * This is a STT service implementation using Kaldi. - * - * @author Kelly Davis - Initial contribution and API - * - */ -@Component -public class KaldiSTTService implements STTService { - - /** - * WebSocket URL to the head node of the Kaldi server cluster - */ - private static final String kaldiWebSocketURL = "ws://52.37.26.79:8888/client/ws/speech"; - - /** - * Set of supported locales - */ - private final HashSet locales = initLocales(); - - /** - * Set of supported audio formats - */ - private final HashSet audioFormats = initAudioFormats(); - - /** - * {@inheritDoc} - */ - @Override - public Set getSupportedLocales() { - return this.locales; - } - - /** - * {@inheritDoc} - */ - @Override - public Set getSupportedFormats() { - return this.audioFormats; - } - - /** - * {@inheritDoc} - */ - @Override - public STTServiceHandle recognize(STTListener sttListener, AudioStream audioStream, Locale locale, - Set grammars) throws STTException { - // Validate arguments - if (null == sttListener) { - throw new IllegalArgumentException("The passed STTListener is null"); - } - if (null == audioStream) { - throw new IllegalArgumentException("The passed AudioSource is null"); - } - boolean isAudioFormatValid = false; - AudioFormat audioFormat = audioStream.getFormat(); - for (AudioFormat currentAudioFormat : this.audioFormats) { - if (currentAudioFormat.isCompatible(audioFormat)) { - isAudioFormatValid = true; - break; - } - } - if (!isAudioFormatValid) { - throw new IllegalArgumentException("The passed AudioSource's AudioFormat is unsupported"); - } - if (null == audioFormat.getBitRate()) { - throw new IllegalArgumentException("The passed AudioSource's AudioFormat's bit rate is not set"); - } - if (!this.locales.contains(locale)) { - throw new IllegalArgumentException("The passed Locale is unsupported"); - } - // Note: Currently Kaldi doesn't use grammars. Thus grammars isn't validated - - // Setup WsDuplexRecognitionSession - WsDuplexRecognitionSession recognitionSession; - try { - recognitionSession = new WsDuplexRecognitionSession(kaldiWebSocketURL); - } catch (IOException e) { - throw new STTException("Error connected to the server", e); - } catch (URISyntaxException e) { - throw new STTException("Invalid WebSocket URL", e); - } - // One need not call recognitionSession.setContentType(...) [See http://bit.ly/1TGvQzA] - recognitionSession.addRecognitionEventListener(new RecognitionEventListenerKaldi(sttListener)); - - // Start recognition - STTServiceKaldiRunnable sttServiceKaldiRunnable = new STTServiceKaldiRunnable(recognitionSession, sttListener, - audioStream); - Thread thread = new Thread(sttServiceKaldiRunnable); - thread.start(); - - // Return STTServiceHandleKaldi - return new STTServiceHandleKaldi(sttServiceKaldiRunnable); - } - - /** - * Initializes this.locales - * - * @return The locales of this instance - */ - private final HashSet initLocales() { - HashSet locales = new HashSet(); - locales.add(new Locale("en", "US")); // For now we only support American English - return locales; - } - - /** - * Initializes this.audioFormats - * - * @return The audio formats of this instance - */ - private final HashSet initAudioFormats() { - HashSet audioFormats = new HashSet(); - - String containers[] = { "NONE", "ASF", "AVI", "DVR-MS", "MKV", "MPEG", "OGG", "QuickTime", "RealMedia", - "WAVE" }; - String codecs[] = { "PCM_SIGNED", "RAW", "A52", "ADPCM", "FLAC", "GSM", "A-LAW", "MU-LAW", "MP3", "QDM", - "SPEEX", "VORBIS", "NIST", "VOC" }; - - for (String container : containers) { - for (String codec : codecs) { - audioFormats.add(new AudioFormat(container, codec, null, null, null, null)); // TODO: Allow only valid - // combinations - } - } - - return audioFormats; - } - - @Override - public String getId() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getLabel(Locale locale) { - // TODO Auto-generated method stub - return null; - } -} diff --git a/addons/voice/org.openhab.voice.kaldi/src/main/java/org/openhab/voice/kaldi/internal/RecognitionEventListenerKaldi.java b/addons/voice/org.openhab.voice.kaldi/src/main/java/org/openhab/voice/kaldi/internal/RecognitionEventListenerKaldi.java deleted file mode 100644 index 5e8729cf633ea..0000000000000 --- a/addons/voice/org.openhab.voice.kaldi/src/main/java/org/openhab/voice/kaldi/internal/RecognitionEventListenerKaldi.java +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.voice.kaldi.internal; - -import java.util.List; - -import org.eclipse.smarthome.core.voice.RecognitionStopEvent; -import org.eclipse.smarthome.core.voice.STTEvent; -import org.eclipse.smarthome.core.voice.STTListener; -import org.eclipse.smarthome.core.voice.SpeechRecognitionErrorEvent; -import org.eclipse.smarthome.core.voice.SpeechRecognitionEvent; - -import ee.ioc.phon.netspeechapi.duplex.RecognitionEvent; -import ee.ioc.phon.netspeechapi.duplex.RecognitionEventListener; - -/** - * A RecognitionEventListener forwarding RecognitionEvent's to STTEvent's - * - * @author Kelly Davis - Initial contribution and API - * - */ -public class RecognitionEventListenerKaldi implements RecognitionEventListener { - - /** - * Target for forwarded events - */ - private final STTListener sttListener; - - /** - * A RecognitionEventListener that forwards RecognitionEvent's to STTEvent's - * - * The target of the STTEvent's is the passed STTListener - * - * @param sttListener The targeted STTListener - */ - public RecognitionEventListenerKaldi(STTListener sttListener) { - this.sttListener = sttListener; - } - - /** - * Target of RecognitionEvent events that are forwarded to the contained STTListener - * - * @param recognitionEvent The fired RecognitionEvent - */ - @Override - public void onRecognitionEvent(RecognitionEvent recognitionEvent) { - int status = recognitionEvent.getStatus(); - switch (status) { - case RecognitionEvent.STATUS_SUCCESS: - RecognitionEvent.Result result = recognitionEvent.getResult(); - if (result.isFinal()) { - sttListener.sttEventReceived(getSTTEvent(recognitionEvent)); - } - break; - case RecognitionEvent.STATUS_NO_SPEECH: - sttListener.sttEventReceived(new SpeechRecognitionErrorEvent("No speech")); - break; - case RecognitionEvent.STATUS_ABORTED: - sttListener.sttEventReceived(new SpeechRecognitionErrorEvent("Aborted")); - break; - case RecognitionEvent.STATUS_AUDIO_CAPTURE: - sttListener.sttEventReceived(new SpeechRecognitionErrorEvent("Error with audio capture")); - break; - case RecognitionEvent.STATUS_NETWORK: - sttListener.sttEventReceived(new SpeechRecognitionErrorEvent("Network error")); - break; - case RecognitionEvent.STATUS_NOT_ALLOWED: - sttListener.sttEventReceived(new SpeechRecognitionErrorEvent("Not allowed")); - break; - case RecognitionEvent.STATUS_SERVICE_NOT_ALLOWED: - sttListener.sttEventReceived(new SpeechRecognitionErrorEvent("Service not allowed")); - break; - case RecognitionEvent.STATUS_BAD_GRAMMAR: - sttListener.sttEventReceived(new SpeechRecognitionErrorEvent("Grammar invalid")); - break; - case RecognitionEvent.STATUS_LANGUAGE_NOT_SUPPORTED: - sttListener.sttEventReceived(new SpeechRecognitionErrorEvent("Language not supported")); - break; - } - } - - /** - * Utility method to create a STTEvent from a successful, final RecognitionEvent - * - * @param recognitionEvent The successful, final RecognitionEvent - * @return A STTEvent created from the passed RecognitionEvent - */ - private STTEvent getSTTEvent(RecognitionEvent recognitionEvent) { - RecognitionEvent.Result result = recognitionEvent.getResult(); - List hypotheses = result.getHypotheses(); - - float confidence = -1.0f; - String transcript = new String(); - for (RecognitionEvent.Hypothesis hypothesis : hypotheses) { - if (confidence < hypothesis.getConfidence()) { - confidence = hypothesis.getConfidence(); - transcript = hypothesis.getTranscript(); - } - } - return new SpeechRecognitionEvent(transcript, confidence); - } - - /** - * Called when the WebSocket is closed - */ - @Override - public void onClose() { - sttListener.sttEventReceived(new RecognitionStopEvent()); - } -} diff --git a/addons/voice/org.openhab.voice.kaldi/src/main/java/org/openhab/voice/kaldi/internal/STTServiceHandleKaldi.java b/addons/voice/org.openhab.voice.kaldi/src/main/java/org/openhab/voice/kaldi/internal/STTServiceHandleKaldi.java deleted file mode 100644 index 935445ec545ec..0000000000000 --- a/addons/voice/org.openhab.voice.kaldi/src/main/java/org/openhab/voice/kaldi/internal/STTServiceHandleKaldi.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.voice.kaldi.internal; - -import org.eclipse.smarthome.core.voice.STTServiceHandle; - -/** - * Kaldi implementation of a STTServiceHandle - * - * @author Kelly Davis - Initial contribution and API - * - */ -public class STTServiceHandleKaldi implements STTServiceHandle { - - /** - * STTServiceKaldiRunnable managed by this instance - */ - private final STTServiceKaldiRunnable sttServiceKaldiRunnable; - - /** - * Creates an instance to manage the passed STTServiceKaldiRunnable - * - * @param sttServiceKaldiRunnable The managed STTServiceKaldiRunnable - */ - public STTServiceHandleKaldi(STTServiceKaldiRunnable sttServiceKaldiRunnable) { - this.sttServiceKaldiRunnable = sttServiceKaldiRunnable; - } - - /** - * {@inheritDoc} - */ - @Override - public void abort() { - this.sttServiceKaldiRunnable.abort(); - } -} diff --git a/addons/voice/org.openhab.voice.kaldi/src/main/java/org/openhab/voice/kaldi/internal/STTServiceKaldiRunnable.java b/addons/voice/org.openhab.voice.kaldi/src/main/java/org/openhab/voice/kaldi/internal/STTServiceKaldiRunnable.java deleted file mode 100644 index 824d1fdedd184..0000000000000 --- a/addons/voice/org.openhab.voice.kaldi/src/main/java/org/openhab/voice/kaldi/internal/STTServiceKaldiRunnable.java +++ /dev/null @@ -1,166 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.voice.kaldi.internal; - -import java.io.IOException; -import java.util.Arrays; - -import org.eclipse.smarthome.core.audio.AudioFormat; -import org.eclipse.smarthome.core.audio.AudioStream; -import org.eclipse.smarthome.core.voice.RecognitionStartEvent; -import org.eclipse.smarthome.core.voice.STTListener; -import org.eclipse.smarthome.core.voice.SpeechRecognitionErrorEvent; - -import ee.ioc.phon.netspeechapi.duplex.RecognitionEvent; -import ee.ioc.phon.netspeechapi.duplex.RecognitionEventListener; -import ee.ioc.phon.netspeechapi.duplex.WsDuplexRecognitionSession; - -/** - * A Runnable that sends AudioStream data in a WsDuplexRecognitionSession - * - * @author Kelly Davis - Initial contribution and API - * - */ -public class STTServiceKaldiRunnable implements Runnable, RecognitionEventListener { - - /** - * Boolean indicating if the server closed the connection - */ - private volatile boolean isClosed; - - /** - * Boolean indicating if the thread is aborting - */ - private volatile boolean isAborting; - - /** - * The source of audio data - */ - private final AudioStream audioStream; - - /** - * The STTListener notified of STTEvents - */ - private final STTListener sttListener; - - /** - * The WsDuplexRecognitionSession communication is over - */ - private final WsDuplexRecognitionSession recognitionSession; - - /** - * Constructs an instance targeting the passed WsDuplexRecognitionSession - * - * @param recognitionSession The WsDuplexRecognitionSession sesion - * @param sttListener The STTListener targeted for STTEvents - * @param audioStream The AudioSource data - */ - public STTServiceKaldiRunnable(WsDuplexRecognitionSession recognitionSession, STTListener sttListener, - AudioStream audioStream) { - this.isClosed = false; - this.isAborting = false; - this.audioStream = audioStream; - this.sttListener = sttListener; - this.recognitionSession = recognitionSession; - - this.recognitionSession.addRecognitionEventListener(this); - } - - /** - * This method sends AudioSource data in the WsDuplexRecognitionSession - */ - @Override - public void run() { - try { - this.recognitionSession.connect(); - AudioFormat audioFormat = this.audioStream.getFormat(); - int bitRate = audioFormat.getBitRate().intValue(); - int byteRate = (bitRate / 8); - int chunkRate = 4; // 4 <= chunkRate [See: http://bit.ly/1V4Ktw2] - byte buffer[] = new byte[byteRate / chunkRate]; - - sttListener.sttEventReceived(new RecognitionStartEvent()); - - boolean sentLastChunk = false; - while (!this.isAborting && !this.isClosed) { - long millisWithinChunkSecond = System.currentTimeMillis() % (1000 / chunkRate); - int size = audioStream.read(buffer); - if (size < 0) { - sentLastChunk = true; - byte buffer2[] = new byte[0]; - this.recognitionSession.sendChunk(buffer2, true); - break; - } - if (size == (byteRate / chunkRate)) { - this.recognitionSession.sendChunk(buffer, false); - } else { - sentLastChunk = true; - byte buffer2[] = Arrays.copyOf(buffer, size); - this.recognitionSession.sendChunk(buffer2, true); - break; - } - Thread.sleep(1000 / chunkRate - millisWithinChunkSecond); - } - - if (this.isAborting && !this.isClosed && !sentLastChunk) { - byte buffer2[] = new byte[0]; - this.recognitionSession.sendChunk(buffer2, true); - } - } catch (IOException e) { - sttListener.sttEventReceived(new SpeechRecognitionErrorEvent("Unable to send audio data to the server")); - } catch (InterruptedException e) { - sttListener.sttEventReceived( - new SpeechRecognitionErrorEvent("Unable to send data to the server at the proper rate")); - } catch (RuntimeException e) { - // Note: This is a workaround for a bug in net-speech-api and Java-WebSocket. - // - // The problem is RecognitionEventListener's onClose() are only called - // after the connection is closed. Thus, the loop above does not know - // when to stop sending data and may try to send data on a session that - // is closed. - // - // A possible solution would be to have WsDuplexRecognitionSession call - // RecognitionEventListener's onClose() methods in an override of the - // method onCloseInitiated() of WebSocketClient. However, this also - // doesn't work as this method is never called before or after a session - // is closed. - // - // This temporary, but working, solution is to catch a RuntimeException - // here and assume that it results from sendChunk() being called on a - // closed session then proceede as if onClose() was called. - } - } - - /** - * This method initiates the process of aborting this thread - */ - public void abort() { - this.isAborting = true; - } - - /** - * {@inheritDoc} - */ - @Override - public void onRecognitionEvent(RecognitionEvent recognitionEvent) { - // RecognitionEvent are ignored - } - - /** - * {@inheritDoc} - */ - @Override - public void onClose() { - this.isClosed = true; - } -} diff --git a/addons/voice/org.openhab.voice.pollytts/.classpath b/addons/voice/org.openhab.voice.pollytts/.classpath deleted file mode 100644 index c0db8152068bf..0000000000000 --- a/addons/voice/org.openhab.voice.pollytts/.classpath +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/addons/voice/org.openhab.voice.pollytts/.project b/addons/voice/org.openhab.voice.pollytts/.project deleted file mode 100644 index 305f34e09edc0..0000000000000 --- a/addons/voice/org.openhab.voice.pollytts/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.openhab.voice.pollytts - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/addons/voice/org.openhab.voice.pollytts/META-INF/MANIFEST.MF b/addons/voice/org.openhab.voice.pollytts/META-INF/MANIFEST.MF deleted file mode 100644 index 54841c7050b3a..0000000000000 --- a/addons/voice/org.openhab.voice.pollytts/META-INF/MANIFEST.MF +++ /dev/null @@ -1,28 +0,0 @@ -Manifest-Version: 1.0 -Automatic-Module-Name: org.openhab.voice.pollytts -Bundle-ActivationPolicy: lazy -Bundle-ManifestVersion: 2 -Bundle-Name: Polly Text-to-Speech -Bundle-SymbolicName: org.openhab.voice.pollytts;singleton:=true -Bundle-Vendor: openHAB -Bundle-Version: 2.5.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-ClassPath: - ., - lib/aws-java-sdk-core-1.11.490.jar, - lib/aws-java-sdk-polly-1.11.490.jar, - lib/commons-logging-1.1.3.jar, - lib/httpclient-4.5.5.jar, - lib/httpcore-4.4.9.jar, - lib/jackson-annotations-2.6.0.jar, - lib/jackson-core-2.6.7.jar, - lib/jackson-databind-2.6.7.2.jar, - lib/joda-time-2.8.1.jar -Import-Package: - org.eclipse.jdt.annotation;resolution:=optional, - org.eclipse.smarthome.config.core, - org.eclipse.smarthome.core.audio, - org.eclipse.smarthome.core.voice, - org.osgi.framework, - org.slf4j -Service-Component: OSGI-INF/*.xml diff --git a/addons/voice/org.openhab.voice.pollytts/OSGI-INF/.gitignore b/addons/voice/org.openhab.voice.pollytts/OSGI-INF/.gitignore deleted file mode 100644 index 6722cd96e785a..0000000000000 --- a/addons/voice/org.openhab.voice.pollytts/OSGI-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.xml diff --git a/addons/voice/org.openhab.voice.pollytts/build.properties b/addons/voice/org.openhab.voice.pollytts/build.properties deleted file mode 100644 index 295aa168bc3aa..0000000000000 --- a/addons/voice/org.openhab.voice.pollytts/build.properties +++ /dev/null @@ -1,17 +0,0 @@ -source.. = src/main/java/ -output.. = target/classes -bin.includes = META-INF/,\ - .,\ - OSGI-INF/,\ - lib/aws-java-sdk-core-1.11.490.jar,\ - lib/aws-java-sdk-polly-1.11.490.jar,\ - lib/commons-logging-1.1.3.jar,\ - lib/httpclient-4.5.5.jar,\ - lib/httpcore-4.4.9.jar,\ - lib/jackson-annotations-2.6.0.jar,\ - lib/jackson-core-2.6.7.jar,\ - lib/jackson-databind-2.6.7.2.jar,\ - lib/joda-time-2.8.1.jar,\ - ESH-INF/,\ - NOTICE - diff --git a/addons/voice/org.openhab.voice.pollytts/lib/aws-java-sdk-core-1.11.490.jar b/addons/voice/org.openhab.voice.pollytts/lib/aws-java-sdk-core-1.11.490.jar deleted file mode 100644 index ab8458186895f..0000000000000 Binary files a/addons/voice/org.openhab.voice.pollytts/lib/aws-java-sdk-core-1.11.490.jar and /dev/null differ diff --git a/addons/voice/org.openhab.voice.pollytts/lib/aws-java-sdk-polly-1.11.490.jar b/addons/voice/org.openhab.voice.pollytts/lib/aws-java-sdk-polly-1.11.490.jar deleted file mode 100644 index 5aa07ebe89689..0000000000000 Binary files a/addons/voice/org.openhab.voice.pollytts/lib/aws-java-sdk-polly-1.11.490.jar and /dev/null differ diff --git a/addons/voice/org.openhab.voice.pollytts/lib/commons-logging-1.1.3.jar b/addons/voice/org.openhab.voice.pollytts/lib/commons-logging-1.1.3.jar deleted file mode 100644 index ab5125407a6a7..0000000000000 Binary files a/addons/voice/org.openhab.voice.pollytts/lib/commons-logging-1.1.3.jar and /dev/null differ diff --git a/addons/voice/org.openhab.voice.pollytts/lib/httpclient-4.5.5.jar b/addons/voice/org.openhab.voice.pollytts/lib/httpclient-4.5.5.jar deleted file mode 100644 index 7796b0e742c1b..0000000000000 Binary files a/addons/voice/org.openhab.voice.pollytts/lib/httpclient-4.5.5.jar and /dev/null differ diff --git a/addons/voice/org.openhab.voice.pollytts/lib/httpcore-4.4.9.jar b/addons/voice/org.openhab.voice.pollytts/lib/httpcore-4.4.9.jar deleted file mode 100644 index cddba28f99b72..0000000000000 Binary files a/addons/voice/org.openhab.voice.pollytts/lib/httpcore-4.4.9.jar and /dev/null differ diff --git a/addons/voice/org.openhab.voice.pollytts/lib/jackson-annotations-2.6.0.jar b/addons/voice/org.openhab.voice.pollytts/lib/jackson-annotations-2.6.0.jar deleted file mode 100644 index 0a18c3419a291..0000000000000 Binary files a/addons/voice/org.openhab.voice.pollytts/lib/jackson-annotations-2.6.0.jar and /dev/null differ diff --git a/addons/voice/org.openhab.voice.pollytts/lib/jackson-core-2.6.7.jar b/addons/voice/org.openhab.voice.pollytts/lib/jackson-core-2.6.7.jar deleted file mode 100644 index d195e85d246be..0000000000000 Binary files a/addons/voice/org.openhab.voice.pollytts/lib/jackson-core-2.6.7.jar and /dev/null differ diff --git a/addons/voice/org.openhab.voice.pollytts/lib/jackson-databind-2.6.7.2.jar b/addons/voice/org.openhab.voice.pollytts/lib/jackson-databind-2.6.7.2.jar deleted file mode 100644 index f9651b2bf23b4..0000000000000 Binary files a/addons/voice/org.openhab.voice.pollytts/lib/jackson-databind-2.6.7.2.jar and /dev/null differ diff --git a/addons/voice/org.openhab.voice.pollytts/lib/joda-time-2.8.1.jar b/addons/voice/org.openhab.voice.pollytts/lib/joda-time-2.8.1.jar deleted file mode 100644 index 94be65991eb8f..0000000000000 Binary files a/addons/voice/org.openhab.voice.pollytts/lib/joda-time-2.8.1.jar and /dev/null differ diff --git a/addons/voice/org.openhab.voice.pollytts/pom.xml b/addons/voice/org.openhab.voice.pollytts/pom.xml deleted file mode 100644 index a2da42d0247f9..0000000000000 --- a/addons/voice/org.openhab.voice.pollytts/pom.xml +++ /dev/null @@ -1,15 +0,0 @@ - - 4.0.0 - - - pom - org.openhab.voice - 2.5.0-SNAPSHOT - - - org.openhab.voice.pollytts - eclipse-plugin - - Polly Text-to-Speech - - diff --git a/addons/voice/pom.xml b/addons/voice/pom.xml deleted file mode 100644 index 748377d20716d..0000000000000 --- a/addons/voice/pom.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - 4.0.0 - - - org.openhab.addons - pom - 2.5.0-SNAPSHOT - - - org.openhab.voice - pom - - pom - - openHAB Voice Add-Ons - - - - org.openhab.voice.pollytts - - - diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index be2fa1e02732d..42e78cc5fab26 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -67,32 +67,32 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.bluetooth ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.bluetooth.bluegiga ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.bluetooth.bluez ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.bluetooth.blukii ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.bluetooth.ruuvitag ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.boschindego ${project.version} @@ -102,7 +102,12 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles + org.openhab.binding.buienradar + ${project.version} + + + org.openhab.addons.bundles org.openhab.binding.chromecast ${project.version} @@ -162,7 +167,7 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.dsmr ${project.version} @@ -177,7 +182,7 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.enocean ${project.version} @@ -187,12 +192,12 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.exec ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.feed ${project.version} @@ -222,7 +227,7 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.ftpupload ${project.version} @@ -287,12 +292,12 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.ihc ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.innogysmarthome ${project.version} @@ -357,7 +362,7 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.lgwebos ${project.version} @@ -377,7 +382,7 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.loxone ${project.version} @@ -386,6 +391,11 @@ org.openhab.binding.lutron ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.mail + ${project.version} + org.openhab.addons.bundles org.openhab.binding.max @@ -432,7 +442,7 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.modbus ${project.version} @@ -487,7 +497,7 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.nikohomecontrol ${project.version} @@ -517,12 +527,12 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.onewiregpio ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.onkyo ${project.version} @@ -542,7 +552,7 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.orvibo ${project.version} @@ -612,7 +622,7 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.samsungtv ${project.version} @@ -652,7 +662,7 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.smartmeter ${project.version} @@ -697,7 +707,7 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.systeminfo ${project.version} @@ -782,7 +792,17 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles + org.openhab.binding.xmltv + ${project.version} + + + org.openhab.addons.bundles + org.openhab.binding.xmppclient + ${project.version} + + + org.openhab.addons.bundles org.openhab.binding.yamahareceiver ${project.version} @@ -792,7 +812,7 @@ ${project.version} - org.openhab.binding + org.openhab.addons.bundles org.openhab.binding.zoneminder ${project.version} @@ -812,12 +832,12 @@ ${project.version} - org.openhab.io + org.openhab.addons.bundles org.openhab.io.azureiothub ${project.version} - org.openhab.io + org.openhab.addons.bundles org.openhab.io.homekit ${project.version} @@ -847,17 +867,12 @@ ${project.version} - org.openhab.io + org.openhab.addons.bundles org.openhab.io.openhabcloud ${project.version} - org.openhab.io - org.openhab.io.transport.feed - ${project.version} - - - org.openhab.io + org.openhab.addons.bundles org.openhab.io.transport.modbus ${project.version} @@ -937,7 +952,7 @@ ${project.version} - org.openhab.voice + org.openhab.addons.bundles org.openhab.voice.pollytts ${project.version} diff --git a/bom/pom.xml b/bom/pom.xml index d365d12ac7d89..47b4b1973e849 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -7,7 +7,6 @@ org.openhab.addons org.openhab.addons.reactor 2.5.0-SNAPSHOT - ../poms/bnd org.openhab.addons.bom diff --git a/bundles/archetype-settings.xml b/bundles/archetype-settings.xml new file mode 100644 index 0000000000000..4535ddb2104d7 --- /dev/null +++ b/bundles/archetype-settings.xml @@ -0,0 +1,20 @@ + + + + + openHAB-snapshots + + + archetype + https://openhab.jfrog.io/openhab/webapp/#/builds/openHAB-Core/ + + + + + + openHAB-snapshots + + diff --git a/bundles/create_openhab_binding_skeleton.cmd b/bundles/create_openhab_binding_skeleton.cmd new file mode 100644 index 0000000000000..e748edb771682 --- /dev/null +++ b/bundles/create_openhab_binding_skeleton.cmd @@ -0,0 +1,40 @@ +@echo off + +SETLOCAL +SET ARGC=0 + +FOR %%x IN (%*) DO SET /A ARGC+=1 + +IF %ARGC% NEQ 3 ( + echo Usage: %0 BindingIdInCamelCase Author GithubUser + exit /B 1 +) + +SET OpenhabVersion="2.5.0-SNAPSHOT" + +SET BindingIdInCamelCase=%~1 +SET BindingIdInLowerCase=%BindingIdInCamelCase% +SET Author=%~2 +SET GithubUser=%~3 + +call :LoCase BindingIdInLowerCase + +call mvn -s archetype-settings.xml archetype:generate -N -DarchetypeGroupId=org.openhab.core -DarchetypeArtifactId=org.openhab.core.tools.archetypes.binding -DarchetypeVersion=%OpenhabVersion% -DgroupId=org.openhab.binding -DartifactId=org.openhab.binding.%BindingIdInLowerCase% -Dpackage=org.openhab.binding.%BindingIdInLowerCase% -Dversion=%OpenhabVersion% -DbindingId=%BindingIdInLowerCase% -DbindingIdCamelCase=%BindingIdInCamelCase% -DvendorName=openHAB -Dnamespace=org.openhab -Dauthor="%Author%" -DgithubUser="%GithubUser%" + +COPY ..\src\etc\NOTICE org.openhab.binding.%BindingIdInLowerCase%\ + +(SET BindingIdInLowerCase=) +(SET BindingIdInCamelCase=) +(SET Author=) +(SET GithubUser=) + +GOTO:EOF + + +:LoCase +:: Subroutine to convert a variable VALUE to all lower case. +:: The argument for this subroutine is the variable NAME. +FOR %%i IN ("A=a" "B=b" "C=c" "D=d" "E=e" "F=f" "G=g" "H=h" "I=i" "J=j" "K=k" "L=l" "M=m" "N=n" "O=o" "P=p" "Q=q" "R=r" "S=s" "T=t" "U=u" "V=v" "W=w" "X=x" "Y=y" "Z=z") DO CALL SET "%1=%%%1:%%~i%%" +GOTO:EOF + +ENDLOCAL diff --git a/bundles/create_openhab_binding_skeleton.sh b/bundles/create_openhab_binding_skeleton.sh new file mode 100755 index 0000000000000..0f8a55342c07e --- /dev/null +++ b/bundles/create_openhab_binding_skeleton.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +[ $# -lt 3 ] && { echo "Usage: $0 "; exit 1; } + +openHABVersion=2.5.0-SNAPSHOT + +camelcaseId=$1 +id=`echo $camelcaseId | tr '[:upper:]' '[:lower:]'` + +author=$2 +githubUser=$3 + +mvn -s archetype-settings.xml archetype:generate -N \ + -DarchetypeGroupId=org.openhab.core \ + -DarchetypeArtifactId=org.openhab.core.tools.archetypes.binding \ + -DarchetypeVersion=$openHABVersion \ + -DgroupId=org.openhab.binding \ + -DartifactId=org.openhab.binding.$id \ + -Dpackage=org.openhab.binding.$id \ + -Dversion=$openHABVersion \ + -DbindingId=$id \ + -DbindingIdCamelCase=$camelcaseId \ + -DvendorName=openHAB \ + -Dnamespace=org.openhab \ + -Dauthor="$author" \ + -DgithubUser="$githubUser" + +directory="org.openhab.binding.$id/" + +cp ../src/etc/NOTICE "$directory" + diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/statedescription/AmazonEchoDynamicStateDescriptionProvider.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/statedescription/AmazonEchoDynamicStateDescriptionProvider.java index abcb33647ffc6..bf0b21394e49e 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/statedescription/AmazonEchoDynamicStateDescriptionProvider.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/statedescription/AmazonEchoDynamicStateDescriptionProvider.java @@ -123,8 +123,9 @@ protected void unsetThingRegistry(ThingRegistry thingRegistry) { if (device == null) { continue; } - if (device.address != null && device.friendlyName != null) { - options.add(new StateOption(device.address, device.friendlyName)); + final String value = device.address; + if (value != null && device.friendlyName != null) { + options.add(new StateOption(value, device.friendlyName)); } } StateDescription result = new StateDescription(originalStateDescription.getMinimum(), @@ -151,8 +152,9 @@ protected void unsetThingRegistry(ThingRegistry thingRegistry) { for (PlayList[] innerLists : playlistMap.values()) { if (innerLists != null && innerLists.length > 0) { PlayList playList = innerLists[0]; - if (playList.playlistId != null && playList.title != null) { - options.add(new StateOption(playList.playlistId, + final String value = playList.playlistId; + if (value != null && playList.title != null) { + options.add(new StateOption(value, String.format("%s (%d)", playList.title, playList.trackCount))); } } @@ -205,8 +207,10 @@ protected void unsetThingRegistry(ThingRegistry thingRegistry) { ArrayList options = new ArrayList<>(); options.add(new StateOption("", "")); for (Device device : devices) { - if (device.capabilities != null && Arrays.asList(device.capabilities).contains("FLASH_BRIEFING")) { - options.add(new StateOption(device.serialNumber, device.accountName)); + final String value = device.serialNumber; + if (value != null && device.capabilities != null + && Arrays.asList(device.capabilities).contains("FLASH_BRIEFING")) { + options.add(new StateOption(value, device.accountName)); } } StateDescription result = new StateDescription(originalStateDescription.getMinimum(), @@ -232,7 +236,7 @@ protected void unsetThingRegistry(ThingRegistry thingRegistry) { if (properties != null && properties.contains("Alexa.Music.PlaySearchPhrase") && StringUtils.isNotEmpty(providerId) && StringUtils.equals(musicProvider.availability, "AVAILABLE") - && StringUtils.isNotEmpty(displayName)) { + && StringUtils.isNotEmpty(displayName) && providerId != null) { options.add(new StateOption(providerId, displayName)); } } diff --git a/bundles/org.openhab.binding.atlona/src/main/java/org/openhab/binding/atlona/internal/discovery/AtlonaDiscovery.java b/bundles/org.openhab.binding.atlona/src/main/java/org/openhab/binding/atlona/internal/discovery/AtlonaDiscovery.java index 64f796fc9d5f2..2d10e276665be 100644 --- a/bundles/org.openhab.binding.atlona/src/main/java/org/openhab/binding/atlona/internal/discovery/AtlonaDiscovery.java +++ b/bundles/org.openhab.binding.atlona/src/main/java/org/openhab/binding/atlona/internal/discovery/AtlonaDiscovery.java @@ -233,7 +233,7 @@ private void messageReceive(String message) { } else if (model.equalsIgnoreCase("AT-UHD-PRO3-1616M")) { typeId = THING_TYPE_PRO3_1616M; } else { - logger.warn("Unknown model #: {}"); + logger.warn("Unknown model #: {}", model); } if (typeId != null) { diff --git a/bundles/org.openhab.binding.bluetooth.bluegiga/.classpath b/bundles/org.openhab.binding.bluetooth.bluegiga/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.bluegiga/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.bluetooth.bluegiga/.project b/bundles/org.openhab.binding.bluetooth.bluegiga/.project new file mode 100644 index 0000000000000..23c11c2fce868 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.bluegiga/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.bluetooth.bluegiga + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/NOTICE b/bundles/org.openhab.binding.bluetooth.bluegiga/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/NOTICE rename to bundles/org.openhab.binding.bluetooth.bluegiga/NOTICE diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/README.md b/bundles/org.openhab.binding.bluetooth.bluegiga/README.md similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/README.md rename to bundles/org.openhab.binding.bluetooth.bluegiga/README.md diff --git a/bundles/org.openhab.binding.bluetooth.bluegiga/pom.xml b/bundles/org.openhab.binding.bluetooth.bluegiga/pom.xml new file mode 100644 index 0000000000000..d7fd872f525f7 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.bluegiga/pom.xml @@ -0,0 +1,24 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.bluetooth.bluegiga + + openHAB Add-ons :: Bundles :: BlueGiga Bluetooth Adapter + + + + org.openhab.addons.bundles + org.openhab.binding.bluetooth + ${project.version} + provided + + + diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaAdapterConstants.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaAdapterConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaAdapterConstants.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaAdapterConstants.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/BlueGigaBluetoothDevice.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/handler/BlueGigaBridgeHandler.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaEventListener.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaEventListener.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaEventListener.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaEventListener.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaException.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaException.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaException.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaException.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaHandlerListener.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaHandlerListener.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaHandlerListener.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaHandlerListener.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaPacket.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaPacket.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaPacket.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaPacket.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaResponsePackets.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaResponsePackets.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaResponsePackets.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaResponsePackets.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaSerialHandler.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaSerialHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaSerialHandler.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/BlueGigaSerialHandler.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeValueEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeValueEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeValueEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeValueEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaAttributeWriteResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaExecuteWriteResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindByTypeValueResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationFoundEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationFoundEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationFoundEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationFoundEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaFindInformationResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaGroupFoundEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaGroupFoundEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaGroupFoundEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaGroupFoundEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicateConfirmResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicatedEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicatedEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicatedEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaIndicatedEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaPrepareWriteResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaProcedureCompletedEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaProcedureCompletedEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaProcedureCompletedEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaProcedureCompletedEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByGroupTypeResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByHandleResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadByTypeResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadLongResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponseEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponseEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponseEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaReadMultipleResponseEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributeclient/BlueGigaWriteCommandResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaAttributeStatusEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaAttributeStatusEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaAttributeStatusEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaAttributeStatusEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaReadTypeResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaSendAttributesResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadRequestEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadRequestEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadRequestEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadRequestEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserReadResponseResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaUserWriteResponseResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaValueEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaValueEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaValueEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaValueEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/attributedb/BlueGigaWriteResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaChannelMapGetResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaConnectionStatusEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaConnectionStatusEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaConnectionStatusEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaConnectionStatusEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectedEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectedEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectedEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaDisconnectedEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaFeatureIndEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaFeatureIndEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaFeatureIndEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaFeatureIndEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetRssiResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaGetStatusResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaUpdateResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaVersionIndEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaVersionIndEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaVersionIndEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/connection/BlueGigaVersionIndEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectDirectResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaConnectSelectiveResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaDiscoverResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaEndProcedureResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaScanResponseEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaScanResponseEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaScanResponseEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaScanResponseEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvDataResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetAdvParametersResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetModeResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/gap/BlueGigaSetScanParametersResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondStatusEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondStatusEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondStatusEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondStatusEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondingFailEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondingFailEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondingFailEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaBondingFailEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaDeleteBondingResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaEncryptStartResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaGetBondsResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPassKeyResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyDisplayEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyDisplayEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyDisplayEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyDisplayEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyRequestEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyRequestEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyRequestEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaPasskeyRequestEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetBondableModeResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaSetParametersResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/security/BlueGigaWhitelistBondsResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaAddressGetResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaBootEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaBootEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaBootEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaBootEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkRxEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkRxEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkRxEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkRxEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkTxEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkTxEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkTxEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaEndpointWatermarkTxEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetConnectionsResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetCountersResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaGetInfoResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaHelloResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaNoLicenseKeyEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaNoLicenseKeyEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaNoLicenseKeyEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaNoLicenseKeyEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaProtocolErrorEvent.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaProtocolErrorEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaProtocolErrorEvent.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaProtocolErrorEvent.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaResetResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistAppendResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistClearResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveCommand.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveCommand.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveCommand.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/command/system/BlueGigaWhitelistRemoveResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirDataType.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirDataType.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirDataType.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirDataType.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirFlags.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirFlags.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirFlags.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirFlags.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirPacket.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirPacket.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirPacket.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirPacket.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirRecord.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirRecord.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirRecord.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirRecord.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/AttributeChangeReason.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/AttributeChangeReason.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/AttributeChangeReason.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/AttributeChangeReason.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/AttributeValueType.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/AttributeValueType.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/AttributeValueType.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/AttributeValueType.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/BgApiResponse.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/BgApiResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/BgApiResponse.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/BgApiResponse.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/BluetoothAddressType.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/BluetoothAddressType.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/BluetoothAddressType.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/BluetoothAddressType.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/ConnectionStatusFlag.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/ConnectionStatusFlag.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/ConnectionStatusFlag.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/ConnectionStatusFlag.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/GapConnectableMode.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/GapConnectableMode.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/GapConnectableMode.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/GapConnectableMode.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverMode.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverMode.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverMode.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverMode.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverableMode.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverableMode.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverableMode.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/GapDiscoverableMode.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/ScanResponseType.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/ScanResponseType.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/ScanResponseType.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/ScanResponseType.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/SmpIoCapabilities.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/SmpIoCapabilities.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/SmpIoCapabilities.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/enumeration/SmpIoCapabilities.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/factory/BlueGigaHandlerFactory.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/factory/BlueGigaHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/factory/BlueGigaHandlerFactory.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/java/org/openhab/binding/bluetooth/bluegiga/internal/factory/BlueGigaHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga/ESH-INF/thing/bluegiga.xml b/bundles/org.openhab.binding.bluetooth.bluegiga/src/main/resources/ESH-INF/thing/bluegiga.xml similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga/ESH-INF/thing/bluegiga.xml rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/main/resources/ESH-INF/thing/bluegiga.xml diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/src/test/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirRecordTest.java b/bundles/org.openhab.binding.bluetooth.bluegiga/src/test/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirRecordTest.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga.test/src/test/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirRecordTest.java rename to bundles/org.openhab.binding.bluetooth.bluegiga/src/test/java/org/openhab/binding/bluetooth/bluegiga/internal/eir/EirRecordTest.java diff --git a/bundles/org.openhab.binding.bluetooth.bluez/.classpath b/bundles/org.openhab.binding.bluetooth.bluez/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.bluez/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.bluetooth.bluez/.project b/bundles/org.openhab.binding.bluetooth.bluez/.project new file mode 100644 index 0000000000000..3463582e6384f --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.bluez/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.bluetooth.bluez + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/NOTICE b/bundles/org.openhab.binding.bluetooth.bluez/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluez/NOTICE rename to bundles/org.openhab.binding.bluetooth.bluez/NOTICE diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/README.md b/bundles/org.openhab.binding.bluetooth.bluez/README.md similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluez/README.md rename to bundles/org.openhab.binding.bluetooth.bluez/README.md diff --git a/bundles/org.openhab.binding.bluetooth.bluez/pom.xml b/bundles/org.openhab.binding.bluetooth.bluez/pom.xml new file mode 100644 index 0000000000000..f21776a482aaa --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.bluez/pom.xml @@ -0,0 +1,30 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.bluetooth.bluez + + openHAB Add-ons :: Bundles :: BlueZ Bluetooth Adapter + + + + org.openhab.addons.bundles + org.openhab.binding.bluetooth + ${project.version} + provided + + + org.sputnikdev + bluetooth-manager-tinyb + 1.3.3 + provided + + + diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/BlueZAdapterConstants.java b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/BlueZAdapterConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/BlueZAdapterConstants.java rename to bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/BlueZAdapterConstants.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/BlueZBluetoothDevice.java b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/BlueZBluetoothDevice.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/BlueZBluetoothDevice.java rename to bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/BlueZBluetoothDevice.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZAdapterConfiguration.java b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZAdapterConfiguration.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZAdapterConfiguration.java rename to bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZAdapterConfiguration.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java rename to bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/handler/BlueZBridgeHandler.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/BlueZHandlerFactory.java b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/BlueZHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/BlueZHandlerFactory.java rename to bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/BlueZHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/discovery/BlueZDiscoveryService.java b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/discovery/BlueZDiscoveryService.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/discovery/BlueZDiscoveryService.java rename to bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/internal/discovery/BlueZDiscoveryService.java diff --git a/addons/binding/org.openhab.binding.bluetooth.bluez/ESH-INF/thing/bluez.xml b/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/ESH-INF/thing/bluez.xml similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluez/ESH-INF/thing/bluez.xml rename to bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/ESH-INF/thing/bluez.xml diff --git a/bundles/org.openhab.binding.bluetooth.blukii/.classpath b/bundles/org.openhab.binding.bluetooth.blukii/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.blukii/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.bluetooth.blukii/.project b/bundles/org.openhab.binding.bluetooth.blukii/.project new file mode 100644 index 0000000000000..e93fed32cd993 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.blukii/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.bluetooth.blukii + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.bluetooth.bluegiga.test/NOTICE b/bundles/org.openhab.binding.bluetooth.blukii/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.bluegiga.test/NOTICE rename to bundles/org.openhab.binding.bluetooth.blukii/NOTICE diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/README.md b/bundles/org.openhab.binding.bluetooth.blukii/README.md similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.blukii/README.md rename to bundles/org.openhab.binding.bluetooth.blukii/README.md diff --git a/bundles/org.openhab.binding.bluetooth.blukii/pom.xml b/bundles/org.openhab.binding.bluetooth.blukii/pom.xml new file mode 100644 index 0000000000000..e59172cb28583 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.blukii/pom.xml @@ -0,0 +1,24 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.bluetooth.blukii + + openHAB Add-ons :: Bundles :: Blukii Bluetooth Adapter + + + + org.openhab.addons.bundles + org.openhab.binding.bluetooth + ${project.version} + provided + + + diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/BlukiiBindingConstants.java b/bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/BlukiiBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/BlukiiBindingConstants.java rename to bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/BlukiiBindingConstants.java diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/handler/BlukiiHandler.java b/bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/handler/BlukiiHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/handler/BlukiiHandler.java rename to bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/handler/BlukiiHandler.java diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/BlukiiDiscoveryParticipant.java b/bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/BlukiiDiscoveryParticipant.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/BlukiiDiscoveryParticipant.java rename to bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/BlukiiDiscoveryParticipant.java diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/BlukiiHandlerFactory.java b/bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/BlukiiHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/BlukiiHandlerFactory.java rename to bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/BlukiiHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/Accelerometer.java b/bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/Accelerometer.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/Accelerometer.java rename to bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/Accelerometer.java diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/BlukiiData.java b/bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/BlukiiData.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/BlukiiData.java rename to bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/BlukiiData.java diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/BlukiiDataDecoder.java b/bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/BlukiiDataDecoder.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/BlukiiDataDecoder.java rename to bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/BlukiiDataDecoder.java diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/Environment.java b/bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/Environment.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/Environment.java rename to bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/Environment.java diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/Magnetometer.java b/bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/Magnetometer.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/Magnetometer.java rename to bundles/org.openhab.binding.bluetooth.blukii/src/main/java/org/openhab/binding/bluetooth/blukii/internal/data/Magnetometer.java diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/ESH-INF/thing/blukii.xml b/bundles/org.openhab.binding.bluetooth.blukii/src/main/resources/ESH-INF/thing/blukii.xml similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.blukii/ESH-INF/thing/blukii.xml rename to bundles/org.openhab.binding.bluetooth.blukii/src/main/resources/ESH-INF/thing/blukii.xml diff --git a/bundles/org.openhab.binding.bluetooth.ruuvitag/.classpath b/bundles/org.openhab.binding.bluetooth.ruuvitag/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.ruuvitag/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.bluetooth.ruuvitag/.project b/bundles/org.openhab.binding.bluetooth.ruuvitag/.project new file mode 100644 index 0000000000000..eb2296e0add78 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.ruuvitag/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.bluetooth.ruuvitag + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/NOTICE b/bundles/org.openhab.binding.bluetooth.ruuvitag/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.ruuvitag/NOTICE rename to bundles/org.openhab.binding.bluetooth.ruuvitag/NOTICE diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/README.md b/bundles/org.openhab.binding.bluetooth.ruuvitag/README.md similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.ruuvitag/README.md rename to bundles/org.openhab.binding.bluetooth.ruuvitag/README.md diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/lib/ruuvitag-common-1.0.0.jar b/bundles/org.openhab.binding.bluetooth.ruuvitag/lib/ruuvitag-common-1.0.0.jar similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.ruuvitag/lib/ruuvitag-common-1.0.0.jar rename to bundles/org.openhab.binding.bluetooth.ruuvitag/lib/ruuvitag-common-1.0.0.jar diff --git a/bundles/org.openhab.binding.bluetooth.ruuvitag/pom.xml b/bundles/org.openhab.binding.bluetooth.ruuvitag/pom.xml new file mode 100644 index 0000000000000..f4f896b160df5 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.ruuvitag/pom.xml @@ -0,0 +1,24 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.bluetooth.ruuvitag + + openHAB Add-ons :: Bundles :: RuuviTag Bluetooth Adapter + + + + org.openhab.addons.bundles + org.openhab.binding.bluetooth + ${project.version} + provided + + + diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/RuuviTagBindingConstants.java b/bundles/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/RuuviTagBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/RuuviTagBindingConstants.java rename to bundles/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/RuuviTagBindingConstants.java diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/handler/RuuviTagHandler.java b/bundles/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/handler/RuuviTagHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/handler/RuuviTagHandler.java rename to bundles/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/handler/RuuviTagHandler.java diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/internal/RuuviTagDiscoveryParticipant.java b/bundles/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/internal/RuuviTagDiscoveryParticipant.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/internal/RuuviTagDiscoveryParticipant.java rename to bundles/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/internal/RuuviTagDiscoveryParticipant.java diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/internal/RuuviTagHandlerFactory.java b/bundles/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/internal/RuuviTagHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/internal/RuuviTagHandlerFactory.java rename to bundles/org.openhab.binding.bluetooth.ruuvitag/src/main/java/org/openhab/binding/bluetooth/ruuvitag/internal/RuuviTagHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.bluetooth.ruuvitag/ESH-INF/thing/ruuvitag.xml b/bundles/org.openhab.binding.bluetooth.ruuvitag/src/main/resources/ESH-INF/thing/ruuvitag.xml similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.ruuvitag/ESH-INF/thing/ruuvitag.xml rename to bundles/org.openhab.binding.bluetooth.ruuvitag/src/main/resources/ESH-INF/thing/ruuvitag.xml diff --git a/bundles/org.openhab.binding.bluetooth/.classpath b/bundles/org.openhab.binding.bluetooth/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.bluetooth/.project b/bundles/org.openhab.binding.bluetooth/.project new file mode 100644 index 0000000000000..f9b7d2db5673f --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.bluetooth + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.bluetooth/ARCHITECTURE.md b/bundles/org.openhab.binding.bluetooth/CONTRIBUTE.md similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/ARCHITECTURE.md rename to bundles/org.openhab.binding.bluetooth/CONTRIBUTE.md diff --git a/addons/binding/org.openhab.binding.bluetooth.blukii/NOTICE b/bundles/org.openhab.binding.bluetooth/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.blukii/NOTICE rename to bundles/org.openhab.binding.bluetooth/NOTICE diff --git a/addons/binding/org.openhab.binding.bluetooth/README.md b/bundles/org.openhab.binding.bluetooth/README.md similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/README.md rename to bundles/org.openhab.binding.bluetooth/README.md diff --git a/bundles/org.openhab.binding.bluetooth/pom.xml b/bundles/org.openhab.binding.bluetooth/pom.xml new file mode 100644 index 0000000000000..067864de9f56b --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth/pom.xml @@ -0,0 +1,16 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.bluetooth + + openHAB Add-ons :: Bundles :: Bluetooth Binding + + diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BeaconBluetoothHandler.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BeaconBluetoothHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BeaconBluetoothHandler.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BeaconBluetoothHandler.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothAdapter.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothAdapter.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothAdapter.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothAdapter.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothAddress.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothAddress.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothAddress.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothAddress.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothBindingConstants.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothBindingConstants.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothBindingConstants.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCharacteristic.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCharacteristic.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCharacteristic.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCharacteristic.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothClass.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothClass.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothClass.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothClass.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCompanyIdentifiers.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCompanyIdentifiers.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCompanyIdentifiers.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCompanyIdentifiers.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCompletionStatus.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCompletionStatus.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCompletionStatus.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothCompletionStatus.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDescriptor.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDescriptor.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDescriptor.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDescriptor.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDevice.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDevice.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDevice.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDevice.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDeviceListener.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDeviceListener.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDeviceListener.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDeviceListener.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDiscoveryListener.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDiscoveryListener.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDiscoveryListener.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothDiscoveryListener.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothService.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothService.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothService.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/BluetoothService.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/ConnectedBluetoothHandler.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/ConnectedBluetoothHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/ConnectedBluetoothHandler.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/ConnectedBluetoothHandler.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/BluetoothDiscoveryParticipant.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/BluetoothDiscoveryParticipant.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/BluetoothDiscoveryParticipant.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/BluetoothDiscoveryParticipant.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryService.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryService.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryService.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/discovery/internal/BluetoothDiscoveryService.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/internal/BluetoothHandlerFactory.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/internal/BluetoothHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/internal/BluetoothHandlerFactory.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/internal/BluetoothHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/notification/BluetoothConnectionStatusNotification.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/notification/BluetoothConnectionStatusNotification.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/notification/BluetoothConnectionStatusNotification.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/notification/BluetoothConnectionStatusNotification.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/notification/BluetoothNotification.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/notification/BluetoothNotification.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/notification/BluetoothNotification.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/notification/BluetoothNotification.java diff --git a/addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/notification/BluetoothScanNotification.java b/bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/notification/BluetoothScanNotification.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/notification/BluetoothScanNotification.java rename to bundles/org.openhab.binding.bluetooth/src/main/java/org/openhab/binding/bluetooth/notification/BluetoothScanNotification.java diff --git a/addons/binding/org.openhab.binding.bluetooth/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.bluetooth/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.bluetooth/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.bluetooth/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.bluetooth/src/main/resources/ESH-INF/thing/channels.xml similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/ESH-INF/thing/channels.xml rename to bundles/org.openhab.binding.bluetooth/src/main/resources/ESH-INF/thing/channels.xml diff --git a/addons/binding/org.openhab.binding.bluetooth/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.bluetooth/src/main/resources/ESH-INF/thing/thing-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/ESH-INF/thing/thing-types.xml rename to bundles/org.openhab.binding.bluetooth/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/addons/binding/org.openhab.binding.bluetooth.test/src/test/java/org/openhab/binding/bluetooth/BluetoothAddressTest.java b/bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/BluetoothAddressTest.java similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.test/src/test/java/org/openhab/binding/bluetooth/BluetoothAddressTest.java rename to bundles/org.openhab.binding.bluetooth/src/test/java/org/openhab/binding/bluetooth/BluetoothAddressTest.java diff --git a/bundles/org.openhab.binding.boschindego/.classpath b/bundles/org.openhab.binding.boschindego/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.boschindego/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.boschindego/.project b/bundles/org.openhab.binding.boschindego/.project new file mode 100644 index 0000000000000..f9348edade0af --- /dev/null +++ b/bundles/org.openhab.binding.boschindego/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.boschindego + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.boschindego/NOTICE b/bundles/org.openhab.binding.boschindego/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.boschindego/NOTICE rename to bundles/org.openhab.binding.boschindego/NOTICE diff --git a/addons/binding/org.openhab.binding.boschindego/README.md b/bundles/org.openhab.binding.boschindego/README.md similarity index 100% rename from addons/binding/org.openhab.binding.boschindego/README.md rename to bundles/org.openhab.binding.boschindego/README.md diff --git a/bundles/org.openhab.binding.boschindego/pom.xml b/bundles/org.openhab.binding.boschindego/pom.xml new file mode 100644 index 0000000000000..33d3889a76472 --- /dev/null +++ b/bundles/org.openhab.binding.boschindego/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.boschindego + + openHAB Add-ons :: Bundles :: Bosch Indego Binding + + + + com.fasterxml.jackson.core + jackson-core + 2.9.8 + provided + + + com.fasterxml.jackson.core + jackson-annotations + 2.9.8 + provided + + + com.fasterxml.jackson.core + jackson-databind + 2.9.8 + provided + + + de.zazaz.iot.bosch.indego + bosch-indego-controller-lib + 0.8 + provided + + + + diff --git a/addons/binding/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/BoschIndegoBindingConstants.java b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/BoschIndegoBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/BoschIndegoBindingConstants.java rename to bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/BoschIndegoBindingConstants.java diff --git a/addons/binding/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/BoschIndegoHandlerFactory.java b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/BoschIndegoHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/BoschIndegoHandlerFactory.java rename to bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/BoschIndegoHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/IndegoStateConstants.java b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/IndegoStateConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/IndegoStateConstants.java rename to bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/IndegoStateConstants.java diff --git a/addons/binding/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/handler/BoschIndegoHandler.java b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/handler/BoschIndegoHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/handler/BoschIndegoHandler.java rename to bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/handler/BoschIndegoHandler.java diff --git a/addons/binding/org.openhab.binding.boschindego/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.boschindego/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.boschindego/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.boschindego/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.boschindego/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.boschindego/src/main/resources/ESH-INF/thing/thing-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.boschindego/ESH-INF/thing/thing-types.xml rename to bundles/org.openhab.binding.boschindego/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/bundles/org.openhab.binding.buienradar/.classpath b/bundles/org.openhab.binding.buienradar/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.buienradar/.project b/bundles/org.openhab.binding.buienradar/.project new file mode 100644 index 0000000000000..eeaa0a6ce9823 --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.buienradar + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.bluetooth.test/NOTICE b/bundles/org.openhab.binding.buienradar/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth.test/NOTICE rename to bundles/org.openhab.binding.buienradar/NOTICE diff --git a/bundles/org.openhab.binding.buienradar/README.md b/bundles/org.openhab.binding.buienradar/README.md new file mode 100644 index 0000000000000..05781207aef14 --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/README.md @@ -0,0 +1,52 @@ +# Buienradar Binding + +The Buienradar Binding periodically (5 minute intervals) retrieves rainfall predictions from the Dutch buienradar.nl webservice API. + +This allows us to warn of upcoming rainfal when there are still open windows, or prevents watering the outside plants needlessly. + +## Supported Things + +The webservice supports one thing, which can be added manually via the web interface. The thing needs longitude and latitude of the location which needs forecasts. + +## Discovery + +No auto-discovery is currently possible. + +## Configuration of the thing + +The configuration can be done via the PaperUI web interface, or manually by adding a Thing to your `.things` file: + +``` +Thing buienradar:rain_forecast:home [ location="52.198864211111925,5.4192629660193585" ] +``` + +and adding the relevant items as such in your `.items` file. Please note that the buienradar service only provides predictions in 5 minutes intervals with a maximum of two hours (120 minutes): + +``` +Number RAIN_CURRENT "Current rain" (Rain) {channel="buienradar:rain_forecast:home:forecast_0" } +Number RAIN_5MIN "Rain 5 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_5" } +Number RAIN_10MIN "Rain 10 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_10" } +Number RAIN_15MIN "Rain 15 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_15" } +Number RAIN_20MIN "Rain 20 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_20" } +Number RAIN_25MIN "Rain 25 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_25" } +Number RAIN_30MIN "Rain 30 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_30" } +Number RAIN_35MIN "Rain 35 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_35" } +Number RAIN_40MIN "Rain 40 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_40" } +Number RAIN_45MIN "Rain 45 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_45" } +Number RAIN_50MIN "Rain 50 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_50" } +Number RAIN_55MIN "Rain 55 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_55" } +Number RAIN_60MIN "Rain 60 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_60" } +Number RAIN_65MIN "Rain 65 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_65" } +Number RAIN_70MIN "Rain 70 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_70" } +Number RAIN_75MIN "Rain 75 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_75" } +Number RAIN_80MIN "Rain 80 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_80" } +Number RAIN_85MIN "Rain 85 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_85" } +Number RAIN_90MIN "Rain 90 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_90" } +Number RAIN_95MIN "Rain 95 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_95" } +Number RAIN_100MIN "Rain 100 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_100" } +Number RAIN_105MIN "Rain 105 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_105" } +Number RAIN_110MIN "Rain 110 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_110" } +Number RAIN_115MIN "Rain 115 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_115" } +Number RAIN_120MIN "Rain 120 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_120" } +``` + diff --git a/bundles/org.openhab.binding.buienradar/pom.xml b/bundles/org.openhab.binding.buienradar/pom.xml new file mode 100644 index 0000000000000..f46049913a0bb --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/pom.xml @@ -0,0 +1,11 @@ + + + 4.0.0 + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + org.openhab.binding.buienradar + openHAB Add-ons :: Bundles :: Buienradar Binding + diff --git a/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarBindingConstants.java b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarBindingConstants.java new file mode 100644 index 0000000000000..a009b6ea1c1a9 --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarBindingConstants.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.buienradar.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link BuienradarBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Edwin de Jong - Initial contribution + */ +@NonNullByDefault +public class BuienradarBindingConstants { + + private static final String BINDING_ID = "buienradar"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_RAIN_FORECAST = new ThingTypeUID(BINDING_ID, "rain_forecast"); + + public static final String FORECAST_0 = "forecast_0"; + public static final String FORECAST_5 = "forecast_5"; + public static final String FORECAST_10 = "forecast_10"; + public static final String FORECAST_15 = "forecast_15"; + public static final String FORECAST_20 = "forecast_20"; + public static final String FORECAST_25 = "forecast_25"; + public static final String FORECAST_30 = "forecast_30"; + public static final String FORECAST_35 = "forecast_35"; + public static final String FORECAST_40 = "forecast_40"; + public static final String FORECAST_45 = "forecast_45"; + public static final String FORECAST_50 = "forecast_50"; + public static final String FORECAST_55 = "forecast_55"; + public static final String FORECAST_60 = "forecast_60"; + public static final String FORECAST_65 = "forecast_65"; + public static final String FORECAST_70 = "forecast_70"; + public static final String FORECAST_75 = "forecast_75"; + public static final String FORECAST_80 = "forecast_80"; + public static final String FORECAST_85 = "forecast_85"; + public static final String FORECAST_90 = "forecast_90"; + public static final String FORECAST_95 = "forecast_95"; + public static final String FORECAST_100 = "forecast_100"; + public static final String FORECAST_105 = "forecast_105"; + public static final String FORECAST_110 = "forecast_110"; + public static final String FORECAST_115 = "forecast_115"; + +} diff --git a/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarConfiguration.java b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarConfiguration.java new file mode 100644 index 0000000000000..65e46acc5beb2 --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarConfiguration.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.buienradar.internal; + +/** + * The {@link BuienradarConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Edwin de Jong - Initial contribution + */ +public class BuienradarConfiguration { + + /** + * Location of the forecast from buienradar + */ + public String location; + + /** + * Refresh interval for retrieving results from buienradar. + */ + public Integer refreshIntervalMinutes; +} diff --git a/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarHandler.java b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarHandler.java new file mode 100644 index 0000000000000..4431c42c54ac8 --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarHandler.java @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.buienradar.internal; + +import static java.util.concurrent.TimeUnit.MINUTES; + +import java.io.IOException; +import java.math.BigDecimal; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.ScheduledFuture; + +import javax.measure.Unit; +import javax.measure.quantity.Speed; + +import org.apache.commons.lang.StringUtils; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.PointType; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.buienradar.internal.buienradarapi.BuienradarPredictionAPI; +import org.openhab.binding.buienradar.internal.buienradarapi.Prediction; +import org.openhab.binding.buienradar.internal.buienradarapi.PredictionAPI; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import tec.uom.se.unit.Units; + +/** + * The {@link BuienradarHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Edwin de Jong - Initial contribution + */ +@NonNullByDefault +public class BuienradarHandler extends BaseThingHandler { + + private final Logger logger = LoggerFactory.getLogger(BuienradarHandler.class); + + private final PredictionAPI client = new BuienradarPredictionAPI(); + + private @NonNullByDefault({}) ScheduledFuture listenableFuture; + + private @NonNullByDefault({}) PointType location; + + private static final Unit MILLIMETRE_PER_HOUR = Units.METRE.divide(1000).divide(Units.HOUR) + .asType(Speed.class); + + public BuienradarHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + + } + + @SuppressWarnings("null") + @Override + public void initialize() { + BuienradarConfiguration config = getConfigAs(BuienradarConfiguration.class); + + boolean configValid = true; + if (StringUtils.trimToNull(config.location) == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/offline.conf-error-missing-location"); + configValid = false; + } + + try { + location = new PointType(config.location); + } catch (IllegalArgumentException e) { + location = null; + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/offline.conf-error-parsing-location"); + configValid = false; + } + + if (configValid) { + updateStatus(ThingStatus.UNKNOWN); + } + if (listenableFuture == null || listenableFuture.isCancelled()) { + listenableFuture = scheduler.scheduleWithFixedDelay(this::refresh, 0L, config.refreshIntervalMinutes, + MINUTES); + } + } + + private void refresh() { + try { + @SuppressWarnings("null") + final List predictions = client.getPredictions(location); + for (final Prediction prediction : predictions) { + final BigDecimal intensity = prediction.getIntensity(); + final ZonedDateTime nowPlusThree = ZonedDateTime.now().plusMinutes(3); + final ZonedDateTime lastFiveMinute = nowPlusThree.withMinute((nowPlusThree.getMinute() / 5) * 5) + .withSecond(0).withNano(0); + final long minutesFromNow = lastFiveMinute.until(prediction.getDateTime(), ChronoUnit.MINUTES); + final long minuteClass = minutesFromNow; + logger.debug("Forecast for {} at {} is {}", minutesFromNow, prediction.getDateTime(), intensity); + if (minuteClass >= 0 && minuteClass <= 115) { + final String label = String.format(Locale.ENGLISH, "forecast_%d", minuteClass); + + /** @TODO: edejong 2019-04-03 Change to SmartHomeUnits.MILLIMETRE_PER_HOUR for OH 2.5 */ + updateState(label, new QuantityType(intensity, MILLIMETRE_PER_HOUR)); + } + } + + updateStatus(ThingStatus.ONLINE); + } catch (IOException e) { + logger.warn("Cannot retrieve predictions", e); + updateStatus(ThingStatus.ONLINE, ThingStatusDetail.COMMUNICATION_ERROR, + String.format("Could not reach buienradar: %s", e.getMessage())); + } + } + + @SuppressWarnings("null") + @Override + public void dispose() { + if (listenableFuture != null && !listenableFuture.isCancelled()) { + listenableFuture.cancel(true); + listenableFuture = null; + } + } +} diff --git a/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarHandlerFactory.java b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarHandlerFactory.java new file mode 100644 index 0000000000000..0354fe8f7ac5a --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/BuienradarHandlerFactory.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.buienradar.internal; + +import static org.openhab.binding.buienradar.internal.BuienradarBindingConstants.THING_TYPE_RAIN_FORECAST; + +import java.util.Collections; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link BuienradarHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Edwin de Jong - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.buienradar", service = ThingHandlerFactory.class) +public class BuienradarHandlerFactory extends BaseThingHandlerFactory { + + private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_RAIN_FORECAST); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + final ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (THING_TYPE_RAIN_FORECAST.equals(thingTypeUID)) { + return new BuienradarHandler(thing); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/buienradarapi/BuienradarParseException.java b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/buienradarapi/BuienradarParseException.java new file mode 100644 index 0000000000000..22304027d7e49 --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/buienradarapi/BuienradarParseException.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.buienradar.internal.buienradarapi; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * An exception thrown when a result from Buienradar could not be correctly parsed. + * + * @author Edwin de Jong - Initial contribution + */ +@NonNullByDefault +public class BuienradarParseException extends Exception { + + private static final long serialVersionUID = 1L; + + public BuienradarParseException() { + super(); + } + + public BuienradarParseException(String message) { + super(message); + } + + public BuienradarParseException(Throwable cause) { + super(cause); + } + + public BuienradarParseException(String message, Throwable cause) { + super(message, cause); + } + + public BuienradarParseException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/buienradarapi/BuienradarPredictionAPI.java b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/buienradarapi/BuienradarPredictionAPI.java new file mode 100644 index 0000000000000..cc4c71845f055 --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/buienradarapi/BuienradarPredictionAPI.java @@ -0,0 +1,156 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.buienradar.internal.buienradarapi; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.io.net.http.HttpUtil; +import org.eclipse.smarthome.core.library.types.PointType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link BuienradarPredictionAPI} class implements the methods for retrieving results from the buienradar.nl + * service. + * + * @author Edwin de Jong - Initial contribution + */ +@NonNullByDefault +public class BuienradarPredictionAPI implements PredictionAPI { + private static final ZoneId ZONE_AMSTERDAM = ZoneId.of("Europe/Amsterdam"); + + private static final String BASE_ADDRESS = "https://gpsgadget.buienradar.nl/data/raintext"; + + private static final int TIMEOUT_MS = 3000; + + private final Logger logger = LoggerFactory.getLogger(BuienradarPredictionAPI.class); + + /** + * Parses a raw intensity string, such as 000, into the right intensity in mm / hour. + * + * @param intensityStr The raw intensity string, being 3 characters long. + * @return The real intensity in mm / hour + * @throws BuienradarParseException when the intensity string could not be parsed. + */ + public static BigDecimal parseIntensity(String intensityStr) throws BuienradarParseException { + try { + // Intensity in mm / hour = 10^((value-109)/32) + double unrounded = Math.pow(10.0, (Integer.parseInt(intensityStr) - 109) / 32.0); + return BigDecimal.valueOf(unrounded).setScale(2, RoundingMode.HALF_EVEN); + } catch (NumberFormatException e) { + throw new BuienradarParseException("Could not parse intensity part of API", e); + } + } + + /** + * Parses a time string, such as 10:30 into a ZonedDateTime, using the reference ZonedDateTime to find + * the closest + * match. + * + * @param timeStr The time string to parse. + * @param now The reference time to use. + * @return A ZonedDateTime of the indicated time. + * @throws BuienradarParseException When the time string cannot be correctly parsed. + */ + public static ZonedDateTime parseDateTime(String timeStr, ZonedDateTime now) throws BuienradarParseException { + final String[] timeElements = timeStr.split(":"); + if (timeElements.length != 2) { + throw new BuienradarParseException("Expecting exactly two time elements"); + } + + final int hour = Integer.parseInt(timeElements[0]); + final int minute = Integer.parseInt(timeElements[1]); + final LocalTime time = LocalTime.of(hour, minute); + + final LocalDate localDateInAmsterdam = now.withZoneSameInstant(ZONE_AMSTERDAM).toLocalDate(); + + final ZonedDateTime tryDateTime = time.atDate(localDateInAmsterdam).atZone(ZONE_AMSTERDAM); + if (tryDateTime.plusMinutes(20).isBefore(now)) { + // Check me: could this go wrong at DTS days? + return time.atDate(localDateInAmsterdam.plusDays(1)).atZone(ZONE_AMSTERDAM); + } else { + return tryDateTime; + } + } + + /** + * Parses a line returned from the buienradar API service. An example line could be 100|23:00. + * + * @param line The line to parse, such as 100|23:00 + * @param now The reference time to determine which instant to match to. + * @return A Prediction interface, which contains the tuple with the intensity and the time. + * @throws BuienradarParseException Thrown when the line could not be correctly parsed. + */ + public static Prediction parseLine(String line, ZonedDateTime now) throws BuienradarParseException { + final String[] lineElements = line.trim().split("\\|"); + if (lineElements.length != 2) { + throw new BuienradarParseException( + String.format("Expected two line elements, but found %s", lineElements.length)); + } + final BigDecimal intensityOut = parseIntensity(lineElements[0]); + final ZonedDateTime dateTime = parseDateTime(lineElements[1], now); + return new Prediction() { + + @Override + public final BigDecimal getIntensity() { + return intensityOut; + } + + @Override + public final ZonedDateTime getDateTime() { + return dateTime; + } + }; + } + + @Override + public List getPredictions(PointType location) throws IOException { + final String address = String.format(Locale.ENGLISH, BASE_ADDRESS + "?lat=%.8f&lon=%.8f", + location.getLatitude().doubleValue(), location.getLongitude().doubleValue()); + final String result = HttpUtil.executeUrl("GET", address, TIMEOUT_MS); + + if (result.trim().isEmpty()) { + logger.warn(String.format("Buienradar API at URI %s return empty result", address)); + return Collections.emptyList(); + } + final List predictions = new ArrayList(24); + final List errors = new LinkedList(); + logger.debug("Returned result from buienradar: {}", result); + final String[] lines = result.split("\n"); + for (String line : lines) { + try { + predictions.add(parseLine(line, ZonedDateTime.now())); + } catch (BuienradarParseException e) { + errors.add(e.getMessage()); + } + } + if (!errors.isEmpty()) { + logger.warn("Could not parse all results: " + errors.stream().collect(Collectors.joining(", "))); + } + return predictions; + } + +} diff --git a/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/buienradarapi/Prediction.java b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/buienradarapi/Prediction.java new file mode 100644 index 0000000000000..7764566ac687b --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/buienradarapi/Prediction.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.buienradar.internal.buienradarapi; + +import java.math.BigDecimal; +import java.time.ZonedDateTime; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link Prediction} interface contains a prediction of rain at a specific time. + * + * @author Edwin de Jong - Initial contribution + */ +@NonNullByDefault +public interface Prediction { + /** + * Intensity of rain in mm/hour + */ + BigDecimal getIntensity(); + + /** + * Date-time of prediction. + */ + ZonedDateTime getDateTime(); +} diff --git a/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/buienradarapi/PredictionAPI.java b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/buienradarapi/PredictionAPI.java new file mode 100644 index 0000000000000..d3a23f5dd82b5 --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/src/main/java/org/openhab/binding/buienradar/internal/buienradarapi/PredictionAPI.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.buienradar.internal.buienradarapi; + +import java.io.IOException; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.PointType; + +/** + * The {@link PredictionAPI} interface. + * + * @author Edwin de Jong - Initial contribution + */ +@NonNullByDefault +public interface PredictionAPI { + List getPredictions(PointType location) throws IOException; +} diff --git a/bundles/org.openhab.binding.buienradar/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.buienradar/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..5fb8b6e93f664 --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,10 @@ + + + + Buienradar Binding + The Buienradar Binding periodically (5 minutes) retrieves results from the Buienradar API. For personal use only. + Edwin de Jong + + diff --git a/bundles/org.openhab.binding.buienradar/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.buienradar/src/main/resources/ESH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..effd19fd2f2a1 --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/src/main/resources/ESH-INF/thing/thing-types.xml @@ -0,0 +1,226 @@ + + + + + + + Does a simple rain forecast at the specified longitude/latitude of + two hours, in 5 minute increments using the buienradar.nl webservice. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + location + + Location of weather in geographical coordinates (latitude/longitude/altitude). + + + + + Refresh interval in minutes + 5 + + + + + + + Number:Speed + + Current rainfall + + + + + Number:Speed + + Rainfall in 5 minutes + + + + + Number:Speed + + Rainfall in 10 minutes + + + + + Number:Speed + + Rainfall in 15 minutes + + + + + Number:Speed + + Rainfall in 20 minutes + + + + + Number:Speed + + Rainfall in 25 minutes + + + + + Number:Speed + + Rainfall in 30 minutes + + + + + Number:Speed + + Rainfall in 35 minutes + + + + + Number:Speed + + Rainfall in 40 minutes + + + + + Number:Speed + + Rainfall in 45 minutes + + + + + Number:Speed + + Rainfall in 50 minutes + + + + + Number:Speed + + Rainfall in 55 minutes + + + + + Number:Speed + + Rainfall in 60 minutes + + + + + Number:Speed + + Rainfall in 65 minutes + + + + + Number:Speed + + Rainfall in 70 minutes + + + + + Number:Speed + + Rainfall in 75 minutes + + + + + Number:Speed + + Rainfall in 80 minutes + + + + + Number:Speed + + Rainfall in 85 minutes + + + + + Number:Speed + + Rainfall in 90 minutes + + + + + Number:Speed + + Rainfall in 95 minutes + + + + + Number:Speed + + Rainfall in 100 minutes + + + + + Number:Speed + + Rainfall in 105 minutes + + + + + Number:Speed + + Rainfall in 110 minutes + + + + + Number:Speed + + Rainfall in 115 minutes + + + + + diff --git a/bundles/org.openhab.binding.buienradar/src/test/java/org/openhab/binding/buienradar/BuienradarPredictionAPITest.java b/bundles/org.openhab.binding.buienradar/src/test/java/org/openhab/binding/buienradar/BuienradarPredictionAPITest.java new file mode 100644 index 0000000000000..2290d02d1ef14 --- /dev/null +++ b/bundles/org.openhab.binding.buienradar/src/test/java/org/openhab/binding/buienradar/BuienradarPredictionAPITest.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.buienradar; + +import static org.junit.Assert.assertEquals; + +import java.math.BigDecimal; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import org.junit.Test; +import org.openhab.binding.buienradar.internal.buienradarapi.BuienradarParseException; +import org.openhab.binding.buienradar.internal.buienradarapi.BuienradarPredictionAPI; +import org.openhab.binding.buienradar.internal.buienradarapi.Prediction; + +public class BuienradarPredictionAPITest { + private static final ZonedDateTime NOW = ZonedDateTime.of(2019, 3, 10, 20, 37, 0, 0, ZoneId.of("Europe/Amsterdam")); + + private static final ZonedDateTime NOW_LONDON = ZonedDateTime.of(2019, 3, 10, 20, 37, 0, 0, + ZoneId.of("Europe/London")); + + @Test + public void testParseIntensity000() throws BuienradarParseException { + assertEquals(BigDecimal.valueOf(0, 2), BuienradarPredictionAPI.parseIntensity("000")); + } + + @Test + public void testParseIntensity050() throws BuienradarParseException { + assertEquals(BigDecimal.valueOf(1, 2), BuienradarPredictionAPI.parseIntensity("050")); + } + + @Test + public void testParseIntensity500() throws BuienradarParseException { + assertEquals(BigDecimal.valueOf(165481709994318L, 2), BuienradarPredictionAPI.parseIntensity("500")); + } + + @Test + public void testParseIntensity101() throws BuienradarParseException { + assertEquals(BigDecimal.valueOf(56, 2), BuienradarPredictionAPI.parseIntensity("101")); + } + + @Test + public void testParseDateTime() throws BuienradarParseException { + final ZonedDateTime parsed = BuienradarPredictionAPI.parseDateTime("20:45", NOW); + assertEquals(ZonedDateTime.of(2019, 3, 10, 20, 45, 0, 0, ZoneId.of("Europe/Amsterdam")), parsed); + } + + @Test + public void testParseDateTimeLondon() throws BuienradarParseException { + // 20:37 in London is *before* 20:45 in Amsterdam, therefore, it should be parsed as a timestamp happening + // tomorrow. + final ZonedDateTime parsed = BuienradarPredictionAPI.parseDateTime("20:45", NOW_LONDON); + assertEquals(ZonedDateTime.of(2019, 3, 11, 20, 45, 0, 0, ZoneId.of("Europe/Amsterdam")), parsed); + } + + @Test + public void testParseDateTimeTomorrow() throws BuienradarParseException { + final ZonedDateTime parsed = BuienradarPredictionAPI.parseDateTime("19:40", NOW); + assertEquals(ZonedDateTime.of(2019, 3, 11, 19, 40, 0, 0, ZoneId.of("Europe/Amsterdam")), parsed); + } + + @Test + public void testParseLine() throws BuienradarParseException { + final Prediction parsed = BuienradarPredictionAPI.parseLine("000|19:35", NOW); + assertEquals(ZonedDateTime.of(2019, 3, 11, 19, 35, 0, 0, ZoneId.of("Europe/Amsterdam")), parsed.getDateTime()); + assertEquals(BigDecimal.valueOf(0, 2), parsed.getIntensity()); + } + +} diff --git a/bundles/org.openhab.binding.chromecast/.classpath b/bundles/org.openhab.binding.chromecast/.classpath new file mode 100644 index 0000000000000..615608997a6c5 --- /dev/null +++ b/bundles/org.openhab.binding.chromecast/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/binding/org.openhab.binding.chromecast/.gitignore b/bundles/org.openhab.binding.chromecast/.gitignore similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/.gitignore rename to bundles/org.openhab.binding.chromecast/.gitignore diff --git a/bundles/org.openhab.binding.chromecast/.project b/bundles/org.openhab.binding.chromecast/.project new file mode 100644 index 0000000000000..daae34788c058 --- /dev/null +++ b/bundles/org.openhab.binding.chromecast/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.chromecast + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.chromecast/NOTICE b/bundles/org.openhab.binding.chromecast/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/NOTICE rename to bundles/org.openhab.binding.chromecast/NOTICE diff --git a/addons/binding/org.openhab.binding.chromecast/README.md b/bundles/org.openhab.binding.chromecast/README.md similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/README.md rename to bundles/org.openhab.binding.chromecast/README.md diff --git a/bundles/org.openhab.binding.chromecast/pom.xml b/bundles/org.openhab.binding.chromecast/pom.xml new file mode 100644 index 0000000000000..448f71b15d639 --- /dev/null +++ b/bundles/org.openhab.binding.chromecast/pom.xml @@ -0,0 +1,25 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.chromecast + + openHAB Add-ons :: Bundles :: Chromecast Binding + + + + su.litvak.chromecast + api-v2 + 0.11.0 + provided + + + + diff --git a/addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastAudioSink.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastAudioSink.java similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastAudioSink.java rename to bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastAudioSink.java diff --git a/addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastBindingConstants.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastBindingConstants.java rename to bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastBindingConstants.java diff --git a/addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastCommander.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastCommander.java similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastCommander.java rename to bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastCommander.java diff --git a/addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastEventReceiver.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastEventReceiver.java similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastEventReceiver.java rename to bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastEventReceiver.java diff --git a/addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastScheduler.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastScheduler.java similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastScheduler.java rename to bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastScheduler.java diff --git a/addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastStatusUpdater.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastStatusUpdater.java similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastStatusUpdater.java rename to bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastStatusUpdater.java diff --git a/addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/discovery/ChromecastDiscoveryParticipant.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/discovery/ChromecastDiscoveryParticipant.java similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/discovery/ChromecastDiscoveryParticipant.java rename to bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/discovery/ChromecastDiscoveryParticipant.java diff --git a/addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/factory/ChromecastHandlerFactory.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/factory/ChromecastHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/factory/ChromecastHandlerFactory.java rename to bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/factory/ChromecastHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/handler/ChromecastHandler.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/handler/ChromecastHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/handler/ChromecastHandler.java rename to bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/handler/ChromecastHandler.java diff --git a/addons/binding/org.openhab.binding.chromecast/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.chromecast/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.chromecast/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.chromecast/ESH-INF/config/config.xml b/bundles/org.openhab.binding.chromecast/src/main/resources/ESH-INF/config/config.xml similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/ESH-INF/config/config.xml rename to bundles/org.openhab.binding.chromecast/src/main/resources/ESH-INF/config/config.xml diff --git a/addons/binding/org.openhab.binding.chromecast/ESH-INF/i18n/chromecast_de.properties b/bundles/org.openhab.binding.chromecast/src/main/resources/ESH-INF/i18n/chromecast_de.properties similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/ESH-INF/i18n/chromecast_de.properties rename to bundles/org.openhab.binding.chromecast/src/main/resources/ESH-INF/i18n/chromecast_de.properties diff --git a/addons/binding/org.openhab.binding.chromecast/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.chromecast/src/main/resources/ESH-INF/thing/thing-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.chromecast/ESH-INF/thing/thing-types.xml rename to bundles/org.openhab.binding.chromecast/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/bundles/org.openhab.binding.coolmasternet/src/main/java/org/openhab/binding/coolmasternet/internal/ControllerHandler.java b/bundles/org.openhab.binding.coolmasternet/src/main/java/org/openhab/binding/coolmasternet/internal/ControllerHandler.java index a347fce5f5979..703fbe03dfb85 100644 --- a/bundles/org.openhab.binding.coolmasternet/src/main/java/org/openhab/binding/coolmasternet/internal/ControllerHandler.java +++ b/bundles/org.openhab.binding.coolmasternet/src/main/java/org/openhab/binding/coolmasternet/internal/ControllerHandler.java @@ -17,6 +17,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.Reader; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; @@ -147,20 +148,23 @@ public boolean isConnected() { out.write(command.getBytes()); out.write("\r\n".getBytes()); - BufferedReader in = new BufferedReader(new InputStreamReader(localSocket.getInputStream())); - while (true) { - String line = in.readLine(); - logger.trace("Read result '{}'", line); - if ("OK".equals(line)) { - return response.toString(); - } - response.append(line); - if (response.length() > 100) { - /* - * Usually this loop times out on errors, but in the case that we just keep getting - * data we should also fail with an error. - */ - throw new CoolMasterClientError(String.format("Got gibberish response to command %s", command)); + try (Reader isr = new InputStreamReader(localSocket.getInputStream()); + BufferedReader in = new BufferedReader(isr)) { + while (true) { + String line = in.readLine(); + logger.trace("Read result '{}'", line); + if ("OK".equals(line)) { + return response.toString(); + } + response.append(line); + if (response.length() > 100) { + /* + * Usually this loop times out on errors, but in the case that we just keep getting + * data we should also fail with an error. + */ + throw new CoolMasterClientError( + String.format("Got gibberish response to command %s", command)); + } } } } catch (SocketTimeoutException e) { diff --git a/bundles/org.openhab.binding.darksky/.classpath b/bundles/org.openhab.binding.darksky/.classpath index a5d95095ccaaf..1a0c5608f3440 100644 --- a/bundles/org.openhab.binding.darksky/.classpath +++ b/bundles/org.openhab.binding.darksky/.classpath @@ -28,5 +28,11 @@ + + + + + + diff --git a/bundles/org.openhab.binding.darksky/README.md b/bundles/org.openhab.binding.darksky/README.md index 3e2f8e81d25e3..429506f79255f 100644 --- a/bundles/org.openhab.binding.darksky/README.md +++ b/bundles/org.openhab.binding.darksky/README.md @@ -125,7 +125,7 @@ Once the parameters `forecastHours` or `forecastDays` will be changed, the avail **Offset:** For each trigger channel you can optionally configure an `offset` in minutes. The `offset` must be configured in the channel properties for the corresponding thing. -The minimum allowed offset is -1440 and the maximum allowed offset is 1440. +The minimum allowed `offset` is -1440 and the maximum allowed `offset` is 1440. If an `offset` is set, the event is moved forward or backward accordingly. diff --git a/bundles/org.openhab.binding.darksky/src/main/resources/ESH-INF/thing/channel-types.xml b/bundles/org.openhab.binding.darksky/src/main/resources/ESH-INF/thing/channel-types.xml index 558481fdc4bb4..72d154e171752 100644 --- a/bundles/org.openhab.binding.darksky/src/main/resources/ESH-INF/thing/channel-types.xml +++ b/bundles/org.openhab.binding.darksky/src/main/resources/ESH-INF/thing/channel-types.xml @@ -338,7 +338,7 @@ Sun - + diff --git a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/discovery/ThingDiscoveryService.java b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/discovery/ThingDiscoveryService.java index 177d657822406..ec9ce87cc7375 100644 --- a/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/discovery/ThingDiscoveryService.java +++ b/bundles/org.openhab.binding.deconz/src/main/java/org/openhab/binding/deconz/internal/discovery/ThingDiscoveryService.java @@ -27,6 +27,7 @@ import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; import org.eclipse.smarthome.config.discovery.DiscoveryResult; import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.DiscoveryService; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.ThingHandler; @@ -41,7 +42,7 @@ * @author David Graeff - Initial contribution */ @NonNullByDefault -public class ThingDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService { +public class ThingDiscoveryService extends AbstractDiscoveryService implements DiscoveryService, ThingHandlerService { private static final Set SUPPORTED_THING_TYPES_UIDS = Collections .unmodifiableSet(Stream.of(THING_TYPE_PRESENCE_SENSOR, THING_TYPE_DAYLIGHT_SENSOR, THING_TYPE_POWER_SENSOR, THING_TYPE_CONSUMPTION_SENSOR, THING_TYPE_LIGHT_SENSOR, THING_TYPE_TEMPERATURE_SENSOR, @@ -143,7 +144,6 @@ public void activate() { @Override public void deactivate() { - removeOlderResults(getTimestampOfLastScan()); super.deactivate(); } diff --git a/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/DigiplexHandlerFactory.java b/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/DigiplexHandlerFactory.java index 9605d7ae6d2dc..6348e915623a7 100644 --- a/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/DigiplexHandlerFactory.java +++ b/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/DigiplexHandlerFactory.java @@ -51,10 +51,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { } else if (thingTypeUID.equals(THING_TYPE_AREA)) { return new DigiplexAreaHandler(thing); } else if (thingTypeUID.equals(THING_TYPE_BRIDGE)) { - DigiplexBridgeHandler bridge = new DigiplexBridgeHandler((Bridge) thing); - return bridge; + return new DigiplexBridgeHandler((Bridge) thing); } - return null; } diff --git a/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/discovery/DigiplexDiscoveryService.java b/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/discovery/DigiplexDiscoveryService.java index f12f1ec7a039a..bebf19ec4d7ca 100644 --- a/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/discovery/DigiplexDiscoveryService.java +++ b/bundles/org.openhab.binding.digiplex/src/main/java/org/openhab/binding/digiplex/internal/discovery/DigiplexDiscoveryService.java @@ -12,11 +12,12 @@ */ package org.openhab.binding.digiplex.internal.discovery; +import static org.openhab.binding.digiplex.internal.DigiplexBindingConstants.*; + import java.util.Collections; import java.util.HashMap; import java.util.Map; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; @@ -26,7 +27,6 @@ import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.ThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; -import org.openhab.binding.digiplex.internal.DigiplexBindingConstants; import org.openhab.binding.digiplex.internal.communication.AreaLabelRequest; import org.openhab.binding.digiplex.internal.communication.AreaLabelResponse; import org.openhab.binding.digiplex.internal.communication.DigiplexMessageHandler; @@ -34,7 +34,6 @@ import org.openhab.binding.digiplex.internal.communication.ZoneLabelRequest; import org.openhab.binding.digiplex.internal.communication.ZoneLabelResponse; import org.openhab.binding.digiplex.internal.handler.DigiplexBridgeHandler; -import org.osgi.service.component.annotations.Component; /** * Service for discovering things on Digiplex alarm systems @@ -43,20 +42,18 @@ * */ @NonNullByDefault -@Component(service = DiscoveryService.class, immediate = false, configurationPid = "discovery.digiplex") public class DigiplexDiscoveryService extends AbstractDiscoveryService - implements ThingHandlerService, DigiplexMessageHandler { + implements DiscoveryService, ThingHandlerService, DigiplexMessageHandler { private static final int MAX_ZONE = 96; private static final int MAX_AREA = 8; private static final int DISCOVERY_TIMEOUT = 30; - @Nullable - DigiplexBridgeHandler bridgeHandler; + private @Nullable DigiplexBridgeHandler bridgeHandler; public DigiplexDiscoveryService() { - super(Collections.singleton(DigiplexBindingConstants.THING_TYPE_ZONE), DISCOVERY_TIMEOUT, false); + super(Collections.singleton(THING_TYPE_ZONE), DISCOVERY_TIMEOUT, false); } @Override @@ -91,11 +88,10 @@ public void handleZoneLabelResponse(ZoneLabelResponse response) { } ThingUID bridgeUID = bridgeHandler.getThing().getUID(); - ThingUID thingUID = new ThingUID(DigiplexBindingConstants.THING_TYPE_ZONE, bridgeUID, - String.format("zone%d", response.zoneNo)); + ThingUID thingUID = new ThingUID(THING_TYPE_ZONE, bridgeUID, String.format("zone%d", response.zoneNo)); Map properties = new HashMap<>(1); - properties.put(DigiplexBindingConstants.PROPERTY_ZONE_NO, Integer.toString(response.zoneNo)); + properties.put(PROPERTY_ZONE_NO, Integer.toString(response.zoneNo)); DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID) .withProperties(properties).withLabel(response.zoneName).build(); @@ -104,7 +100,7 @@ public void handleZoneLabelResponse(ZoneLabelResponse response) { } private boolean isDefaultName(ZoneLabelResponse response) { - return DigiplexBindingConstants.ZONE_DEFAULT_NAMES.stream().anyMatch(format -> { + return ZONE_DEFAULT_NAMES.stream().anyMatch(format -> { if (String.format(format, response.zoneNo).equals(response.zoneName)) { return true; } else { @@ -115,19 +111,17 @@ private boolean isDefaultName(ZoneLabelResponse response) { @Override @SuppressWarnings("null") - public void handleAreaLabelResponse(@NonNull AreaLabelResponse response) { + public void handleAreaLabelResponse(AreaLabelResponse response) { // we have no other option to check whether area is actually enabled than to compare its name with the default - if (response.success && response.areaName - .equals(String.format(DigiplexBindingConstants.AREA_DEFAULT_NAME, response.areaNo))) { + if (response.success && response.areaName.equals(String.format(AREA_DEFAULT_NAME, response.areaNo))) { return; } ThingUID bridgeUID = bridgeHandler.getThing().getUID(); - ThingUID thingUID = new ThingUID(DigiplexBindingConstants.THING_TYPE_AREA, bridgeUID, - String.format("area%d", response.areaNo)); + ThingUID thingUID = new ThingUID(THING_TYPE_AREA, bridgeUID, String.format("area%d", response.areaNo)); Map properties = new HashMap<>(1); - properties.put(DigiplexBindingConstants.PROPERTY_AREA_NO, Integer.toString(response.areaNo)); + properties.put(PROPERTY_AREA_NO, Integer.toString(response.areaNo)); DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID) .withProperties(properties).withLabel(response.areaName).build(); @@ -137,21 +131,24 @@ public void handleAreaLabelResponse(@NonNull AreaLabelResponse response) { @Override public void setThingHandler(@Nullable ThingHandler handler) { - bridgeHandler = (DigiplexBridgeHandler) handler; - if (bridgeHandler != null) { + if (handler instanceof DigiplexBridgeHandler) { + bridgeHandler = (DigiplexBridgeHandler) handler; bridgeHandler.registerMessageHandler(this); } } @Override - @Nullable - public ThingHandler getThingHandler() { + public @Nullable ThingHandler getThingHandler() { return bridgeHandler; } + @Override + public void activate() { + super.activate(null); + } + @Override public void deactivate() { super.deactivate(); } - } diff --git a/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/binding/binding.xml index 51f08f87ae93b..cc6803e551bb6 100644 --- a/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/binding/binding.xml +++ b/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/binding/binding.xml @@ -1,8 +1,7 @@ - + Digiplex/EVO Binding Binding for Digiplex/EVO alarm systems (utilizing PRT3 module) diff --git a/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/thing/area.xml b/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/thing/area.xml index 3c64c2a870c45..1b6e5735c6f70 100644 --- a/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/thing/area.xml +++ b/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/thing/area.xml @@ -1,141 +1,142 @@ - - - - - - - - Area - - - - - - - - - - - - - - - Controls how often area status will be refreshed from the PRT3 module - 60 - - - + + + + + + + + Area + + + + + + + + + + + + + + + Controls how often area status will be refreshed from the PRT3 module + 60 + + + - String - - Area Status as received from 'Area Status Request' - - - - - - - - - - + String + + Area Status as received from 'Area Status Request' + + + + + + + + + + + + + Contact + + Indicates if area is armed + + + + + + + - - Contact - - Indicates if area is armed - - - - - - - + + Contact + + + + + + + + - - Contact - - - - - - - - + + Contact + + + + + + + + - - Contact - - - - - - - - + + Contact + + Indicates if area is ready (no open zones) + + + + + + + - - Contact - - Indicates if area is ready (no open zones) - - - - - - - + + Contact + + Indicates if area is in the programming mode + + + + + + + - - Contact - - Indicates if area is in the programming mode - - - - - - - + + Contact + + Indicates if area is in alarm + + + + + + + - - Contact - - Indicates if area is in alarm - - - - - - - + + Contact + + + + + + + + - - Contact - - - - - - - - - - - String - - Used to control area status. By reading its state one can check result of the last command sent to the alarm system. - - - - - - - + + String + + Used to control area status. By reading its state one can check result of the last command sent to the alarm system. + + + + + + + diff --git a/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/thing/bridge.xml b/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/thing/bridge.xml index 516c76220e6e0..b4ffefaaec3cd 100644 --- a/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/thing/bridge.xml +++ b/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/thing/bridge.xml @@ -1,67 +1,65 @@ - + - Digiplex PRT3 module with Serial Interface - - - - - + + + + + - - - communication - - - - - - - serial-port - Set the serial port used to access PRT3 device - - - - - - serial-port - Set the serial port baud rate - 2400 - true - - - - - - - - + + + communication + + + + + + + serial-port + Set the serial port used to access PRT3 device + + + + + + serial-port + Set the serial port baud rate + 2400 + true + + + + + + + + - + - Number - - Counts messages sent to the alarm system + Number + + Counts messages sent to the alarm system - - - Number - - Counts responses received from the alarm system - - - Number - - Counts events received from the alarm system - + + Number + + Counts responses received from the alarm system + + + + Number + + Counts events received from the alarm system + diff --git a/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/thing/zone.xml b/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/thing/zone.xml index 30d4117b08fd7..339751e32f966 100644 --- a/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/thing/zone.xml +++ b/bundles/org.openhab.binding.digiplex/src/main/resources/ESH-INF/thing/zone.xml @@ -1,104 +1,105 @@ - - + + - - - - - Zone - - - - - - - - - + + + + + Zone + + + + + + + + + - Contact - - Zone Status (Open/Closed) - - - - - - - + Contact + + Zone Status (Open/Closed) + + + + + + + - - String - - Indicates actual zone state as a string - - - - - - - - - + + String + + Indicates actual zone state as a string + + + + + + + + + - - Contact - - Indicates if zone is in alarm - - - - - - - + + Contact + + Indicates if zone is in alarm + + + + + + + - - Contact - - Indicates if zone is in fire alarm - - - - - - - + + Contact + + Indicates if zone is in fire alarm + + + + + + + - - Contact - - Indicates if zone has lost a supervision - - - - - - - + + Contact + + Indicates if zone has lost a supervision + + + + + + + - - Contact - - Indicates if zone is low on battery - - - - - - - + + Contact + + Indicates if zone is low on battery + + + + + + + - - DateTime - - Indicates when the zone has been triggered for the last time - - + + DateTime + + Indicates when the zone has been triggered for the last time + + diff --git a/bundles/org.openhab.binding.dsmr/.classpath b/bundles/org.openhab.binding.dsmr/.classpath new file mode 100644 index 0000000000000..1a0c5608f3440 --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/.classpath @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.dsmr/.project b/bundles/org.openhab.binding.dsmr/.project new file mode 100644 index 0000000000000..a10d6445872dd --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.dsmr + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.bluetooth/NOTICE b/bundles/org.openhab.binding.dsmr/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.bluetooth/NOTICE rename to bundles/org.openhab.binding.dsmr/NOTICE diff --git a/bundles/org.openhab.binding.dsmr/README.md b/bundles/org.openhab.binding.dsmr/README.md new file mode 100644 index 0000000000000..28c960918c806 --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/README.md @@ -0,0 +1,336 @@ +# DSMR Binding + +The DSMR-binding is targeted towards Dutch and Luxembourger users having a smart meter (Dutch: 'Slimme meter'). +Data of Dutch/Luxembourg smart meters can be obtained via the P1-port. +When connecting this port from a serial port the data can be read out. + +This binding reads the P1-port of the Dutch Smart Meters that comply to NTA8130, DSMR v2.1, DSMR v2.2, DSMR v3.0, DSMR v4.0, DSMR v4.04, and DSMR 5.0. +This binding reads the P1-port of the Luxembourg’s electricity meter "Smarty" that comply to V1.0. +Although DSMR v4.2 is not an official specification, the binding has support for this version. + +If you are not living in the Netherlands/Luxembourg but do want to read a meter please have look at the [SmartMeter Binding](https://www.openhab.org/addons/bindings/smartmeter). +Because the Dutch Meter standard is based on the IEC-62056-21 standard it might be desirable to build support for other country metering systems based on that standard in this binding. + +## Serial Port Configuration + +The P1-port is a serial port. To configure the serial port within openHAB see the [general documentation about serial port configuration](/docs/administration/serial.html). + +## Supported Things + +### dsmrBridge (The Netherlands) + +`dsmrBridge`: This is the device that communicated between the binding (serial) and its internal meters. +You always have to have a 'Dutch Smart Meter'-bridge. The bridge contains the serial port configuration. +Specific meters are bound via the bridge to the smart meter. A smart meter consists typically out of minimal 2 meters. +A generic meter and the electricity meter. Each meter is bound to the DSMR protocol the physical meter supports. +For each meter it is possible to set a refresh rate at which the status is updated. +The physical meter might update with a high frequency per second, while it's desired to have only values per minute. + +### smartyBridge (Luxembourg) + +`smartyBridge`: This is the device that communicated between the binding (serial) and its internal meters. +You always have to have a 'Dutch Smart Meter'-bridge. The bridge contains the serial port configuration. + +## Discovery + +The `dsmrBridge` and meters can be discovered via the discovery process. +The `smartyBridge` can be discovered. +Because the `smartyBridge` requires a decryption key. +You need to set the decryption key when the bridge is added. +After the decryption key is set a new discovery can be started to discover the meter. + +If a bridge is manually configured it is possible to auto detect available meters. + +### Configuration + +The configuration for the `dsmrBridge` consists of the following parameters: + +| Parameter | Description | +|---------------------|-------------------------------------------------------------------------------------------------------------| +| serialPort | The serial port where the P1-port is connected to (e.g. Linux: `/dev/ttyUSB1`, Windows: `COM2`) (mandatory) | +| receivedTimeout | The time out period in which messages are expected to arrive, default is 120 seconds | +| baudrate | Baudrate when no auto detect. valid values: 4800, 9600, 19200, 38400, 57600, 115200 | +| databits | Data bits when no auto detect. valid values: 5, 6, 7, 8 | +| parity | Parity when no auto detect. valid values: E(ven), N(one), O(dd) | +| stopbits | Stop bits when no auto detect. valid values: 1, 1.5, 2 | + +The configuration for the `smartyBridge` consists of the following parameters: + +| Parameter | Description | +|---------------------|-------------------------------------------------------------------------------------------------------------| +| serialPort | The serial port where the P1-port is connected to (e.g. Linux: `/dev/ttyUSB1`, Windows: `COM2`) (mandatory) | +| decryptionKey | The meter specific decryption key (mandatory) | +| receivedTimeout | The time out period in which messages are expected to arrive, default is 120 seconds | + + +**Note:** *The manual configuration is only needed if the DSMR-device requires non DSMR-standard Serial Port parameters (i.e. something different then `115200 8N1` or `9600 7E1`)* + +### Troubleshooting + +If there are unexpected configuration issues. +For example a meter could not be found or not all channels expected are available. +Than run the discovery process and look into the log file. +There are extra checks that can give more information about what might be wrong. + +## Meters + +The information in this paragraph in necessary if you choose to configure the meters manually in a `.things` file. + +Supported meters: + +| Meter Thing | Thing type ID | M-Bus channel | Refresh rate | +|-------------------------------------------------|------------------------------|---------------|--------------| +| DSMR V2 / V3 Device | `device_v2_v3` | -1 | 10 seconds | +| DSMR V4 Device | `device_v4` | -1 | 10 seconds | +| DSMR V5 Device | `device_v5` | -1 | 10 seconds | +| ACE4000 GTMM Mk3 Electricity meter | `electricity_ace4000` | 0 | 10 seconds | +| DSMR V2.1 Electricity meter | `electricity_v2_1` | 0 | 10 seconds | +| DSMR V2.2 Electricity meter | `electricity_v2_2` | 0 | 10 seconds | +| DSMR V3 Electricity meter | `electricity_v3_0` | 0 | 10 seconds | +| DSMR V4.0 Electricity meter | `electricity_v4_0` | 0 | 10 seconds | +| DSMR V4.0.4 Electricity meter | `electricity_v4_0_4` | 0 | 10 seconds | +| DSMR V4.2 Electricity meter | `electricity_v4_2` | 0 | 10 seconds | +| DSMR V5 Electricity meter | `electricity_v5_0` | 0 | 10 seconds | +| Smarty V1.0 Electricity Meter | `electricity_smarty_v1_0` | 0 | 10 seconds | +| ACE4000 GTMM Mk3 Gas meter | `gas_ace4000` | 3 | 1 hour | +| DSMR V2.1 Gas meter | `gas_v2_1` | 0 | 24 hours | +| DSMR V2.2 Gas meter | `gas_v2_2` | 0 | 24 hours | +| DSMR V3.0 Gas meter | `gas_v3_0` | *note 1* | 1 hour | +| ACE4000 GTMM Mk3 Cooling meter | `cooling_ace4000` | 6 | 1 hour | +| DSMR V2.2 Cooling meter | `cooling_v2_2` | 0 | 1 hour | +| ACE4000 GTMM Mk3 Heating meter | `heating_ace4000` | 4 | 1 hour | +| DSMR V2.2 Heating meter | `heating_v2_2` | 0 | 1 hour | +| ACE4000 GTMM Mk3 Water meter | `water_ace4000` | 5 | 1 hour | +| DSMR V2.2 Water meter | `water_v2_2` | 0 | 1 hour | +| DSMR V3.0 Water meter | `water_v3_0` | *note 1* | 1 hour | +| ACE4000 GTMM Mk3 1st Slave Electricity meter | `slave_electricity1_ace4000` | 1 | 1 hour | +| ACE4000 GTMM Mk3 2nd Slave Electricity meter | `slave_electricity2_ace4000` | 2 | 1 hour | +| DSMR V4.x Slave Electricity meter | `slave_electricity_v4` | *note 1* | 1 hour | +| DSMR V5 Slave Electricity meter | `slave_electricity_v5` | *note 1* | 5 minutes | +| DSMR V3.0 Generic meter | `generic_v3_0` | *note 1* | 1 hour | +| DSMR V3.0 Giga Joule meter (heating or cooling) | `gj_v3_0` | *note 1* | 1 hour | +| DSMR V4.x Giga Joule meter (heating or cooling) | `gj_v4` | *note 1* | 1 hour | +| DSMR V5 Giga Joule meter (heating or cooling) | `gj_v5_0` | *note 1* | 5 minutes | +| DSMR V4.x m3 meter (gas or water) | `m3_v4` | *note 1* | 1 hour | +| DSMR V5 m3 meter (gas or water) | `m3_v5_0` | *note 1* | 5 minutes | + +*note 1*. The channel of these meters is dependent on the physical installation and corresponds to the M-Bus channel. +You can ask your supplier / installer for this information or you can retrieve it from the logfiles (see *Determine M-Bus channel*). + +#### Configuration + +The configuration for the meters consists of the following parameters: + +| Parameter | Description | +|---------------------|--------------------------------------------------------------------------------------| +| refresh | Time in seconds with which the state of the device is updated. Default is 60 seconds | +| channel | M-Bus channel. See the table above | + + +**Examples** + +``` +Bridge dsmr:dsmrBridge:myDSMRDevice [serialPort="/dev/ttyUSB0"] { + Things: + device_v5 dsmrDeviceV5 [channel=-1] + electricity_v5_0 electricityV5 [channel=0] +} +``` + +### Channels + +#### Item configuration + +Paper UI. Item configuration can be done in the regular way. + +Manual configuration: +The following channels are supported: + +- Y channel is supported +- \- channel is not supported +- O channel is supported only if the device has this functionality + + +| Channel Type ID | Item Type | Description | Ace4000 | DSMR V2.1 | DSMR V2.2 | DSMR V3.0 | DSMR V4.0 | DSMR V4.0.4 | DSMR V4.2 | DSMR V5 | SMARTY V1.0 | +|--------------------------------------------------|--------------------------|------------------------------------------------------------------------|---------|-----------|-----------|-----------|-----------|-------------|-----------|---------|-------------| +| | | **Channels for the generic device** | | | | | | | | | | +| `p1_text_code` | String | Text code from the device | - | Y | Y | Y | Y | Y | Y | - | - | +| `p1_text_string` | String | Text string from the device | - | Y | Y | Y | Y | Y | Y | Y | Y | +| `p1_version_output` | String | Version information (most times this refers to the DSMR specification) | - | - | - | - | Y | Y | Y | Y | Y | +| `p1_timestamp` | DateTime | Timestamp of the last device reading | - | - | - | - | Y | Y | Y | Y | Y | +| | | **Channels for the cooling meter** | | | | | | | | | | +| `cmeter_value_v2` | Number:Energy | The total amount of cooling used in the past period (GJ) | Y | - | Y | - | - | - | - | - | - | +| `cmeter_value_v2_timestamp` | DateTime | Timestamp of the last meter reading | Y | - | Y | - | - | - | - | - | - | +| `cmeter_equipment_identifier_v2_2` | String | Equipment identifier | - | - | Y | - | - | - | - | - | - | +| | | **Channels for the main electricity meter** | | | | | | | | | | +| `emeter_equipment_identifier_v2_x` | String | Electricity Equipment identifier | - | Y | Y | - | - | - | - | - | Y | +| `emeter_equipment_identifier` | String | Electricity Equipment identifier | - | - | - | Y | Y | Y | Y | Y | - | +| `emeter_delivery_tariff0` | Number:Energy | Total amount of electricity used for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | Y | +| `emeter_delivery_tariff1` | Number:Energy | Total amount of electricity used for tariff 1 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | - | +| `emeter_delivery_tariff2` | Number:Energy | Total amount of electricity used for tariff 2 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | - | +| `emeter_production_tariff0` | Number:Energy | Total amount of electricity produced for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | Y | +| `emeter_production_tariff1` | Number:Energy | Total amount of electricity produced for tariff 1 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | - | +| `emeter_production_tariff2` | Number:Energy | Total amount of electricity produced for tariff 2 (kWh) | Y | Y | Y | Y | Y | Y | Y | Y | - | +| `emeter_delivery_tariff0_antifraud` | Number:Energy | Total amount of electricity used for tariff 2 [antifraud] (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_delivery_tariff1_antifraud` | Number:Energy | Total amount of electricity used for tariff 1 [antifraud] (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_delivery_tariff2_antifraud` | Number:Energy | Total amount of electricity used for tariff 2 [antifraud] (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_total_imported_energy_register_q` | Number:Energy | Total Imported Energy (Q+) (kvarh) | - | - | - | - | - | - | - | - | Y | +| `emeter_total_exported_energy_register_q` | Number:Energy | Total Exported Energy (Q-) (kvarh) | - | - | - | - | - | - | - | - | Y | +| `emeter_tariff_indicator` | String | Current tariff indicator | Y | Y | Y | Y | Y | Y | Y | Y | - | +| `emeter_treshold_a_v2_1` | Number:ElectricCurrent | Actual treshold (A) | - | Y | - | - | - | - | - | - | - | +| `emeter_treshold_a` | Number:ElectricCurrent | Actual treshold (A) | Y | - | Y | Y | - | - | - | - | - | +| `emeter_treshold_kwh` | Number:Power | Actual treshold (kW) | - | - | - | - | Y | Y | - | - | - | +| `emeter_switch_position_v2_1` | Number | Switch position | - | Y | - | - | - | - | - | - | - | +| `emeter_switch_position` | Number | Switch position | Y | - | Y | Y | Y | Y | - | - | Y | +| `emeter_active_import_power` | Number:Power | Aggregate active import power (W) | Y | - | - | - | - | - | - | - | - | +| `emeter_actual_delivery` | Number:Power | Current power delivery (kW) | - | Y | Y | Y | Y | Y | Y | Y | Y | +| `emeter_actual_production` | Number:Power | Current power production (kW) | - | - | - | Y | Y | Y | Y | Y | Y | +| `emeter_actual_reactive_delivery` | Number | Actual Reactive Power Delivery (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_actual_reactive_production` | Number | Actual Reactive Power Production (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_active_threshold_smax` | Number | Active threshold (SMAX) (kVA) | - | - | - | - | - | - | - | - | Y | +| `emeter_power_failures` | Number | Number of power failures | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_long_power_failures` | Number | Number of long power failures | - | - | - | - | Y | Y | Y | Y | - | +| `emeter_power_failure_log_entries` | Number | Number of entries in the power failure log | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_power_failure_log_timestamp[x]` *note 2* | DateTime | Timestamp for entry [x] in the power failure log | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_power_failure_log_duration[x]` *note 2* | Number:Time | Duration for entry [x] the power failure log | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_voltage_sags_l1` | Number | Number of voltage sags L1 | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_voltage_sags_l2` | Number | Number of voltage sags L2 | - | - | - | - | O | O | O | O | O | +| `emeter_voltage_sags_l3` | Number | Number of voltage sags L3 | - | - | - | - | O | O | O | O | O | +| `emeter_voltage_swells_l1` | Number | Number of voltage swells L1 | - | - | - | - | Y | Y | Y | Y | - | +| `emeter_voltage_swells_l2` | Number | Number of voltage swells L2 | - | - | - | - | O | O | O | O | - | +| `emeter_voltage_swells_l3` | Number | Number of voltage swells L3 | - | - | - | - | O | O | O | O | - | +| `emeter_instant_current_l1` | Number:ElectricCurrent | Instant Current L1 (A) | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_instant_current_l2` | Number:ElectricCurrent | Instant Current L2 (A) | - | - | - | - | O | O | O | O | O | +| `emeter_instant_current_l3` | Number:ElectricCurrent | Instant Current L3 (A) | - | - | - | - | O | O | O | O | O | +| `emeter_instant_power_delivery_l1` | Number:Power | Instant Power Delivery L1 (kW) | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_instant_power_delivery_l2` | Number:Power | Instant Power Delivery L2 (kW) | - | - | - | - | O | O | O | O | O | +| `emeter_instant_power_delivery_l3` | Number:Power | Instant Power Delivery L3 (kW) | - | - | - | - | O | O | O | O | O | +| `emeter_instant_power_production_l1` | Number:Power | Instant Power Production L1 (kW) | - | - | - | - | Y | Y | Y | Y | Y | +| `emeter_instant_power_production_l2` | Number:Power | Instant Power Production L2 (kW) | - | - | - | - | O | O | O | O | O | +| `emeter_instant_power_production_l3` | Number:Power | Instant Power Production L3 (kW) | - | - | - | - | O | O | O | O | O | +| `emeter_instant_reactive_power_delivery_l1` | Number:Power | Instant Reactive Power Delivery L1 (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_instant_reactive_power_delivery_l2` | Number:Power | Instant Reactive Power Delivery L2 (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_instant_reactive_power_delivery_l3` | Number:Power | Instant Reactive Power Delivery L3 (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_instant_reactive_power_production_l1` | Number:Power | Instant Reactive Power Prodcution L1 (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_instant_reactive_power_production_l2` | Number:Power | Instant Reactive Power Prodcution L2 (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_instant_reactive_power_production_l3` | Number:Power | Instant Reactive Power Prodcution L3 (kvar) | - | - | - | - | - | - | - | - | Y | +| `emeter_instant_voltage_l1` | Number:ElectricPotential | Instant Voltage L1 (V) | - | - | - | - | - | - | - | Y | - | +| `emeter_instant_voltage_l2` | Number:ElectricPotential | Instant Voltage L2 (V) | - | - | - | - | - | - | - | O | - | +| `emeter_instant_voltage_l3` | Number:ElectricPotential | Instant Voltage L3 (V) | - | - | - | - | - | - | - | O | - | +| | | **Channels for the slave electricity meter** | | | | | | | | | | +| `meter_device_type` | String | Slave Electricity Meter Device Type | - | - | - | - | Y | Y | Y | Y | - | +| `meter_equipment_identifier` | String | Slave Electricity Meter ID | - | - | - | - | Y | Y | Y | Y | - | +| `emeter_delivery_tariff0` | Number:Energy | Total amount of slave electricity used for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_delivery_tariff1` | Number:Energy | Total amount of slave electricity used for tariff 1 (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_delivery_tariff2` | Number:Energy | Total amount of slave electricity used for tariff 2 (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_production_tariff0` | Number:Energy | Total amount of slave electricity produced for tariff 0 (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_production_tariff1` | Number:Energy | Total amount of slave electricity produced for tariff 1 (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_production_tariff2` | Number:Energy | Total amount of slave electricity produced for tariff 2 (kWh) | Y | - | - | - | - | - | - | - | - | +| `emeter_tariff_indicator` | String | Current slave tariff indicator | Y | - | - | - | - | - | - | - | - | +| `emeter_treshold_a` | Number:ElectricCurrent | Actual slave treshold (A) | Y | - | - | - | - | - | - | - | - | +| `meter_switch_position` | Number | Slave electricity switch position | Y | - | - | Y | Y | Y | - | - | - | +| `emeter_active_import_power` | Number:Power | Slave aggregate active import power (W) | Y | - | - | - | - | - | - | - | - | +| `emeter_value` | Number:Energy | Slave electricity usage (kWh) in the past period | - | - | - | - | Y | Y | Y | Y | - | +| `emeter_value_timestamp` | DateTime | Timestamp of the last reading | - | - | - | - | Y | Y | Y | Y | - | +| `meter_device_type` | String | Gas Meter Device Type | - | - | - | Y | - | - | - | - | - | +| `meter_equipment_identifier` | String | Gas Meter ID | Y | - | - | Y | - | - | - | - | - | +| | | **Channels for the gas meter** | | | | | | | | | | +| `gmeter_equipment_identifier_v2` | String | Gas Meter ID | - | Y | Y | - | - | - | - | - | - | +| `gmeter_24h_delivery_v2` | Number:Volume | Gas Delivery past 24 hours | Y | Y | Y | - | - | - | - | - | - | +| `gmeter_24h_delivery_v2_timestamp` | DateTime | Timestamp of the last reading | Y | Y | Y | - | - | - | - | - | - | +| `gmeter_24h_delivery_compensated_v2` | Number:Volume | Gas Delivery past 24 hours (compensated) | - | Y | Y | - | - | - | - | - | - | +| `gmeter_24h_delivery_compensated_v2_timestamp` | DateTime | Timestamp of the last reading | - | Y | Y | - | - | - | - | - | - | +| `gmeter_value_v3` | Number:Volume | Gas Delivery past period | - | - | - | Y | - | - | - | - | - | +| `gmeter_value_v3_timestamp` | DateTime | Timestamp of the last reading | - | - | - | Y | - | - | - | - | - | +| `gmeter_valve_position_v2_1` | Number | Gas Valve position | - | Y | - | - | - | - | - | - | - | +| `gmeter_valve_position_v2_2` | Number | Gas Valve position | Y | - | Y | - | - | - | - | - | - | +| | | **Channels for the generic meter** | | | | | | | | | | +| `meter_valve_switch_position` | Number | Gas Valve position | - | - | - | Y | - | - | - | - | - | +| `meter_device_type` | String | Generic Meter Device Type | - | - | - | Y | - | - | - | - | - | +| `gmeter_equipment_identifier` | String | Generic Meter ID | - | - | - | Y | - | - | - | - | - | +| `genmeter_value_v3` | Number | Delivery past period | - | - | - | Y | - | - | - | - | - | +| `meter_valve_switch_position` | Number | Generic Meter Valve/Switch position | - | - | - | Y | - | - | - | - | - | +| | | **Channels for the GJ meter (Heating or Cooling)** | | | | | | | | | | +| `meter_device_type` | String | GJ Meter Device Type | - | - | - | Y | Y | Y | Y | Y | - | +| `meter_equipment_identifier` | Number | GJ Meter ID | - | - | - | Y | Y | Y | Y | Y | - | +| `gjmeter_value_v3` | Number:Energy | GJ Delivery past period | - | - | - | Y | - | - | - | - | - | +| `gjmeter_value_v3_timestamp` | DateTime | Timestamp of the last reading | - | - | - | Y | - | - | - | - | - | +| `gjmeter_value_v4` | Number:Energy | GJ Delivery past period | - | - | - | - | Y | Y | Y | Y | - | +| `gjmeter_value_v4_timestamp` | DateTime | Timestamp of the last reading | - | - | - | - | Y | Y | Y | Y | - | +| | | **Channels for the heating meter** | | | | | | | | | | +| `meter_valve_switch_position` | Number | GJ Meter Valve position | - | - | - | Y | Y | Y | Y | - | - | +| `meter_equipment_identifier` | String | Heating Meter ID | Y | - | - | - | - | - | - | - | - | +| `hmeter_equipment_identifier_v2_2` | String | Heating Meter ID | - | - | Y | - | - | - | - | - | - | +| `hmeter_value_v2` | Number:Energy | Heating Delivery past period | Y | - | Y | - | - | - | - | - | - | +| `hmeter_value_v2_timestamp` | DateTime | Timestamp of the last reading | Y | - | Y | - | - | - | - | - | - | +| | | **Channels for the m3 meter (Gas or Water)** | | | | | | | | | | +| `meter_device_type` | String | m3 Meter Device Type | - | - | - | - | Y | Y | Y | Y | - | +| `meter_equipment_identifier` | String | m3 Meter ID | - | - | - | - | Y | Y | Y | Y | - | +| `m3meter_value` | Number:Volume | m3 Delivery past period | - | - | - | - | Y | Y | Y | Y | - | +| `meter_valve_switch_position` | Number | m3 Meter Valve position | - | - | - | - | Y | Y | Y | - | - | +| | | **Channels for the water meter** | | | | | | | | | | +| `meter_device_type` | String | Water Meter Device Type | - | - | - | Y | - | - | - | - | - | +| `meter_equipment_identifier` | String | Water Meter ID | Y | - | - | Y | - | - | - | - | - | +| `wmeter_equipment_identifier_v2_2` | String | Water Meter ID | - | - | Y | - | - | - | - | - | - | +| `wmeter_value_v2` | Number:Volume | Water Delivery past period | Y | - | Y | - | - | - | - | - | - | +| `wmeter_value_v2_timestamp` | DateTime | Timestamp of the last reading | Y | - | Y | - | - | - | - | - | - | +| `wmeter_value_v3` | Number:Volume | Water Delivery past period | - | - | - | Y | - | - | - | - | - | +| `meter_valve_switch_position` | Number | Water Meter Valve position | - | - | - | Y | - | - | - | - | - | + +*note 2*. The power failure log has a dynamic number of entries starting at `0`. +So `emeter_power_failure_log_timestamp0`, `emeter_power_failure_log_duration0` refers to the first entry, +`emeter_power_failure_log_timestamp1`, `emeter_power_failure_log_duration1` refers to the second entry, etc. + +Channel identifier: `dsmr::::` + +- ThingTypeID. See table with supported meters +- BridgeID. The configured id for the bridge +- id. The configured id for the ThingType you want to address +- channel type id. The channel type id + +The following configuration must to be added to a item configuration file. E.g. `things/dsmr.items` + +``` +ItemType "" () {channel=""} +``` + +**Examples** + +``` +Number:Energy MeterDeliveryTariff0 "Total electricity delivered to the resident during low tariff period [%.3f kWh]" {channel="dsmr:electricity_v5_0:mysmartmeter:electricityV5:emeter_delivery_tariff1} +``` + +## Full configuration example + +`things/dsmr.things` + +``` +Bridge dsmr:dsmrBridge:mysmartmeter [serialPort="/dev/ttyUSB0"] { + Things: + device_v5 dsmrV5Device [channel=-1] + electricity_v5_0 electricityV5 [channel=0] +} +``` + +`things/dsmr.items` + +``` +String P1Version "P1 Version output" {channel="dsmr:device_v5:mysmartmeter:dsmrV5Device:p1_version_output"} +Number:Energy MeterDeliveryTariff0 "Total electricity delivered to the resident during low tariff period [%.3f kWh]" {channel="dsmr:device_v5:mysmartmeter:electricityV5:emeter_delivery_tariff1} +Number:Energy MeterDeliveryTariff1 "Total electricity delivered to the resident during high tariff period [%.3f kWh]" {channel="dsmr:device_v5:mysmartmeter:electricityV5:emeter_delivery_tariff2} +``` + +## Determine M-Bus channel + +By manually trigger the discovery process, e.g. via PaperUI, you can use the logging to find out a M-Bus channel. Look for the following logfile line: +` [INFO ] [] - New compatible meter: [Meter type: M3_V5_0, channel: 1, Meter type: ELECTRICITY_V5, channel: 0]` + +Here you find the ThingTypeID (it is stated only in capitals) and the M-Bus channel. The above example would lead to the following Thing definition + +``` +Bridge definition { + Things: + m3_v5_0 mygasmeter [channel=1] + electricity_v5 [channel=0] +} +``` diff --git a/bundles/org.openhab.binding.dsmr/pom.xml b/bundles/org.openhab.binding.dsmr/pom.xml new file mode 100644 index 0000000000000..5d0d0f6318222 --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.dsmr + + openHAB Add-ons :: Bundles :: DSMR Binding + + diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java new file mode 100644 index 0000000000000..6c1160fdd3f02 --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRBindingConstants.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.dsmr.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * This class defines common constants, which are used across the whole binding. + * + * @author M. Volaart - Initial contribution + * @author Hilbrand Bouwkamp - Removed time constants + */ +@NonNullByDefault +public final class DSMRBindingConstants { + /** + * Binding id. + */ + public static final String BINDING_ID = "dsmr"; + + /** + * Key to use to identify serial port. + */ + public static final String DSMR_PORT_NAME = "org.openhab.binding.dsmr"; + + /** + * Bridge device things + */ + public static final ThingTypeUID THING_TYPE_DSMR_BRIDGE = new ThingTypeUID(BINDING_ID, "dsmrBridge"); + public static final ThingTypeUID THING_TYPE_SMARTY_BRIDGE = new ThingTypeUID(BINDING_ID, "smartyBridge"); + + /** + * Configuration parameter for the serial port. + */ + public static final String CONFIGURATION_SERIAL_PORT = "serialPort"; + + public static final String CONFIGURATION_DECRYPTION_KEY = "decryptionKey"; + public static final String CONFIGURATION_DECRYPTION_KEY_EMPTY = ""; + + private DSMRBindingConstants() { + // Constants class + } +} diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRHandlerFactory.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRHandlerFactory.java similarity index 95% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRHandlerFactory.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRHandlerFactory.java index d33d2dcc74d54..d00799f9b104b 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRHandlerFactory.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/DSMRHandlerFactory.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.dsmr.internal; +import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.*; + import java.util.HashMap; import java.util.Hashtable; import java.util.Map; @@ -68,7 +70,7 @@ public class DSMRHandlerFactory extends BaseThingHandlerFactory { */ @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { - if (thingTypeUID.equals(DSMRBindingConstants.THING_TYPE_DSMR_BRIDGE)) { + if (THING_TYPE_DSMR_BRIDGE.equals(thingTypeUID) || THING_TYPE_SMARTY_BRIDGE.equals(thingTypeUID)) { logger.debug("DSMR Bridge Thing {} supported", thingTypeUID); return true; } else { @@ -97,7 +99,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); logger.debug("Searching for thingTypeUID {}", thingTypeUID); - if (DSMRBindingConstants.THING_TYPE_DSMR_BRIDGE.equals(thingTypeUID)) { + if (THING_TYPE_DSMR_BRIDGE.equals(thingTypeUID) || THING_TYPE_SMARTY_BRIDGE.equals(thingTypeUID)) { DSMRBridgeHandler handler = new DSMRBridgeHandler((Bridge) thing, serialPortManager); registerDiscoveryService(handler); return handler; diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDevice.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDevice.java similarity index 92% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDevice.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDevice.java index 2440b3e831ab6..ed0ee99a02c08 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDevice.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDevice.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.dsmr.internal.device; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Interface for classes controlling DSMR devices. * * @author Hilbrand Bouwkamp - Initial contribution */ +@NonNullByDefault public interface DSMRDevice { /** diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java similarity index 89% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java index 0ef8fe53d915c..034ef1b042841 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceConfiguration.java @@ -46,6 +46,11 @@ public class DSMRDeviceConfiguration { */ public String stopbits; + /** + * The Luxembourgish smart meter decryption key + */ + public String decryptionKey; + /** * When no message was received after the configured number of seconds action will be taken. */ @@ -61,6 +66,7 @@ public boolean isSerialFixedSettings() { @Override public String toString() { return "DSMRDeviceConfiguration [serialPort=" + serialPort + ", Baudrate=" + baudrate + ", Databits=" + databits - + ", Parity=" + parity + ", Stopbits=" + stopbits + ", receivedTimeout=" + receivedTimeout + "]"; + + ", Parity=" + parity + ", Stopbits=" + stopbits + ", decryptionKey=" + decryptionKey + + ", receivedTimeout=" + receivedTimeout + "]"; } } diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java similarity index 97% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java index c67f6702d9a9d..218ee4f46fbc0 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRDeviceRunnable.java @@ -84,7 +84,7 @@ public void run() { } logger.trace("Device shutdown"); } catch (RuntimeException e) { - logger.warn("DSMRDeviceThread stopped with a RuntimeException", e); + logger.warn("DSMRDeviceRunnable stopped with a RuntimeException", e); portEventListener.handleErrorEvent(DSMRConnectorErrorEvent.READ_ERROR); } catch (InterruptedException e) { Thread.currentThread().interrupt(); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMREventListener.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMREventListener.java similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMREventListener.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMREventListener.java diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java similarity index 89% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java index 3651eaf683453..4b1278b218bf8 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRFixedConfigDevice.java @@ -31,17 +31,20 @@ public class DSMRFixedConfigDevice implements DSMRDevice { private final DSMRTelegramListener telegramListener; /** - * Constructor + * Constructor. * * @param serialPortManager the manager to get a new serial port connecting from * @param serialPortName the port name (e.g. /dev/ttyUSB0 or COM1) * @param fixedPortSettings The serial port connection settings * @param listener the parent {@link DSMREventListener} + * @param telegramListener listener to report found telegrams or errors */ public DSMRFixedConfigDevice(SerialPortManager serialPortManager, String serialPortName, - DSMRSerialSettings fixedPortSettings, DSMREventListener listener) { + DSMRSerialSettings fixedPortSettings, DSMREventListener listener, DSMRTelegramListener telegramListener) { this.fixedPortSettings = fixedPortSettings; - telegramListener = new DSMRTelegramListener(listener); + this.telegramListener = telegramListener; + telegramListener.setDsmrEventListener(listener); + dsmrPort = new DSMRSerialConnector(serialPortManager, serialPortName, telegramListener); } diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java similarity index 96% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java index 48c47536080ba..f2b4c47aa8921 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDevice.java @@ -64,7 +64,7 @@ enum DeviceState { * This factor is multiplied with the {@link #baudrateSwitchTimeoutSeconds} and used as the duration the discovery * of the baudrate may take. */ - private static final int DISCOVER_TIMEOUT_FACTOR = 4; + private static final long DISCOVER_TIMEOUT_FACTOR = 4L; /* * Februari 2017 @@ -127,16 +127,19 @@ enum DeviceState { * @param serialPortManager the manager to get a new serial port connecting from * @param serialPortName the port name (e.g. /dev/ttyUSB0 or COM1) * @param listener the parent {@link DSMREventListener} + * @param telegramListener listener to report found telegrams or errors * @param scheduler the scheduler to use with the baudrate switching timers * @param baudrateSwitchTimeoutSeconds timeout period for when to try other baudrate settings and end the discovery * of the baudrate */ public DSMRSerialAutoDevice(SerialPortManager serialPortManager, String serialPortName, DSMREventListener listener, - ScheduledExecutorService scheduler, int baudrateSwitchTimeoutSeconds) { + DSMRTelegramListener telegramListener, ScheduledExecutorService scheduler, + int baudrateSwitchTimeoutSeconds) { this.parentListener = listener; this.scheduler = scheduler; this.baudrateSwitchTimeoutSeconds = baudrateSwitchTimeoutSeconds; - telegramListener = new DSMRTelegramListener(listener); + this.telegramListener = telegramListener; + telegramListener.setDsmrEventListener(listener); dsmrConnector = new DSMRSerialConnector(serialPortManager, serialPortName, telegramListener); logger.debug("Initialized port '{}'", serialPortName); } diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java similarity index 75% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java index a2da26fca8f88..6c54535043d1c 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/DSMRTelegramListener.java @@ -23,6 +23,7 @@ import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramParser; +import org.openhab.binding.dsmr.internal.device.p1telegram.TelegramParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,23 +35,31 @@ * @author Hilbrand Bouwkamp - Moved this code out of the DSMRPort class, fixed some issues and reduced code */ @NonNullByDefault -class DSMRTelegramListener implements P1TelegramListener, DSMRConnectorListener { +public class DSMRTelegramListener implements P1TelegramListener, DSMRConnectorListener { private final Logger logger = LoggerFactory.getLogger(DSMRTelegramListener.class); - private final P1TelegramParser parser; + private final TelegramParser parser; - private DSMREventListener dsmrEventListener; + private @NonNullByDefault({}) DSMREventListener dsmrEventListener; /** * Constructor. * * @param eventListener listener to send received errors or messages to */ - public DSMRTelegramListener(DSMREventListener eventListener) { - dsmrEventListener = eventListener; + public DSMRTelegramListener() { parser = new P1TelegramParser(this); } + /** + * Constructs {@link DSMRTelegramListener} with a Smarty decryptor to first decrypt incoming messages. + * + * @param decryptionKey Smarty decryption key + */ + public DSMRTelegramListener(String decryptionKey) { + parser = new SmartyDecrypter(new P1TelegramParser(this), this, decryptionKey); + } + /** * Set the DSMR event listener. * @@ -62,7 +71,7 @@ public void setDsmrEventListener(DSMREventListener eventListener) { @Override public void handleData(byte[] data, int length) { - parser.parseData(data, 0, length); + parser.parse(data, length); } @Override @@ -74,18 +83,17 @@ public void handleErrorEvent(DSMRConnectorErrorEvent portEvent) { /** * Handler for cosemObjects received in a P1 telegram * - * @param cosemObjects. List of received {@link CosemObject} objects - * @param telegramState. {@link TelegramState} describing the state of the received telegram. + * @param telegram the received telegram. */ @Override public void telegramReceived(P1Telegram telegram) { - TelegramState telegramState = telegram.getTelegramState(); - List cosemObjects = telegram.getCosemObjects(); + final TelegramState telegramState = telegram.getTelegramState(); + final List cosemObjects = telegram.getCosemObjects(); + if (logger.isTraceEnabled()) { logger.trace("Received {} Cosem Objects with state: '{}'", cosemObjects.size(), telegramState); } - - if (telegramState == TelegramState.OK) { + if (telegramState == TelegramState.OK || telegramState == TelegramState.INVALID_ENCRYPTION_KEY) { dsmrEventListener.handleTelegramReceived(telegram); } else { if (logger.isDebugEnabled()) { diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypter.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypter.java new file mode 100644 index 0000000000000..f161421f09581 --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypter.java @@ -0,0 +1,232 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.dsmr.internal.device; + +import java.nio.ByteBuffer; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.util.HexUtils; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; +import org.openhab.binding.dsmr.internal.device.p1telegram.TelegramParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Decodes messages send by The Luxembourgian Smart Meter "Smarty". + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public class SmartyDecrypter implements TelegramParser { + + private enum State { + WAITING_FOR_START_BYTE, + READ_SYSTEM_TITLE_LENGTH, + READ_SYSTEM_TITLE, + READ_SEPARATOR_82, + READ_PAYLOAD_LENGTH, + READ_SEPARATOR_30, + READ_FRAME_COUNTER, + READ_PAYLOAD, + READ_GCM_TAG, + DONE_READING_TELEGRAM; + } + + private static final byte START_BYTE = (byte) 0xDB; + private static final byte SEPARATOR_82 = (byte) 0x82; + private static final byte SEPARATOR_30 = 0x30; + private static final int ADD_LENGTH = 17; + private static final String ADD = "3000112233445566778899AABBCCDDEEFF"; + private static final byte[] ADD_DECODED = HexUtils.hexToBytes(ADD); + private static final int GCM_TAG_LENGTH = 12; + private static final int GCM_BITS = GCM_TAG_LENGTH * Byte.SIZE; + private static final int MESSAGES_BUFFER_SIZE = 4096; + + private final Logger logger = LoggerFactory.getLogger(SmartyDecrypter.class); + private final ByteBuffer iv = ByteBuffer.allocate(GCM_TAG_LENGTH); + private final ByteBuffer cipherText = ByteBuffer.allocate(MESSAGES_BUFFER_SIZE); + private final TelegramParser parser; + private @Nullable final SecretKeySpec secretKeySpec; + + private State state = State.WAITING_FOR_START_BYTE; + private int currentBytePosition; + private int changeToNextStateAt; + private int dataLength; + private boolean lenientMode; + private P1TelegramListener telegramListener; + + /** + * Constructor. + * + * @param parser parser of the Cosem messages + * @param telegramListener + * @param decryptionKey The key to decrypt the messages + */ + public SmartyDecrypter(TelegramParser parser, P1TelegramListener telegramListener, String decryptionKey) { + this.parser = parser; + this.telegramListener = telegramListener; + secretKeySpec = decryptionKey.isEmpty() ? null : new SecretKeySpec(HexUtils.hexToBytes(decryptionKey), "AES"); + } + + @Override + public void parse(byte[] data, int length) { + for (int i = 0; i < length; i++) { + currentBytePosition++; + if (processStateActions(data[i])) { + processCompleted(); + } + } + if (lenientMode && secretKeySpec == null) { + parser.parse(data, length); + } + } + + private boolean processStateActions(byte rawInput) { + switch (state) { + case WAITING_FOR_START_BYTE: + if (rawInput == START_BYTE) { + reset(); + state = State.READ_SYSTEM_TITLE_LENGTH; + } + break; + case READ_SYSTEM_TITLE_LENGTH: + state = State.READ_SYSTEM_TITLE; + // 2 start bytes (position 0 and 1) + system title length + changeToNextStateAt = 1 + rawInput; + break; + case READ_SYSTEM_TITLE: + iv.put(rawInput); + if (currentBytePosition >= changeToNextStateAt) { + state = State.READ_SEPARATOR_82; + changeToNextStateAt++; + } + break; + case READ_SEPARATOR_82: + if (rawInput == SEPARATOR_82) { + state = State.READ_PAYLOAD_LENGTH; // Ignore separator byte + changeToNextStateAt += 2; + } else { + logger.debug("Missing separator (0x82). Dropping telegram."); + state = State.WAITING_FOR_START_BYTE; + } + break; + case READ_PAYLOAD_LENGTH: + dataLength <<= 8; + dataLength |= rawInput; + if (currentBytePosition >= changeToNextStateAt) { + state = State.READ_SEPARATOR_30; + changeToNextStateAt++; + } + break; + case READ_SEPARATOR_30: + if (rawInput == SEPARATOR_30) { + state = State.READ_FRAME_COUNTER; + // 4 bytes for frame counter + changeToNextStateAt += 4; + } else { + logger.debug("Missing separator (0x30). Dropping telegram."); + state = State.WAITING_FOR_START_BYTE; + } + break; + case READ_FRAME_COUNTER: + iv.put(rawInput); + if (currentBytePosition >= changeToNextStateAt) { + state = State.READ_PAYLOAD; + changeToNextStateAt += dataLength - ADD_LENGTH; + } + break; + case READ_PAYLOAD: + cipherText.put(rawInput); + if (currentBytePosition >= changeToNextStateAt) { + state = State.READ_GCM_TAG; + changeToNextStateAt += GCM_TAG_LENGTH; + } + break; + case READ_GCM_TAG: + // All input has been read. + cipherText.put(rawInput); + if (currentBytePosition >= changeToNextStateAt) { + state = State.DONE_READING_TELEGRAM; + } + break; + } + if (state == State.DONE_READING_TELEGRAM) { + state = State.WAITING_FOR_START_BYTE; + return true; + } + return false; + } + + private void processCompleted() { + byte[] plainText = decrypt(); + + reset(); + if (plainText == null) { + telegramListener + .telegramReceived(new P1Telegram(Collections.emptyList(), TelegramState.INVALID_ENCRYPTION_KEY)); + } else { + parser.parse(plainText, plainText.length); + } + } + + /** + * Decrypts the collected message. + * + * @return the decrypted message + */ + private byte @Nullable [] decrypt() { + try { + if (secretKeySpec != null) { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new GCMParameterSpec(GCM_BITS, iv.array())); + cipher.updateAAD(ADD_DECODED); + return cipher.doFinal(cipherText.array(), 0, cipherText.position()); + } + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + logger.warn("Decrypting smarty telegram failed: ", e); + } + return null; + } + + @Override + public void reset() { + parser.reset(); + state = State.WAITING_FOR_START_BYTE; + iv.clear(); + cipherText.clear(); + currentBytePosition = 0; + changeToNextStateAt = 0; + dataLength = 0; + } + + @Override + public void setLenientMode(boolean lenientMode) { + this.lenientMode = lenientMode; + parser.setLenientMode(lenientMode); + } +} diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRBaseConnector.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRBaseConnector.java similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRBaseConnector.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRBaseConnector.java diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorErrorEvent.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorErrorEvent.java similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorErrorEvent.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorErrorEvent.java diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorListener.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorListener.java similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorListener.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRConnectorListener.java diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java similarity index 98% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java index 582db9d9b9fd4..ebd271351ae8f 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialConnector.java @@ -233,7 +233,7 @@ public void setSerialPortParams(DSMRSerialSettings portSettings) { } } catch (UnsupportedCommOperationException e) { logger.debug( - "Port does not support requested port settings (invalid dsmr:portsettings parameter?): {}", + "Port does {} not support requested port settings (invalid dsmr:portsettings parameter?): {}", serialPortName, portSettings); dsmrConnectorListener.handleErrorEvent(DSMRConnectorErrorEvent.NOT_COMPATIBLE); } diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialSettings.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialSettings.java similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialSettings.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/connector/DSMRSerialSettings.java diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDate.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDate.java similarity index 98% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDate.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDate.java index 94821fb139c52..54141856ca251 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDate.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDate.java @@ -20,6 +20,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.DateTimeType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,6 +31,7 @@ * @author M. Volaart - Initial contribution * @author Hilbrand Bouwkamp - Class now a factory instead of data containing class */ +@NonNullByDefault class CosemDate extends CosemValueDescriptor { public static final CosemDate INSTANCE = new CosemDate("timestamp"); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDecimal.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDecimal.java similarity index 95% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDecimal.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDecimal.java index 49b42e135d376..a09f093ac8af7 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDecimal.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemDecimal.java @@ -14,6 +14,7 @@ import java.text.ParseException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.DecimalType; /** @@ -22,6 +23,7 @@ * @author M. Volaart - Initial contribution * @author Hilbrand Bouwkamp - Combined Integer and Double because {@link DecimalType} handles both */ +@NonNullByDefault class CosemDecimal extends CosemValueDescriptor { public static final CosemDecimal INSTANCE = new CosemDecimal(); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemHexString.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemHexString.java similarity index 96% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemHexString.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemHexString.java index 7f447af52b89c..9587570f6a9c0 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemHexString.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemHexString.java @@ -14,6 +14,7 @@ import java.text.ParseException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.StringType; /** @@ -22,6 +23,7 @@ * @author M. Volaart - Initial contribution * @author Hilbrand Bouwkamp - Class now a factory instead of data containing class */ +@NonNullByDefault class CosemHexString extends CosemValueDescriptor { public static final CosemHexString INSTANCE = new CosemHexString(); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObject.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObject.java similarity index 98% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObject.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObject.java index 693fd244eef35..c25caa3857b37 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObject.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObject.java @@ -19,6 +19,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.types.State; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +29,7 @@ * * @author M. Volaart - Initial contribution */ +@NonNullByDefault public class CosemObject { /** diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectFactory.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectFactory.java similarity index 93% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectFactory.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectFactory.java index ff1977efc49df..f2f665992f167 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectFactory.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectFactory.java @@ -18,6 +18,8 @@ import java.util.List; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,6 +28,7 @@ * * @author M. Volaart - Initial contribution */ +@NonNullByDefault public class CosemObjectFactory { private final Logger logger = LoggerFactory.getLogger(CosemObjectFactory.class); @@ -84,7 +87,7 @@ public CosemObjectFactory() { * @param cosemStringValues String containing Cosem values * @return CosemObject or null if parsing failed */ - public CosemObject getCosemObject(String obisIdString, String cosemStringValues) { + public @Nullable CosemObject getCosemObject(String obisIdString, String cosemStringValues) { OBISIdentifier obisId; OBISIdentifier reducedObisId; @@ -112,7 +115,7 @@ public CosemObject getCosemObject(String obisIdString, String cosemStringValues) cosemObject = getCosemObjectInternal(obisMsgType, obisId, cosemStringValues); if (cosemObject != null) { logger.trace("Searched reducedObisId {} in the wild card type list, result: {}", reducedObisId, - cosemObject); + cosemObject); obisLookupTableDynamic.put(reducedObisId, obisMsgType); break; } @@ -136,8 +139,8 @@ public CosemObject getCosemObject(String obisIdString, String cosemStringValues) * * @return a CosemObject or null if parsing failed */ - private CosemObject getCosemObjectInternal(CosemObjectType cosemObjectType, OBISIdentifier obisIdentifier, - String cosemStringValues) { + private @Nullable CosemObject getCosemObjectInternal(CosemObjectType cosemObjectType, OBISIdentifier obisIdentifier, + String cosemStringValues) { CosemObject obj = new CosemObject(cosemObjectType, obisIdentifier); try { @@ -147,7 +150,7 @@ private CosemObject getCosemObjectInternal(CosemObjectType cosemObjectType, OBIS return obj; } catch (ParseException pe) { logger.trace("Failed to construct Cosem Object for type {}, values: {}", cosemObjectType, cosemStringValues, - pe); + pe); } return null; } diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectType.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectType.java similarity index 90% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectType.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectType.java index df03d4b676817..0206af77dae51 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectType.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemObjectType.java @@ -20,7 +20,6 @@ import javax.measure.quantity.Time; -import org.eclipse.smarthome.core.library.types.QuantityType; import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; import org.openhab.binding.dsmr.internal.meter.DSMRMeterConstants; @@ -37,7 +36,7 @@ * * * @author M. Volaart - Initial contribution - * @author Hilbrand Bouwkamp - Cosem subclasses made into factory classes and introduced {@link QuantityType} + * @author Hilbrand Bouwkamp - Cosem subclasses made into factory classes and introduced quantity type */ public enum CosemObjectType { UNKNOWN(new OBISIdentifier(-1, DSMRMeterConstants.UNKNOWN_CHANNEL, -1, -1, -1, null), CosemString.INSTANCE), @@ -136,7 +135,21 @@ public enum CosemObjectType { GJMETER_VALUE_V4(new OBISIdentifier(0, null, 24, 2, 1, null), CosemDate.INSTANCE, CosemQuantity.GIGA_JOULE), /* Generic Meter (DSMR v3 only) */ - GENMETER_VALUE_V3(new OBISIdentifier(0, null, 24, 3, 0, null), CosemDecimal.INSTANCE); + GENMETER_VALUE_V3(new OBISIdentifier(0, null, 24, 3, 0, null), CosemDecimal.INSTANCE), + + /* Additional Luxembourgish Smarty Electricity */ + EMETER_TOTAL_IMPORTED_ENERGY_REGISTER_Q(new OBISIdentifier(1, null, 3, 8, 0, null), CosemQuantity.KILO_VAR_HOUR), + EMETER_TOTAL_EXPORTED_ENERGY_REGISTER_Q(new OBISIdentifier(1, null, 4, 8, 0, null), CosemQuantity.KILO_VAR_HOUR), + // The actual reactive's and threshold have no unit in the data and therefore are not quantity types. + EMETER_ACTUAL_REACTIVE_DELIVERY(new OBISIdentifier(1, 0, 3, 7, 0, null), CosemDecimal.INSTANCE), + EMETER_ACTUAL_REACTIVE_PRODUCTION(new OBISIdentifier(1, 0, 4, 7, 0, null), CosemDecimal.INSTANCE), + EMETER_ACTIVE_THRESHOLD_SMAX(new OBISIdentifier(0, 0, 17, 0, 0, null), CosemDecimal.INSTANCE), + EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L1(new OBISIdentifier(1, 0, 23, 7, 0, null), CosemQuantity.KILO_VAR), + EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L2(new OBISIdentifier(1, 0, 43, 7, 0, null), CosemQuantity.KILO_VAR), + EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L3(new OBISIdentifier(1, 0, 63, 7, 0, null), CosemQuantity.KILO_VAR), + EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L1(new OBISIdentifier(1, 0, 24, 7, 0, null), CosemQuantity.KILO_VAR), + EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L2(new OBISIdentifier(1, 0, 44, 7, 0, null), CosemQuantity.KILO_VAR), + EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L3(new OBISIdentifier(1, 0, 64, 7, 0, null), CosemQuantity.KILO_VAR); /** OBIS reduced identifier */ public final OBISIdentifier obisId; diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemQuantity.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemQuantity.java similarity index 91% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemQuantity.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemQuantity.java index 2ff9562aa1f9e..765c8b708cc1e 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemQuantity.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemQuantity.java @@ -24,6 +24,8 @@ import javax.measure.quantity.Power; import javax.measure.quantity.Volume; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.QuantityType; import org.eclipse.smarthome.core.library.unit.MetricPrefix; import org.eclipse.smarthome.core.library.unit.SIUnits; @@ -36,7 +38,8 @@ * * @param The {@link Quantity} type of the unit of this class */ -class CosemQuantity> extends CosemValueDescriptor> { +@NonNullByDefault +class CosemQuantity> extends CosemValueDescriptor> { public static final CosemQuantity AMPERE = new CosemQuantity<>(SmartHomeUnits.AMPERE); public static final CosemQuantity CUBIC_METRE = new CosemQuantity<>(SIUnits.CUBIC_METRE); @@ -45,6 +48,8 @@ class CosemQuantity> extends CosemValueDescriptor KILO_WATT_HOUR = new CosemQuantity<>(SmartHomeUnits.KILOWATT_HOUR); public static final CosemQuantity VOLT = new CosemQuantity<>(SmartHomeUnits.VOLT); public static final CosemQuantity WATT = new CosemQuantity<>(SmartHomeUnits.WATT); + public static final CosemQuantity KILO_VAR = new CosemQuantity<>(SmartHomeUnits.KILOVAR); + public static final CosemQuantity KILO_VAR_HOUR = new CosemQuantity<>(SmartHomeUnits.KILOVAR_HOUR); /** * Pattern to convert a cosem value to a value that can be parsed by {@link QuantityType}. diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemString.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemString.java similarity index 94% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemString.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemString.java index 53bcfe713f35f..424e8a82369fb 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemString.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemString.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.dsmr.internal.device.cosem; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.StringType; /** @@ -20,6 +21,7 @@ * @author M. Volaart - Initial contribution * @author Hilbrand Bouwkamp - Class now a factory instead of data containing class */ +@NonNullByDefault class CosemString extends CosemValueDescriptor { public static final CosemString INSTANCE = new CosemString(); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemValueDescriptor.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemValueDescriptor.java similarity index 94% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemValueDescriptor.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemValueDescriptor.java index a567b4f997a73..45af03244ddea 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemValueDescriptor.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/CosemValueDescriptor.java @@ -14,6 +14,7 @@ import java.text.ParseException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.types.State; /** @@ -21,6 +22,7 @@ * * @author M. Volaart - Initial contribution */ +@NonNullByDefault abstract class CosemValueDescriptor { /** @@ -30,8 +32,6 @@ abstract class CosemValueDescriptor { /** * Creates a new {@link CosemValueDescriptor} with no unit and a default channel. - * - * @param cosemValueClass the {@link CosemValue} class that the {@link CosemValueDescriptor} represent */ public CosemValueDescriptor() { this(""); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java similarity index 81% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java index 0d84fd8b4517e..0da24c981168f 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/cosem/OBISIdentifier.java @@ -17,12 +17,16 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + /** * Class representing an OBISIdentifier * * @author M. Volaart - Initial contribution * @author Hilbrand Bouwkamp - Fix bug in regex pattern. */ +@NonNullByDefault public class OBISIdentifier { /** * String representing a-b:c.d.e.f OBIS ID @@ -36,11 +40,11 @@ public class OBISIdentifier { /* the six individual group values of the OBIS ID */ private int groupA; - private Integer groupB; + private @Nullable Integer groupB; private int groupC; private int groupD; - private int groupE; - private Integer groupF; + private @Nullable Integer groupE; + private @Nullable Integer groupF; /** * Constructs a new OBIS Identifier (A-B:C.D.E.F) @@ -52,7 +56,8 @@ public class OBISIdentifier { * @param groupE E value * @param groupF F value */ - public OBISIdentifier(int groupA, Integer groupB, int groupC, int groupD, int groupE, Integer groupF) { + public OBISIdentifier(int groupA, @Nullable Integer groupB, int groupC, int groupD, @Nullable Integer groupE, + @Nullable Integer groupF) { this.groupA = groupA; this.groupB = groupB; this.groupC = groupC; @@ -78,7 +83,7 @@ public OBISIdentifier(String obisIDString) throws ParseException { // Optional value B if (m.group(4) != null) { - this.groupB = Integer.parseInt(m.group(4)); + this.groupB = Integer.valueOf(m.group(4)); } // Required value C & D @@ -87,12 +92,12 @@ public OBISIdentifier(String obisIDString) throws ParseException { // Optional value E if (m.group(9) != null) { - this.groupE = Integer.parseInt(m.group(9)); + this.groupE = Integer.valueOf(m.group(9)); } // Optional value F if (m.group(11) != null) { - this.groupF = Integer.parseInt(m.group(11)); + this.groupF = Integer.valueOf(m.group(11)); } } else { throw new ParseException("Invalid OBIS identifier:" + obisIDString, 0); @@ -109,7 +114,7 @@ public int getGroupA() { /** * @return the groupB */ - public Integer getGroupB() { + public @Nullable Integer getGroupB() { return groupB; } @@ -130,21 +135,21 @@ public int getGroupD() { /** * @return the groupE */ - public int getGroupE() { + public @Nullable Integer getGroupE() { return groupE; } /** * @return the groupF */ - public Integer getGroupF() { + public @Nullable Integer getGroupF() { return groupF; } @Override public String toString() { - return groupA + "-" + (groupB != null ? (groupB + ":") : "") + groupC + "." + groupD + "." + groupE - + (groupF != null ? ("*" + groupF) : ""); + return groupA + "-" + (groupB == null ? "" : (groupB + ":")) + groupC + "." + groupD + + (groupE == null ? "" : ("." + groupE)) + (groupF == null ? "" : ("*" + groupF)); } /** @@ -156,7 +161,7 @@ public String toString() { * @return true if both OBISIdentifiers match, false otherwise */ @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { OBISIdentifier o; if (other != null && other instanceof OBISIdentifier) { o = (OBISIdentifier) other; @@ -184,7 +189,7 @@ public boolean equals(Object other) { } /** - * Checks whether this OBIS Identifer and the other identifer equals taking the wildcards into account + * Checks whether this OBIS Identifier and the other identifier equals taking the wildcards into account * * @param o OBISIdentifier to compare to * @@ -199,7 +204,9 @@ public boolean equalsWildCard(OBISIdentifier o) { } result &= groupC == o.groupC; result &= groupD == o.groupD; - result &= groupE == o.groupE; + if (groupE != null && o.groupE != null) { + result &= (groupE.equals(o.groupE)); + } if (groupF != null && o.groupF != null) { result &= (groupF.equals(o.groupF)); } @@ -209,15 +216,15 @@ public boolean equalsWildCard(OBISIdentifier o) { @Override public int hashCode() { - return Objects.hash(groupA, ((groupB == null) ? 0 : groupB), groupC, groupD, groupE, - ((groupF == null) ? 0 : groupF)); + return Objects.hash(groupA, (groupB != null ? groupB : 0), groupC, groupD, (groupE != null ? groupE : 0), + (groupF != null ? groupF : 0)); } /** * Returns an reduced OBIS Identifier. This means group F is set to null * (.i.e. not applicable) * - * @return reduced OBIS Identifer + * @return reduced OBIS Identifier */ public OBISIdentifier getReducedOBISIdentifier() { return new OBISIdentifier(groupA, groupB, groupC, groupD, groupE, null); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/CRC16.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/CRC16.java similarity index 98% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/CRC16.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/CRC16.java index 0def3d29981a6..d7bc51fac9a78 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/CRC16.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/CRC16.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.dsmr.internal.device.p1telegram; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * CRC16 implementation. * @@ -22,7 +24,9 @@ * * @author M. Volaart - Initial contribution */ +@NonNullByDefault public class CRC16 { + public enum Polynom { CRC16_IBM(0xA001), // standard CRC-16 x16+x15+x2+1 (CRC-16-IBM) CRC16_IBM_REVERSE(0xC002), // standard reverse x16+x14+x+1 (CRC-16-IBM) diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java new file mode 100644 index 0000000000000..d350eaa9deabb --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1Telegram.java @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.dsmr.internal.device.p1telegram; + +import java.util.Collections; +import java.util.List; +import java.util.Map.Entry; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.dsmr.internal.device.cosem.CosemObject; + +/** + * Data class containing a Telegram with CosemObjects and TelegramState and if in lenient mode also the raw telegram + * data. + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public class P1Telegram { + + /** + * The TelegramState described the meta data of the P1Telegram + */ + public enum TelegramState { + /** + * OK. Telegram was successful received and CRC16 checksum is verified (CRC16 only for DSMR V4 and up) + */ + OK("P1 telegram received OK"), + /** + * CRC_ERROR. CRC16 checksum failed (only DSMR V4 and up) + */ + CRC_ERROR("CRC checksum failed for received P1 telegram"), + /** + * DATA_CORRUPTION. The P1 telegram has syntax errors. + */ + DATA_CORRUPTION("Received P1 telegram is corrupted"), + /** + * P1TelegramListener. The smarty telegram was successful received but could not be decoded because of an invalid + * encryption key. + */ + INVALID_ENCRYPTION_KEY("Failed to decrypt P1 telegram due to invalid encryption key"); + + /** + * public accessible state details + */ + public final String stateDetails; + + /** + * Constructs a new TelegramState enum + * + * @param stateDetails String containing the details of this TelegramState + */ + private TelegramState(String stateDetails) { + this.stateDetails = stateDetails; + } + } + + private final List cosemObjects; + private final TelegramState telegramState; + private final String rawTelegram; + private final List> unknownCosemObjects; + + public P1Telegram(List cosemObjects, TelegramState telegramState) { + this(cosemObjects, telegramState, "", Collections.emptyList()); + } + + public P1Telegram(List cosemObjects, TelegramState telegramState, String rawTelegram, + List> unknownCosemObjects) { + this.cosemObjects = cosemObjects; + this.telegramState = telegramState; + this.rawTelegram = rawTelegram; + this.unknownCosemObjects = unknownCosemObjects; + } + + /** + * @return The list of CosemObjects + */ + public List getCosemObjects() { + return cosemObjects; + } + + /** + * @return The raw telegram data. + */ + public String getRawTelegram() { + return rawTelegram; + } + + /** + * @return The state of the telegram + */ + public TelegramState getTelegramState() { + return telegramState; + } + + /** + * @return The list of CosemObject found in the telegram but not known to the binding + */ + public List> getUnknownCosemObjects() { + return unknownCosemObjects; + } +} diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramListener.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramListener.java similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramListener.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramListener.java diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java similarity index 85% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java index ef266ef6c5cc8..3ea47f207c3d7 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParser.java @@ -13,8 +13,10 @@ package org.openhab.binding.dsmr.internal.device.p1telegram; import java.nio.charset.StandardCharsets; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.List; +import java.util.Map.Entry; import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -34,7 +36,7 @@ * @author Hilbrand Bouwkamp - Removed asynchronous call and some clean up */ @NonNullByDefault -public class P1TelegramParser { +public class P1TelegramParser implements TelegramParser { /** * State of the parser @@ -56,13 +58,13 @@ private enum State { CRC_VALUE } - private final Logger logger = LoggerFactory.getLogger(P1TelegramParser.class); - /** * Pattern for the CRC-code */ private static final String CRC_PATTERN = "[0-9A-Z]{4}"; + private final Logger logger = LoggerFactory.getLogger(P1TelegramParser.class); + /* internal state variables */ /** @@ -73,7 +75,7 @@ private enum State { /** * Current cosem object values buffer. */ - private final StringBuilder cosemObjectValuesString = new StringBuilder(); + private final StringBuilder obisValue = new StringBuilder(); /** * In lenient mode store raw data and log when a complete message is received. @@ -88,7 +90,7 @@ private enum State { /** * CRC calculation helper */ - private CRC16 crc; + private final CRC16 crc; /** * Current state of the P1 telegram parser @@ -108,17 +110,22 @@ private enum State { /** * CosemObjectFactory helper class */ - private CosemObjectFactory factory; + private final CosemObjectFactory factory; /** * Received Cosem Objects in the P1Telegram that is currently received */ - private List cosemObjects = new ArrayList<>(); + private final List cosemObjects = new ArrayList<>(); + + /** + * List of Cosem Object values that are not known to this binding. + */ + private final List> unknownCosemObjects = new ArrayList<>(); /** * Listener for new P1 telegrams */ - private P1TelegramListener telegramListener; + private final P1TelegramListener telegramListener; /** * Creates a new P1TelegramParser @@ -135,17 +142,15 @@ public P1TelegramParser(P1TelegramListener telegramListener) { } /** - * Parses data. If parsing is not ready yet nothing will be returned. If - * parsing fails completely nothing will be returned. If parsing succeeds - * (partial) the received OBIS messages will be returned. + * Parses data. If a complete message is received the message will be passed to the telegramListener. * - * @param data byte data - * @param offset offset tot start in the data buffer + * @param data byte data to parse * @param length number of bytes to parse */ - public void parseData(byte[] data, int offset, int length) { + @Override + public void parse(byte[] data, int length) { if (lenientMode || logger.isTraceEnabled()) { - String rawBlock = new String(data, offset, length, StandardCharsets.UTF_8); + String rawBlock = new String(data, 0, length, StandardCharsets.UTF_8); if (lenientMode) { rawData.append(rawBlock); @@ -154,7 +159,7 @@ public void parseData(byte[] data, int offset, int length) { logger.trace("Raw data: {}, Parser state entering parseData: {}", rawBlock, state); } } - for (int i = offset; i < (offset + length); i++) { + for (int i = 0; i < length; i++) { char c = (char) data[i]; switch (state) { @@ -245,7 +250,7 @@ public void parseData(byte[] data, int offset, int length) { if (logger.isDebugEnabled()) { logger.trace("received CRC value: {}, calculated CRC value: 0x{}", crcValue, - String.format("%04X", calculatedCRC)); + String.format("%04X", calculatedCRC)); } if (crcP1Telegram != calculatedCRC) { logger.trace("CRC value does not match, p1 Telegram failed"); @@ -256,9 +261,8 @@ public void parseData(byte[] data, int offset, int length) { telegramState = TelegramState.CRC_ERROR; } } - telegramListener.telegramReceived( - new P1Telegram(new ArrayList<>(cosemObjects), telegramState, rawData.toString())); - setState(State.WAIT_FOR_START); + telegramListener.telegramReceived(constructTelegram()); + reset(); if (c == '/') { /* * Immediately proceed to the next state (robust implementation for meter that do not follow @@ -275,9 +279,17 @@ public void parseData(byte[] data, int offset, int length) { logger.trace("State after parsing: {}", state); } - /** - * Reset the current telegram state - */ + private P1Telegram constructTelegram() { + final List cosemObjectsCopy = new ArrayList<>(cosemObjects); + + if (lenientMode) { + return new P1Telegram(cosemObjectsCopy, telegramState, rawData.toString(), unknownCosemObjects); + } else { + return new P1Telegram(cosemObjectsCopy, telegramState); + } + } + + @Override public void reset() { setState(State.WAIT_FOR_START); } @@ -314,11 +326,11 @@ private void handleCharacter(char c) { crc.processByte((byte) c); break; case DATA_OBIS_VALUE: - cosemObjectValuesString.append(c); + obisValue.append(c); crc.processByte((byte) c); break; case DATA_OBIS_VALUE_END: - cosemObjectValuesString.append(c); + obisValue.append(c); crc.processByte((byte) c); break; case CRC_VALUE: @@ -339,22 +351,22 @@ private void handleCharacter(char c) { */ private void clearInternalData() { obisId.setLength(0); - cosemObjectValuesString.setLength(0); + obisValue.setLength(0); rawData.setLength(0); crcValue.setLength(0); crc.initialize(); cosemObjects.clear(); + unknownCosemObjects.clear(); } /** * Clears all the current OBIS data. I.e. * - current OBIS identifier * - current OBIS value - * - current OBIS data object */ private void clearObisData() { obisId.setLength(0); - cosemObjectValuesString.setLength(0); + obisValue.setLength(0); } /** @@ -364,17 +376,23 @@ private void storeCurrentCosemObject() { String obisIdString = obisId.toString(); if (!obisIdString.isEmpty()) { - CosemObject cosemObject = factory.getCosemObject(obisIdString, cosemObjectValuesString.toString()); + final String obisValueString = obisValue.toString(); + CosemObject cosemObject = factory.getCosemObject(obisIdString, obisValueString); - if (cosemObject != null) { + if (cosemObject == null) { + if (lenientMode) { + unknownCosemObjects.add(new SimpleEntry(obisIdString, obisValueString)); + } + } else { logger.trace("Adding {} to list of Cosem Objects", cosemObject); cosemObjects.add(cosemObject); } } + clearObisData(); } /** - * @param state the new state to set + * @param newState the new state to set */ private void setState(State newState) { synchronized (state) { @@ -392,12 +410,10 @@ private void setState(State newState) { // If the current state is CRLF we are processing the header and don't have a cosem object yet if (state != State.CRLF) { storeCurrentCosemObject(); - clearObisData(); } break; case CRC_VALUE: storeCurrentCosemObject(); - clearObisData(); break; default: break; @@ -406,9 +422,7 @@ private void setState(State newState) { } } - /** - * @param lenientMode the lenientMode to set - */ + @Override public void setLenientMode(boolean lenientMode) { this.lenientMode = lenientMode; } diff --git a/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/TelegramParser.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/TelegramParser.java new file mode 100644 index 0000000000000..c9671c9e3f38b --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/device/p1telegram/TelegramParser.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.dsmr.internal.device.p1telegram; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public interface TelegramParser { + + /** + * + * @param data byte data to parse + * @param length number of bytes to parse + */ + void parse(byte[] data, int length); + + /** + * Reset the current telegram state. + */ + default void reset() { + }; + + /** + * @param lenientMode the lenientMode to set + */ + default void setLenientMode(boolean lenientMode) { + }; +} diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java similarity index 79% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java index b243cf3067f4c..5a8eb9f81b515 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRBridgeDiscoveryService.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.dsmr.internal.discovery; +import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.*; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -24,16 +26,18 @@ import org.eclipse.smarthome.config.discovery.DiscoveryService; import org.eclipse.smarthome.core.i18n.LocaleProvider; import org.eclipse.smarthome.core.i18n.TranslationProvider; +import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.io.transport.serial.SerialPortIdentifier; import org.eclipse.smarthome.io.transport.serial.SerialPortManager; -import org.openhab.binding.dsmr.internal.DSMRBindingConstants; import org.openhab.binding.dsmr.internal.device.DSMRDeviceRunnable; import org.openhab.binding.dsmr.internal.device.DSMREventListener; import org.openhab.binding.dsmr.internal.device.DSMRSerialAutoDevice; +import org.openhab.binding.dsmr.internal.device.DSMRTelegramListener; import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorErrorEvent; import org.openhab.binding.dsmr.internal.device.cosem.CosemObject; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram.TelegramState; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; @@ -105,15 +109,18 @@ protected void startScan() { if (portIdentifier.isCurrentlyOwned()) { logger.trace("Possible port to check:{}, owned:{} by:{}", currentScannedPortName, portIdentifier.isCurrentlyOwned(), portIdentifier.getCurrentOwner()); - if (DSMRBindingConstants.DSMR_PORT_NAME.equals(portIdentifier.getCurrentOwner())) { + if (DSMR_PORT_NAME.equals(portIdentifier.getCurrentOwner())) { logger.debug("The port {} is owned by this binding. If no DSMR meters will be found it " + "might indicate the port is locked by an older instance of this binding. " + "Restart the system to unlock the port.", currentScannedPortName); } } else { logger.debug("Start discovery on serial port: {}", currentScannedPortName); - DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, portIdentifier.getName(), - this, scheduler, BAUDRATE_SWITCH_TIMEOUT_SECONDS); + // + final DSMRTelegramListener telegramListener = new DSMRTelegramListener(""); + final DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, + portIdentifier.getName(), this, telegramListener, scheduler, + BAUDRATE_SWITCH_TIMEOUT_SECONDS); device.setLenientMode(true); currentScannedDevice = new DSMRDeviceRunnable(device, this); currentScannedDevice.run(); @@ -145,7 +152,7 @@ private void stopSerialPortScan() { /** * Handle if telegrams are received. * - * If there are cosem objects received a new bridge will we discovered + * If there are cosem objects received a new bridge will we discovered. * * @param telegram the received telegram */ @@ -156,35 +163,39 @@ public void handleTelegramReceived(P1Telegram telegram) { if (logger.isDebugEnabled()) { logger.debug("[{}] Received {} cosemObjects", currentScannedPortName, cosemObjects.size()); } - if (!cosemObjects.isEmpty()) { - bridgeDiscovered(telegram); + if (telegram.getTelegramState() == TelegramState.INVALID_ENCRYPTION_KEY) { + bridgeDiscovered(THING_TYPE_SMARTY_BRIDGE); + stopSerialPortScan(); + } else if (!cosemObjects.isEmpty()) { + ThingUID bridgeThingUID = bridgeDiscovered(THING_TYPE_DSMR_BRIDGE); + meterDetector.detectMeters(telegram).getKey().forEach(m -> meterDiscovered(m, bridgeThingUID)); stopSerialPortScan(); } } /** + * Creates a bridge. * - * Therefore this method will always return true - * - * @return true if bridge is accepted, false otherwise + * @return The {@link ThingUID} of the newly created bridge */ - private boolean bridgeDiscovered(P1Telegram telegram) { - ThingUID thingUID = new ThingUID(DSMRBindingConstants.THING_TYPE_DSMR_BRIDGE, - Integer.toHexString(currentScannedPortName.hashCode())); + private ThingUID bridgeDiscovered(ThingTypeUID bridgeThingTypeUID) { + ThingUID thingUID = new ThingUID(bridgeThingTypeUID, Integer.toHexString(currentScannedPortName.hashCode())); + final boolean smarty = THING_TYPE_SMARTY_BRIDGE.equals(bridgeThingTypeUID); + final String label = String.format("@text/thing-type.dsmr.%s.label", smarty ? "smartyBridge" : "dsmrBridge"); // Construct the configuration for this meter Map properties = new HashMap<>(); - properties.put("serialPort", currentScannedPortName); - - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) - .withThingType(DSMRBindingConstants.THING_TYPE_DSMR_BRIDGE).withProperties(properties) - .withLabel("@text/thing-type.dsmr.dsmrBridge.label").build(); + properties.put(CONFIGURATION_SERIAL_PORT, currentScannedPortName); + if (smarty) { + properties.put(CONFIGURATION_DECRYPTION_KEY, CONFIGURATION_DECRYPTION_KEY_EMPTY); + } + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(bridgeThingTypeUID) + .withProperties(properties).withLabel(label).build(); logger.debug("[{}] discovery result:{}", currentScannedPortName, discoveryResult); thingDiscovered(discoveryResult); - meterDetector.detectMeters(telegram).getKey().forEach(m -> meterDiscovered(m, thingUID)); - return true; + return thingUID; } @Override diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRDiscoveryService.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRDiscoveryService.java similarity index 98% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRDiscoveryService.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRDiscoveryService.java index 20920e138d08f..fabee46834ac8 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRDiscoveryService.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRDiscoveryService.java @@ -35,7 +35,7 @@ @NonNullByDefault public abstract class DSMRDiscoveryService extends AbstractDiscoveryService { /** - * Timeout for Discovery timeout + * Timeout for discovery time. */ private static final int DSMR_DISCOVERY_TIMEOUT_SECONDS = 60; diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetector.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetector.java similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetector.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetector.java diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java similarity index 92% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java index c986f5256b410..a53232764cdbd 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryService.java @@ -100,11 +100,19 @@ protected void verifyUnregisteredCosemObjects(P1Telegram telegram, reportUnregisteredMeters(); } else { reportUnrecognizedCosemObjects(undetectedCosemObjects); - logger.info("There are some unrecognized cosem values in the data received from the meter," + logger.info("There are unrecognized cosem values in the data received from the meter," + " which means some meters might not be detected. Please report your raw data as reference: {}", telegram.getRawTelegram()); } } + if (!telegram.getUnknownCosemObjects().isEmpty()) { + logger.info("There are unrecognized cosem values in the data received from the meter," + + " which means you have values that can't be read by a channel: {}. Please report them and your raw data as reference: {}", + telegram.getUnknownCosemObjects().stream() + .map(e -> String.format("obis id:{}, value:{}", e.getKey(), e.getValue())) + .collect(Collectors.joining(", ")), + telegram.getRawTelegram()); + } } /** diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java similarity index 80% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java index bafdaffe6f81b..d42452fd493c2 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRBridgeHandler.java @@ -12,13 +12,14 @@ */ package org.openhab.binding.dsmr.internal.handler; +import static org.openhab.binding.dsmr.internal.DSMRBindingConstants.THING_TYPE_SMARTY_BRIDGE; + import java.util.ArrayList; import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.ThingStatus; @@ -32,6 +33,7 @@ import org.openhab.binding.dsmr.internal.device.DSMREventListener; import org.openhab.binding.dsmr.internal.device.DSMRFixedConfigDevice; import org.openhab.binding.dsmr.internal.device.DSMRSerialAutoDevice; +import org.openhab.binding.dsmr.internal.device.DSMRTelegramListener; import org.openhab.binding.dsmr.internal.device.connector.DSMRConnectorErrorEvent; import org.openhab.binding.dsmr.internal.device.connector.DSMRSerialSettings; import org.openhab.binding.dsmr.internal.device.p1telegram.P1Telegram; @@ -70,23 +72,23 @@ public class DSMRBridgeHandler extends BaseBridgeHandler implements DSMREventLis /** * The dsmrDevice managing the connection and handling telegrams. */ - private @Nullable DSMRDevice dsmrDevice; + private @NonNullByDefault({}) DSMRDevice dsmrDevice; /** * Long running process that controls the DSMR device connection. */ - private @Nullable DSMRDeviceRunnable dsmrDeviceRunnable; + private @NonNullByDefault({}) DSMRDeviceRunnable dsmrDeviceRunnable; /** * Thread for {@link DSMRDeviceRunnable}. A thread is used because the {@link DSMRDeviceRunnable} is a blocking * process that runs as long as the thing is not disposed. */ - private @Nullable Thread dsmrDeviceThread; + private @NonNullByDefault({}) Thread dsmrDeviceThread; /** * Watchdog to check if messages received and restart if necessary. */ - private @Nullable ScheduledFuture watchdog; + private @NonNullByDefault({}) ScheduledFuture watchdog; /** * Number of nanoseconds after which a timeout is triggered when no messages received. @@ -98,6 +100,8 @@ public class DSMRBridgeHandler extends BaseBridgeHandler implements DSMREventLis */ private volatile long telegramReceivedTimeNanos; + private final boolean smartyMeter; + /** * Constructor * @@ -107,6 +111,7 @@ public class DSMRBridgeHandler extends BaseBridgeHandler implements DSMREventLis public DSMRBridgeHandler(Bridge bridge, SerialPortManager serialPortManager) { super(bridge); this.serialPortManager = serialPortManager; + smartyMeter = THING_TYPE_SMARTY_BRIDGE.equals(bridge.getThingTypeUID()); } /** @@ -128,44 +133,49 @@ public void handleCommand(ChannelUID channelUID, Command command) { */ @Override public void initialize() { - DSMRDeviceConfiguration deviceConfig = getConfigAs(DSMRDeviceConfiguration.class); + final DSMRDeviceConfiguration deviceConfig = getConfigAs(DSMRDeviceConfiguration.class); + + if (smartyMeter && (deviceConfig.decryptionKey == null || deviceConfig.decryptionKey.length() != 32)) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/error.configuration.invalidsmartykey"); + return; + } logger.trace("Using configuration {}", deviceConfig); updateStatus(ThingStatus.UNKNOWN); receivedTimeoutNanos = TimeUnit.SECONDS.toNanos(deviceConfig.receivedTimeout); - try { - DSMRDevice dsmrDevice = createDevice(deviceConfig); - resetLastReceivedState(); - this.dsmrDevice = dsmrDevice; // otherwise Eclipse will give a null pointer error on the next line :-( - dsmrDeviceRunnable = new DSMRDeviceRunnable(dsmrDevice, this); - dsmrDeviceThread = new Thread(dsmrDeviceRunnable); - dsmrDeviceThread.start(); - watchdog = scheduler.scheduleWithFixedDelay(this::alive, receivedTimeoutNanos, receivedTimeoutNanos, + final DSMRDevice dsmrDevice = createDevice(deviceConfig); + resetLastReceivedState(); + this.dsmrDevice = dsmrDevice; // otherwise Eclipse will give a null pointer error on the next line :-( + dsmrDeviceRunnable = new DSMRDeviceRunnable(dsmrDevice, this); + dsmrDeviceThread = new Thread(dsmrDeviceRunnable); + dsmrDeviceThread.start(); + watchdog = scheduler.scheduleWithFixedDelay(this::alive, receivedTimeoutNanos, receivedTimeoutNanos, TimeUnit.NANOSECONDS); - } catch (IllegalArgumentException e) { - logger.debug("Incomplete configuration: {}", deviceConfig); - - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, - "@text/error.configuration.incomplete"); - } } /** * Creates the {@link DSMRDevice} that corresponds with the user specified configuration. * * @param deviceConfig device configuration - * @return Specific {@link DSMRDevice} instance or throws an {@link IllegalArgumentException} if no valid - * configuration was set. + * @return Specific {@link DSMRDevice} instance */ private DSMRDevice createDevice(DSMRDeviceConfiguration deviceConfig) { - DSMRDevice dsmrDevice; + final DSMRDevice dsmrDevice; - if (deviceConfig.isSerialFixedSettings()) { + if (smartyMeter) { dsmrDevice = new DSMRFixedConfigDevice(serialPortManager, deviceConfig.serialPort, - DSMRSerialSettings.getPortSettingsFromConfiguration(deviceConfig), this); + DSMRSerialSettings.HIGH_SPEED_SETTINGS, this, new DSMRTelegramListener(deviceConfig.decryptionKey)); } else { - dsmrDevice = new DSMRSerialAutoDevice(serialPortManager, deviceConfig.serialPort, this, scheduler, - deviceConfig.receivedTimeout); + final DSMRTelegramListener telegramListener = new DSMRTelegramListener(); + + if (deviceConfig.isSerialFixedSettings()) { + dsmrDevice = new DSMRFixedConfigDevice(serialPortManager, deviceConfig.serialPort, + DSMRSerialSettings.getPortSettingsFromConfiguration(deviceConfig), this, telegramListener); + } else { + dsmrDevice = new DSMRSerialAutoDevice(serialPortManager, deviceConfig.serialPort, this, + telegramListener, scheduler, deviceConfig.receivedTimeout); + } } return dsmrDevice; } @@ -204,7 +214,7 @@ private void alive() { if (deltaLastReceived > receivedTimeoutNanos) { logger.debug("No data received for {} seconds, restarting port if possible.", - TimeUnit.NANOSECONDS.toSeconds(deltaLastReceived)); + TimeUnit.NANOSECONDS.toSeconds(deltaLastReceived)); if (dsmrDeviceRunnable != null) { dsmrDeviceRunnable.restart(); } @@ -230,7 +240,7 @@ private void resetLastReceivedState() { public synchronized void handleTelegramReceived(P1Telegram telegram) { if (telegram.getCosemObjects().isEmpty()) { logger.debug("Parsing worked but something went wrong, so there were no CosemObjects:{}", - telegram.getTelegramState().stateDetails); + telegram.getTelegramState().stateDetails); deviceOffline(ThingStatusDetail.COMMUNICATION_ERROR, telegram.getTelegramState().stateDetails); } else { resetLastReceivedState(); @@ -255,7 +265,7 @@ private void meterValueReceived(P1Telegram telegram) { getThing().getThings().forEach(child -> { if (logger.isTraceEnabled()) { logger.trace("Update child:{} with {} objects", child.getThingTypeUID().getId(), - telegram.getCosemObjects().size()); + telegram.getCosemObjects().size()); } DSMRMeterHandler dsmrMeterHandler = (DSMRMeterHandler) child.getHandler(); diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java similarity index 94% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java index d9c75b34f18e1..add2cf8818055 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/handler/DSMRMeterHandler.java @@ -99,17 +99,17 @@ public void initialize() { meterType = DSMRMeterType.valueOf(getThing().getThingTypeUID().getId().toUpperCase()); } catch (IllegalArgumentException iae) { logger.warn( - "{} could not be initialized due to an invalid meterType {}. Delete this Thing if the problem persists.", - getThing(), getThing().getThingTypeUID().getId().toUpperCase()); + "{} could not be initialized due to an invalid meterType {}. Delete this Thing if the problem persists.", + getThing(), getThing().getThingTypeUID().getId().toUpperCase()); updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_ERROR, - "@text/error.configuration.invalidmetertype"); + "@text/error.configuration.invalidmetertype"); return; } DSMRMeterConfiguration meterConfig = getConfigAs(DSMRMeterConfiguration.class); DSMRMeterDescriptor meterDescriptor = new DSMRMeterDescriptor(meterType, meterConfig.channel); meter = new DSMRMeter(meterDescriptor); meterWatchdog = scheduler.scheduleWithFixedDelay(this::updateState, meterConfig.refresh, meterConfig.refresh, - TimeUnit.SECONDS); + TimeUnit.SECONDS); updateStatus(ThingStatus.UNKNOWN); } @@ -158,7 +158,7 @@ private synchronized void updateState() { @Override public void telegramReceived(P1Telegram telegram) { lastReceivedValues = Collections.emptyList(); - DSMRMeter localMeter = meter; + final DSMRMeter localMeter = meter; if (localMeter == null) { return; @@ -183,7 +183,7 @@ public void telegramReceived(P1Telegram telegram) { @Override public synchronized void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE - && getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.BRIDGE_OFFLINE) { + && getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.BRIDGE_OFFLINE) { // Set status to offline --> Thing will become online after receiving meter values setDeviceOffline(ThingStatusDetail.NONE, null); } else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) { diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeter.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeter.java similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeter.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeter.java diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConfiguration.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConfiguration.java similarity index 92% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConfiguration.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConfiguration.java index 7931e843aa630..9a4efe1425eb4 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConfiguration.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConfiguration.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.dsmr.internal.meter; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * This class describes the configuration for a meter. * * @author M. Volaart - Initial contribution * @author Hilbrand Bouwkamp - Added refresh field */ +@NonNullByDefault public class DSMRMeterConfiguration { /** * M-Bus channel diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConstants.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConstants.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterConstants.java diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterDescriptor.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterDescriptor.java similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterDescriptor.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterDescriptor.java diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterKind.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterKind.java similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterKind.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterKind.java diff --git a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java similarity index 87% rename from addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java rename to bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java index bad5fe98a37fc..2ec3ca428ce41 100644 --- a/addons/binding/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java +++ b/bundles/org.openhab.binding.dsmr/src/main/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterType.java @@ -268,11 +268,34 @@ public enum DSMRMeterType { /** DSMR V5.0 Slave Electricity meter */ SLAVE_ELECTRICITY_V5_0(DSMRMeterKind.SLAVE_ELECTRICITY1, CosemObjectType.METER_EQUIPMENT_IDENTIFIER, CosemObjectType.METER_DEVICE_TYPE, CosemObjectType.METER_EQUIPMENT_IDENTIFIER, - CosemObjectType.EMETER_VALUE); + CosemObjectType.EMETER_VALUE), + + /** Luxembourg "Smarty" V1.0 Electricity meter */ + ELECTRICITY_SMARTY_V1_0(DSMRMeterKind.MAIN_ELECTRICITY, CosemObjectType.EMETER_EQUIPMENT_IDENTIFIER_V2_X, + new CosemObjectType[] { + CosemObjectType.EMETER_EQUIPMENT_IDENTIFIER_V2_X, CosemObjectType.EMETER_DELIVERY_TARIFF0, + CosemObjectType.EMETER_PRODUCTION_TARIFF0, CosemObjectType.EMETER_TOTAL_IMPORTED_ENERGY_REGISTER_Q, + CosemObjectType.EMETER_TOTAL_EXPORTED_ENERGY_REGISTER_Q, CosemObjectType.EMETER_ACTUAL_DELIVERY, + CosemObjectType.EMETER_ACTUAL_PRODUCTION, CosemObjectType.EMETER_ACTUAL_REACTIVE_DELIVERY, + CosemObjectType.EMETER_ACTUAL_REACTIVE_PRODUCTION, CosemObjectType.EMETER_ACTIVE_THRESHOLD_SMAX, + CosemObjectType.EMETER_SWITCH_POSITION }, + new CosemObjectType[] { + CosemObjectType.EMETER_POWER_FAILURES, CosemObjectType.EMETER_VOLTAGE_SAGS_L1, + CosemObjectType.EMETER_VOLTAGE_SAGS_L2, CosemObjectType.EMETER_VOLTAGE_SAGS_L3, + CosemObjectType.EMETER_VOLTAGE_SWELLS_L1, CosemObjectType.EMETER_VOLTAGE_SWELLS_L2, + CosemObjectType.EMETER_VOLTAGE_SWELLS_L3, CosemObjectType.EMETER_INSTANT_CURRENT_L1, + CosemObjectType.EMETER_INSTANT_CURRENT_L2, CosemObjectType.EMETER_INSTANT_CURRENT_L3, + CosemObjectType.EMETER_INSTANT_POWER_DELIVERY_L1, CosemObjectType.EMETER_INSTANT_POWER_DELIVERY_L2, + CosemObjectType.EMETER_INSTANT_POWER_DELIVERY_L3, CosemObjectType.EMETER_INSTANT_POWER_PRODUCTION_L1, + CosemObjectType.EMETER_INSTANT_POWER_PRODUCTION_L2, CosemObjectType.EMETER_INSTANT_POWER_PRODUCTION_L3, + CosemObjectType.EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L1, CosemObjectType.EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L2, + CosemObjectType.EMETER_INSTANT_REACTIVE_POWER_DELIVERY_L3, CosemObjectType.EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L1, + CosemObjectType.EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L2, CosemObjectType.EMETER_INSTANT_REACTIVE_POWER_PRODUCTION_L3, + }); // @formatter:on public static final Set METER_THING_TYPES = Arrays.asList(DSMRMeterType.values()).stream() - .map(DSMRMeterType::getThingTypeUID).collect(Collectors.toSet()); + .map(DSMRMeterType::getThingTypeUID).collect(Collectors.toSet()); private final Logger logger = LoggerFactory.getLogger(DSMRMeterType.class); @@ -304,22 +327,25 @@ public enum DSMRMeterType { /** * Creates a new enum * - * @param channelKey - * String containing the channel configuration for this meter + * @param meterKind kind of meter + * @param cosemObjectTypeMeterId identifier cosem object + * @param requiredCosemObjects list of objects that are present in this meter type */ DSMRMeterType(DSMRMeterKind meterKind, CosemObjectType cosemObjectTypeMeterId, - CosemObjectType... requiredCosemObjects) { + CosemObjectType... requiredCosemObjects) { this(meterKind, cosemObjectTypeMeterId, requiredCosemObjects, new CosemObjectType[0]); } /** * Creates a new enum * - * @param channelKey - * String containing the channel configuration for this meter + * @param meterKind kind of meter + * @param cosemObjectTypeMeterId identifier cosem object + * @param requiredCosemObjects list of objects that are present in this meter type + * @param optionalCosemObjects list of objects that are optional present in this meter type */ DSMRMeterType(DSMRMeterKind meterKind, CosemObjectType cosemObjectTypeMeterId, - CosemObjectType[] requiredCosemObjects, CosemObjectType[] optionalCosemObjects) { + CosemObjectType[] requiredCosemObjects, CosemObjectType[] optionalCosemObjects) { this.meterKind = meterKind; this.cosemObjectTypeMeterId = cosemObjectTypeMeterId; this.requiredCosemObjects = requiredCosemObjects; @@ -328,7 +354,7 @@ public enum DSMRMeterType { supportedCosemObjects = new CosemObjectType[requiredCosemObjects.length + optionalCosemObjects.length]; System.arraycopy(requiredCosemObjects, 0, supportedCosemObjects, 0, requiredCosemObjects.length); System.arraycopy(optionalCosemObjects, 0, supportedCosemObjects, requiredCosemObjects.length, - optionalCosemObjects.length); + optionalCosemObjects.length); } /** diff --git a/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..cb8a3d6caadd0 --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,8 @@ + + + + DSMR Binding + This binding integrates Dutch and Luxembourg Smart Meters + Marcel Volaart, Hilbrand Bouwkamp + diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/config/configuration_parameters.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/config/configuration_parameters.xml similarity index 76% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/config/configuration_parameters.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/config/configuration_parameters.xml index 93368d48fa40f..87a6b1cc9aee4 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/config/configuration_parameters.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/config/configuration_parameters.xml @@ -5,12 +5,12 @@ xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd"> - + serial-port The serial port where the P1 port of the Smart Meter is connected (e.g. Linux: /dev/ttyUSB0, Windows: COM1) - + 30 The time period within results are expected in seconds @@ -61,6 +61,23 @@ + + + serial-port + + The serial port where the P1 port of the Smart Meter is connected (e.g. Linux: /dev/ttyUSB0, Windows: COM1) + + + + The Luxembourgian Smart meter decryption key. Ask for your energy grid operator for your Smart meter P1 key. + + + 30 + + The time period within results are expected in seconds + + + 60 diff --git a/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/i18n/dsmr_en.properties b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/i18n/dsmr_en.properties new file mode 100644 index 0000000000000..16879e0480c60 --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/i18n/dsmr_en.properties @@ -0,0 +1,32 @@ +# Text used in the source code. + +# meter kind names +meterKind.invalid.label = Invalid Meter +meterKind.device.label = Generic DSMR Device +meterKind.main_electricity.label = Main Electricity Meter +meterKind.gas.label = Gas Meter +meterKind.heating.label = Heating Meter +meterKind.cooling.label = Cooling Meter +meterKind.water.label = Water Meter +meterKind.generic.label = Generic Meter +meterKind.gj.label = GJ Meter +meterKind.m3.label = M3 Meter +meterKind.slave_electricity1.label = Slave Electricity Meter +meterKind.slave_electricity2.label = Slave Electricity Meter 2 + +# Connector error messages +error.bridge.nodata = Not receiving data from meter. +error.configuration.invalidmetertype = The thing could not be initialized. Delete and re-add thing if the problem persists. +error.configuration.invalidsmartykey = The given Smarty decyption key is to short. The decyption key must be 32 characters long. +error.thing.nodata = Not receiving data from meter. +error.connector.dont_exists = Serial port does not exist. +error.connector.in_use = Serial port is already in use. +error.connector.internal_error = Unexpected error, possible bug. Please report. +error.connector.not_compatible = Serial port is not compatible. +error.connector.read_error = Read error. + +# thing types +thing-type.dsmr.dsmrBridge.label = Smart Meter +thing-type.dsmr.dsmrBridge.description = The Dutch Smart Meter (DSMR) +thing-type.dsmr.smartyBridge.label = Smarty Meter +thing-type.dsmr.smartyBridge.description = The Luxembourgian Smart Meter 'Smarty' diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_nl.properties b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/i18n/dsmr_nl.properties similarity index 86% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_nl.properties rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/i18n/dsmr_nl.properties index 1ccb8121380e4..bc735ecec8970 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/i18n/dsmr_nl.properties +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/i18n/dsmr_nl.properties @@ -19,7 +19,7 @@ meterKind.slave_electricity2.label = Extra Elektriciteitsmeter 2 # Connector error messages error.bridge.nodata = Geen gegevens ontvangen van de meter. error.configuration.invalidmetertype = Het ding kon niet worden geïnitialiseerd. Verwijder en voeg het ding opnieuw toe als het probleem zich blijft voordoen. -error.configuration.incomplete = Onvolledige configuratie. Niet alle benodigde velden zijn ingevuld. +error.configuration.invalidsmartykey = De opgegeven Smarty decyptie key is niet correct. De decyptie key moet 32 karakters lang zijn. error.thing.nodata = Geen gegevens ontvangen van de meter. error.connector.dont_exists = Seriële poort bestaat niet. error.connector.in_use = Seriële poort is al in gebruik. @@ -29,7 +29,9 @@ error.connector.read_error = Leesfout. # thing types thing-type.dsmr.dsmrBridge.label = Slimme Meter -thing-type.dsmr.dsmrBridge.description = Nederlandse Slimme Meter (DSMR). +thing-type.dsmr.dsmrBridge.description = De Nederlandse Slimme Meter (DSMR) +thing-type.dsmr.smartyBridge.label = Smarty Meter +thing-type.dsmr.smartyBridge.description = De luxemburgse Slimme Meter 'Smarty' thing-type.dsmr.cooling_ace4000.label = Koudemeter (ACE4000) thing-type.dsmr.cooling_ace4000.description = Dit is een koudemeter die voldoet aan de ACE4000 GTMM Mk3 standaard. @@ -238,3 +240,77 @@ channel-type.waterValueType.label = Waterverbruik channel-type.waterValueType.description = Het totale waterverbruik in de afgelopen periode. channel-type.waterValvePositionType.label = Waterklepstand channel-type.waterValvePositionType.description = De stand van de waterklepafsluiter. + + + Total imported energy register (P+) 1, 0:1.8.0.255 2 3 F9(3,3) kWh x + Total exported energy register (P-) 1, 0:2.8.0.255 2 3 F9(3,3) kWh x + Total imported energy register (Q+) 1, 0:3.8.0.255 2 3 F9(3,3) kvarh x + Total exported energy register (Q-) 1, 0:4.8.0.255 2 3 F9(3,3) kvarh x + Instantaneous imported active power (P+) 1, 0:1.7.0.255 2 3 F5(3,3) kW x + Instantaneous exported active power (P-) 1, 0:2.7.0.255 2 3 F5(3,3) kW x + Instantaneous imported reactive power (Q+) 1, 0:3.7.0.255 2 3 F5(3,3) kvar x + Instantaneous exported reactive power (Q-) 1, 0:4.7.0.255 2 3 F5(3,3) kvar x + Active threshold (SMAX) 0, 0:17.0.0.255 3 71 F4(1,1) kVA x + Breaker control state 0, 0:96.3.10.255 3 70 I1 - x + Number of power failures 0, 0:96.7.21.255 2 1 F5(0,0) - + Number of voltage sags L1 0, 0:32.32.0.255 2 1 F5(0,0) - + Number of voltage sags L2 0, 0:52.32.0.255 2 1 F5(0,0) + + Number of voltage sags L3 + 0, 0:72.32.0.255 + Number of voltage swells L1 + 0, 0:32.36.0.255 + Number of voltage swells L2 + 0, 0:52.36.0.255 + Number of voltage swells L3 + 0, 0:72.36.0.255 + Long message E + -meter + 0, 0:96.13.0.255 + Long message channel x + 0, 0:96.13.x.255 + Instantaneous current L1 + 1, 0:31.7.0.255 + Instantaneous current L2 + 1, 0:51.7.0.255 + Instantaneous current L3 + 1, 0:71.7.0.255 + Instantaneous active power (P+) L1 + 1, 0:21.7.0.255 + Instantaneous active power (P+) L2 + 1, 0:41.7.0.255 + Instantaneous active power (P+) L3 + 1, 0:61.7.0.255 + Instantaneous active power (P-) L1 + 1, 0:22.7.0.255 + Instantaneous active power (P-) L2 + 1, 0:42.7.0.255 + Instantaneous active power (P-) L3 + 1, 0:62.7.0.255 + Instantaneous reactive power (Q+) L1 + 1, 0:23.7.0.255 + Instantaneous reactive power (Q+) L2 + 1, 0:43.7.0.255 + Instantaneous reactive power (Q+) L3 + 1, 0:63.7.0.255 + Instantaneous reactive power (Q-) L1 + 1, 0:24.7.0.255 + Instantaneous reactive power (Q-) L2 + 1, 0:44.7.0.255 + Instantaneous reactive power (Q-) L3 + 1, 0:64.7.0.255 + Device type channel x + 0, x:24.1.0.255 + Equipment Identifier channel x + 0, x:96.1.0.255 + Last Index capture + -time channel x + 0, x:24.2.1.255 + Last Index gas channel x + 0, x:24.2.1.255 + valve position gas channel x + 0, x:24.4.0.255 + Last Index water channel x + 0, x:24.2.1.255 + Last Index heat channel x + 0, x:24.2.1.255 diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_cooling.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_cooling.xml similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_cooling.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_cooling.xml diff --git a/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_electricity.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_electricity.xml new file mode 100644 index 0000000000000..684b446a364b2 --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_electricity.xml @@ -0,0 +1,327 @@ + + + + + Number:Energy + + The total amount of electricity used. + + + + Number:Energy + + The total amount of electricity used for tariff 0. + + + + Number:Energy + + The total amount of electricity used for tariff 1. + + + + Number:Energy + + The total amount of electricity used for tariff 2. + + + + Number:Energy + + The total amount of electricity used for tariff 0 (anti fraud). + + + + Number + + The total amount of electricity used for tariff 1 (anti fraud). + + + + Number:Energy + + The total amount of electricity used for tariff 2 (anti fraud). + + + + Number:Energy + + The total amount of electricity produced for tariff 0. + + + + Number:Energy + + The total amount of electricity produced for tariff 1. + + + + Number:Energy + + The total amount of electricity produced for tariff 2. + + + + Number:Energy + + The Total imported energy register (P+). + + + + Number:Energy + + The Total exported energy register (P-). + + + + Number:Energy + + The Total imported energy register (Q+). + + + + Number:Energy + + The Total exported energy register (Q-). + + + + String + + The current tariff indicator. + + + + Number:Power + + The aggregate active import power. + + + + Number:Power + + The current power delivery. + + + + Number:Power + + The current power production. + + + + Number + + The current reactive power delivery. + + + + Number + + The current reactive power production. + + + + Number:ElectricCurrent + + The actual threshold. + + + + Number:Power + + The actual threshold. + + + + Number + + Active threshold (SMAX). + + + + Number + + The switch position. + + + + Number + + The number of power failures. + + + + Number + + The number of long power failures. + + + + Number + + Number of log entries. + + + + DateTime + + Timestamp when the power failure ended. There can be multiple log entries. +Each entry has its own channel (emeter_power_failure_log_timestamp*x*, *x* = 0 - 9) + + + + Number:Time + + Duration of the power failure. +Each entry has its own channel (emeter_power_failure_log_duration*x*, *x* = 0 - 9) + + + + Number + + The number of voltage sags L1. + + + + Number + + The number of voltage sags L2. + + + + Number + + The number of voltage sags L3. + + + + Number + + The number of voltage swells L1. + + + + Number + + The number of voltage swells L2. + + + + Number + + The number of voltage swells L3. + + + + Number:ElectricCurrent + + The instant current L1. + + + + Number:ElectricCurrent + + The instant current L2. + + + + Number:ElectricCurrent + + The instant current L3. + + + + Number:Power + + The instant power delivery L1. + + + + Number:Power + + The instant power delivery L2. + + + + Number + + The instant power delivery L3. + + + + Number:Power + + The instant power production L1. + + + + Number:Power + + The instant power production L2. + + + + Number:Power + + The instant power production L3. + + + + Number:Power + + The instant reactive power delivery L1. + + + + Number:Power + + The instant reactive power delivery L2. + + + + Number:Power + + The instant reactive power delivery L3. + + + + Number:Power + + The instant reactive power production L1. + + + + Number:Power + + The instant reactive power production L2. + + + + Number:Power + + The instant reactive power production L3. + + + + Number:ElectricPotential + + The instant voltage L1. + + + + Number:ElectricPotential + + The instant voltage L2. + + + + Number:ElectricPotential + + The instant voltage L3. + + + diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_gas.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_gas.xml similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_gas.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_gas.xml diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_generic.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_generic.xml similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_generic.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_generic.xml diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_genericmeter.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_genericmeter.xml similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_genericmeter.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_genericmeter.xml diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_gj.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_gj.xml similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_gj.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_gj.xml diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_heating.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_heating.xml similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_heating.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_heating.xml diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_m3.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_m3.xml similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_m3.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_m3.xml diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_water.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_water.xml similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/channeltypes_water.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/channeltypes_water.xml diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/dsmr_device.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/dsmr_device.xml similarity index 83% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/dsmr_device.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/dsmr_device.xml index 9856a572b5866..fa7f3019e6594 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/dsmr_device.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/dsmr_device.xml @@ -4,8 +4,8 @@ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> - - The Dutch Smart Meter device + + The Dutch Smart Meter (DSMR) diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_ace4000.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_cooling_ace4000.xml similarity index 75% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_ace4000.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_cooling_ace4000.xml index 1db72c0e69ba0..bb765a748a847 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_ace4000.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_cooling_ace4000.xml @@ -1,9 +1,14 @@ - + + + + This is a cooling meter that complies to the ACE4000 GTMM Mk3 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_v2_2.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_cooling_v2_2.xml similarity index 79% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_v2_2.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_cooling_v2_2.xml index e45227bb97a65..194cff6e9b940 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_cooling_v2_2.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_cooling_v2_2.xml @@ -1,9 +1,14 @@ - + + + + This is a cooling meter that complies to the DSMR V2.2 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v2_v3.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_device_dsmr_v2_v3.xml similarity index 89% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v2_v3.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_device_dsmr_v2_v3.xml index 643959bb97499..d3b3059545700 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v2_v3.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_device_dsmr_v2_v3.xml @@ -1,5 +1,6 @@ - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v4.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_device_dsmr_v4.xml similarity index 96% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v4.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_device_dsmr_v4.xml index 998c6aec1892c..25adb1adc8d2a 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v4.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_device_dsmr_v4.xml @@ -7,6 +7,7 @@ + diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v5.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_device_dsmr_v5.xml similarity index 95% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v5.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_device_dsmr_v5.xml index 76b891d0de763..e519d51c8fde9 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_device_dsmr_v5.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_device_dsmr_v5.xml @@ -7,6 +7,7 @@ + diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_ace4000.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_ace4000.xml similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_ace4000.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_ace4000.xml diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v2_1.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v2_1.xml similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v2_1.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v2_1.xml diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v2_2.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v2_2.xml similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v2_2.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v2_2.xml diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v3.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v3.xml similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v3.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v3.xml diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v4.xml similarity index 97% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v4.xml index 8919007a113a8..e4b8c6bf46e8d 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v4.xml @@ -1,5 +1,6 @@ - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_0_4.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v4_0_4.xml similarity index 97% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_0_4.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v4_0_4.xml index bc25f2d3bae54..33c59678f9dd5 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_0_4.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v4_0_4.xml @@ -1,5 +1,6 @@ - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_2.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v4_2.xml similarity index 97% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_2.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v4_2.xml index da895c6c916a6..0867538debf40 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v4_2.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v4_2.xml @@ -1,5 +1,6 @@ - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v5.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v5.xml similarity index 97% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v5.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v5.xml index ced74b5456622..4591d58d25a6f 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_dsmr_v5.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_dsmr_v5.xml @@ -1,5 +1,6 @@ - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave1_ace4000.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_slave1_ace4000.xml similarity index 87% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave1_ace4000.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_slave1_ace4000.xml index 1d4794c6e7c91..0ecdbf5a72a7a 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave1_ace4000.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_slave1_ace4000.xml @@ -1,9 +1,14 @@ - + + + + This is the first slave electricity meter that complies to the ACE4000 GTMM Mk3 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave2_ace4000.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_slave2_ace4000.xml similarity index 87% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave2_ace4000.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_slave2_ace4000.xml index 5566ed5e1c64d..1770bd83d098b 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave2_ace4000.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_slave2_ace4000.xml @@ -1,9 +1,14 @@ - + + + + This is the second slave electricity meter that complies to the ACE4000 GTMM Mk3 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v4.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_slave_v4.xml similarity index 91% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v4.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_slave_v4.xml index d1f59d15b12d8..5ab826fa635e2 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v4.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_slave_v4.xml @@ -1,5 +1,6 @@ - diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v5.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_slave_v5.xml similarity index 80% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v5.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_slave_v5.xml index 6933201a1d4c7..6b7b05a0113ed 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_electricity_slave_v5.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_slave_v5.xml @@ -1,9 +1,14 @@ - + + + + This is the slave electricity meter that complies to the DSMR 5.x specification. diff --git a/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_smarty_v1.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_smarty_v1.xml new file mode 100644 index 0000000000000..cfa7e5c09c221 --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_electricity_smarty_v1.xml @@ -0,0 +1,72 @@ + + + + + + + + + + This is an electricity meter that complies to the Luxembourg's Smarty V1.0 specification. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_ace4000.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gas_ace4000.xml similarity index 81% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_ace4000.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gas_ace4000.xml index 12aee8dfc3d1f..193daf35cbcdc 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_ace4000.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gas_ace4000.xml @@ -1,9 +1,14 @@ - + + + + This is a gas meter that complies to the ACE4000 GTMM Mk3 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_1.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gas_v2_1.xml similarity index 83% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_1.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gas_v2_1.xml index 68b6c26618413..199d397e1d367 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_1.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gas_v2_1.xml @@ -1,9 +1,14 @@ - + + + + This is a gas meter that complies to the DSMR V2.1 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_2.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gas_v2_2.xml similarity index 83% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_2.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gas_v2_2.xml index f27299f6b6126..44975edfafeda 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v2_2.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gas_v2_2.xml @@ -1,9 +1,14 @@ - + + + + This is a gas meter that complies to the DSMR V2.2 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v3_0.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gas_v3_0.xml similarity index 81% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v3_0.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gas_v3_0.xml index 6544878666a74..573ca1e4615be 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gas_v3_0.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gas_v3_0.xml @@ -1,9 +1,14 @@ - + + + + This is a gas meter that complies to the DSMR V3.0 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_generic_v3_0.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_generic_v3_0.xml similarity index 80% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_generic_v3_0.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_generic_v3_0.xml index 918b439a465b9..2d6b2e277c30d 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_generic_v3_0.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_generic_v3_0.xml @@ -1,9 +1,14 @@ - + + + + This is a generic meter that complies to the DSMR V3.0 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v3_0.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gj_v3_0.xml similarity index 80% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v3_0.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gj_v3_0.xml index c73e8b47c978c..b04f8ad7969de 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v3_0.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gj_v3_0.xml @@ -1,9 +1,14 @@ - + + + + This is a Giga Joule meter that complies to the DSMR V3.0 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v4.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gj_v4.xml similarity index 81% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v4.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gj_v4.xml index c3285243da09c..8e9ede8ab28d6 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v4.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gj_v4.xml @@ -1,9 +1,14 @@ - + + + + This is a Giga Joule meter that complies to the DSMR V4.x specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v5_0.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gj_v5_0.xml similarity index 80% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v5_0.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gj_v5_0.xml index dfbda35e31e80..bc3f3b753cabc 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_gj_v5_0.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_gj_v5_0.xml @@ -1,9 +1,14 @@ - + + + + This is a Giga Joule meter that complies to the DSMR V5.0 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_ace4000.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_heating_ace4000.xml similarity index 77% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_ace4000.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_heating_ace4000.xml index 72e81335f14b8..581f9b43ae697 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_ace4000.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_heating_ace4000.xml @@ -1,9 +1,14 @@ - + + + + This is a heating meter that complies to the ACE4000 GTMM Mk3 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_v2_2.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_heating_v2_2.xml similarity index 79% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_v2_2.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_heating_v2_2.xml index 229588ca719cb..e4e7cddd67215 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_heating_v2_2.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_heating_v2_2.xml @@ -1,9 +1,14 @@ - + + + + This is a heating meter that complies to the DSMR V2.2 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v4.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_m3_v4.xml similarity index 81% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v4.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_m3_v4.xml index f192e2ea24e53..fac084be87501 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v4.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_m3_v4.xml @@ -1,9 +1,14 @@ - + + + + This is a m3 meter that complies to the DSMR V4.x specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v5_0.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_m3_v5_0.xml similarity index 80% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v5_0.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_m3_v5_0.xml index 84cf160f12bb9..1cb83bf791b7e 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_m3_v5_0.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_m3_v5_0.xml @@ -1,9 +1,14 @@ - + + + + This is a m3 meter that complies to the DSMR V5.0 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_ace4000.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_water_ace4000.xml similarity index 77% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_ace4000.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_water_ace4000.xml index 181cbb9b2e0a2..7c6dd14f7987c 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_ace4000.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_water_ace4000.xml @@ -1,9 +1,14 @@ - + + + + This is a water meter that complies to the ACE4000 GTMM Mk3 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v2_2.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_water_v2_2.xml similarity index 79% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v2_2.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_water_v2_2.xml index 055aef5e2d348..9afc6e199ec08 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v2_2.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_water_v2_2.xml @@ -1,9 +1,14 @@ - + + + + This is a water meter that complies to the DSMR V2.2 specification. diff --git a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v3_0.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_water_v3_0.xml similarity index 80% rename from addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v3_0.xml rename to bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_water_v3_0.xml index 230c0a07cce36..b943afbf4f5ac 100644 --- a/addons/binding/org.openhab.binding.dsmr/ESH-INF/thing/meter_water_v3_0.xml +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/meter_water_v3_0.xml @@ -1,9 +1,14 @@ - + + + + This is a water meter that complies to the DSMR V3.0 specification. diff --git a/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/smarty_device.xml b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/smarty_device.xml new file mode 100644 index 0000000000000..7a9da2e565b40 --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/src/main/resources/ESH-INF/thing/smarty_device.xml @@ -0,0 +1,13 @@ + + + + + + The Luxembourgian Smart Meter 'Smarty' + + + + + diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java similarity index 91% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java rename to bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java index 5deb5232136c2..dea925a61c35f 100644 --- a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java +++ b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/TelegramReaderUtil.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.dsmr.internal; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.io.IOException; import java.io.InputStream; @@ -61,7 +61,9 @@ public static P1Telegram readTelegram(String telegramName, TelegramState expecte byte[] telegram = readRawTelegram(telegramName); P1TelegramParser parser = new P1TelegramParser(p1Telegram::set); - parser.parseData(telegram, 0, telegram.length); + parser.setLenientMode(true); + parser.parse(telegram, telegram.length); + assertNotNull("Telegram state should have been set. (Missing newline at end of message?)", p1Telegram.get()); assertEquals("Expected TelegramState should be as expected", expectedTelegramState, p1Telegram.get().getTelegramState()); return p1Telegram.get(); diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java similarity index 97% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java rename to bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java index ee4ff8cf0292b..ef2b4018f6647 100644 --- a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java +++ b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/DSMRSerialAutoDeviceTest.java @@ -99,7 +99,7 @@ public void handleErrorEvent(@NonNull DSMRConnectorErrorEvent connectorErrorEven try (InputStream inputStream = new ByteArrayInputStream(TelegramReaderUtil.readRawTelegram(TELEGRAM_NAME))) { when(mockSerialPort.getInputStream()).thenReturn(inputStream); DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener, - scheduler, 1); + new DSMRTelegramListener(), scheduler, 1); device.start(); assertSame("Expect to be starting discovery state", DeviceState.DISCOVER_SETTINGS, device.getState()); serialPortEventListener @@ -136,7 +136,7 @@ public void handleErrorEvent(@NonNull DSMRConnectorErrorEvent connectorErrorEven throw new PortInUseException(); }).when(mockIdentifier).open(eq(DSMRBindingConstants.DSMR_PORT_NAME), anyInt()); DSMRSerialAutoDevice device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener, - scheduler, 1); + new DSMRTelegramListener(), scheduler, 1); device.start(); assertSame("Expected an error", DSMRConnectorErrorEvent.IN_USE, eventRef.get()); assertSame("Expect to be in error state", DeviceState.ERROR, device.getState()); @@ -146,7 +146,8 @@ public void handleErrorEvent(@NonNull DSMRConnectorErrorEvent connectorErrorEven assertSame("Expect to be starting discovery state", DeviceState.DISCOVER_SETTINGS, device.getState()); // Trigger device to go into error stage with port doesn't exist. mockIdentifier = null; - device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener, scheduler, 1); + device = new DSMRSerialAutoDevice(serialPortManager, DUMMY_PORTNAME, listener, new DSMRTelegramListener(), + scheduler, 1); device.start(); assertSame("Expected an error", DSMRConnectorErrorEvent.DONT_EXISTS, eventRef.get()); assertSame("Expect to be in error state", DeviceState.ERROR, device.getState()); diff --git a/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypterTest.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypterTest.java new file mode 100644 index 0000000000000..ba71133639c76 --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/SmartyDecrypterTest.java @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2010-2018 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.dsmr.internal.device; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertThat; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.Test; +import org.openhab.binding.dsmr.internal.TelegramReaderUtil; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramListener; +import org.openhab.binding.dsmr.internal.device.p1telegram.P1TelegramParser; + +/** + * Test class for the {@link SmartyDecrypter}. + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public class SmartyDecrypterTest { + + private static final String KEY = "D491470F47126332B07D1923B3504188"; + private static final int[] TELEGRAM = new int[] { 0xDB, 0x08, 0x53, 0x41, 0x47, 0x67, 0x70, 0x01, 0xBD, 0x54, 0x82, + 0x02, 0x7A, 0x30, 0x00, 0x05, 0xA8, 0xE3, 0x80, 0x6E, 0xE6, 0xE6, 0x39, 0x27, 0x4C, 0x7B, 0xC5, 0x70, 0x95, + 0xF8, 0x72, 0xB0, 0x8D, 0xDE, 0x62, 0x1F, 0xB7, 0x4E, 0xE8, 0x1E, 0x5E, 0xBE, 0x34, 0x2C, 0x93, 0xD8, 0xE7, + 0x37, 0x81, 0xFB, 0x2A, 0x1E, 0xB8, 0x71, 0x00, 0x74, 0xA5, 0x4F, 0xC5, 0x7A, 0xA7, 0xD1, 0xD9, 0x92, 0x36, + 0xC4, 0x2E, 0x2E, 0xC0, 0x1A, 0x03, 0x24, 0xEF, 0xC7, 0xF0, 0x2E, 0x3B, 0xA2, 0xFA, 0x43, 0x19, 0x6C, 0xA6, + 0x03, 0x83, 0x8A, 0xB8, 0x32, 0x2D, 0xFF, 0xA8, 0x3F, 0x7E, 0x83, 0x93, 0x2E, 0x60, 0x07, 0x40, 0x6B, 0x11, + 0x2B, 0x41, 0x74, 0x5F, 0x38, 0x80, 0x56, 0x46, 0xD5, 0x3F, 0xEA, 0x7A, 0x02, 0x9F, 0xA7, 0x9C, 0x36, 0xD9, + 0xD7, 0x77, 0xDA, 0x8B, 0x5A, 0x03, 0xED, 0x3E, 0xC6, 0xE4, 0x85, 0xDE, 0xC0, 0xC9, 0x8D, 0x7D, 0x53, 0xC0, + 0xBD, 0x17, 0x5B, 0x22, 0x8E, 0x01, 0xF4, 0x66, 0xD9, 0x84, 0x37, 0x77, 0x80, 0x85, 0x03, 0xD6, 0x30, 0x15, + 0x8C, 0x82, 0x36, 0xD8, 0x68, 0x08, 0x8E, 0xB1, 0x39, 0x77, 0xBD, 0xFC, 0x47, 0x43, 0x67, 0xE5, 0x57, 0x13, + 0x6F, 0xEB, 0x6D, 0x53, 0x7F, 0x18, 0x30, 0x44, 0x84, 0xA0, 0x14, 0xB4, 0x90, 0x99, 0xA6, 0x3C, 0x18, 0xD4, + 0x30, 0x3A, 0x4C, 0x53, 0x85, 0x3A, 0x0C, 0xA8, 0xC5, 0xB0, 0xE3, 0xE8, 0x05, 0xBF, 0xD8, 0x42, 0x54, 0x10, + 0xFA, 0xB5, 0x22, 0xB3, 0x5A, 0x8F, 0x25, 0x8B, 0x57, 0xBB, 0x1E, 0x97, 0xE2, 0x48, 0x9B, 0x6F, 0x37, 0x07, + 0x17, 0x53, 0xAF, 0xFB, 0xB4, 0xEE, 0xBC, 0xA5, 0x02, 0xEE, 0xA5, 0x92, 0xB8, 0x80, 0x85, 0xAE, 0x15, 0x5B, + 0xCE, 0xA8, 0xF7, 0x32, 0xD6, 0xB0, 0x11, 0x5B, 0xB9, 0xCE, 0xD7, 0x6B, 0xE5, 0xB8, 0x93, 0xB2, 0xA7, 0xEF, + 0xC8, 0x16, 0x3C, 0x17, 0x1E, 0x77, 0xC3, 0xD2, 0x9A, 0x72, 0xB7, 0x47, 0x8E, 0xD6, 0xDF, 0xE2, 0xC4, 0x8B, + 0xF3, 0xF9, 0xD8, 0xF8, 0x95, 0xCA, 0x1C, 0xB4, 0x2E, 0xAA, 0xC4, 0xCB, 0x21, 0xD6, 0xEA, 0xB5, 0x1E, 0x77, + 0xE4, 0xD6, 0x02, 0x9D, 0x78, 0x84, 0x27, 0xDD, 0x5B, 0xFC, 0x46, 0xBD, 0xD3, 0xE8, 0xA3, 0x2D, 0xBB, 0x6F, + 0x93, 0xB4, 0x84, 0x2B, 0x07, 0x3E, 0x9B, 0x6F, 0xE6, 0xE5, 0xDF, 0xC0, 0x58, 0xB5, 0xF4, 0x54, 0xAA, 0x3E, + 0xF1, 0x62, 0xBD, 0xF9, 0x3C, 0xB1, 0xE0, 0xC4, 0x52, 0xDB, 0xB2, 0xBE, 0x4C, 0xB4, 0xC6, 0x7D, 0x16, 0x9E, + 0x2A, 0x30, 0x61, 0x8F, 0xA2, 0x16, 0x54, 0x41, 0xB6, 0xD3, 0xC2, 0x2F, 0x1C, 0x36, 0x27, 0xE3, 0x5F, 0xFF, + 0xCF, 0x9F, 0x19, 0x47, 0xD7, 0xA6, 0xAE, 0x94, 0x2F, 0xC2, 0x1E, 0x24, 0x6F, 0x0E, 0xFC, 0x45, 0x6A, 0x78, + 0x89, 0xC9, 0x61, 0xC9, 0x3E, 0xA0, 0x89, 0xEE, 0xF6, 0xD1, 0xA4, 0x40, 0x56, 0xBA, 0xAA, 0xB3, 0x52, 0xCB, + 0xEA, 0x7D, 0x7E, 0x20, 0x67, 0x57, 0xAF, 0x0D, 0x42, 0x70, 0x64, 0xB0, 0x58, 0xD5, 0x72, 0x35, 0xA7, 0x8F, + 0x8D, 0xB9, 0x1C, 0xE4, 0xD6, 0x0A, 0x3C, 0x6B, 0xAB, 0xD9, 0x9A, 0x61, 0x16, 0x9B, 0x17, 0x2F, 0x24, 0x14, + 0xF5, 0x65, 0xBD, 0xE0, 0x23, 0x96, 0x54, 0xEA, 0xC7, 0x75, 0x52, 0xFB, 0xCC, 0x2A, 0x2F, 0xA1, 0x02, 0xD7, + 0xCD, 0x00, 0x3D, 0xDB, 0xEB, 0x9C, 0x1F, 0x81, 0x1D, 0xBC, 0x69, 0x53, 0x76, 0x33, 0x6E, 0x2E, 0x9D, 0x6C, + 0xFC, 0x73, 0xFC, 0xB4, 0xA2, 0x94, 0x46, 0xBD, 0x7C, 0xE3, 0x17, 0x0E, 0x58, 0x56, 0x76, 0x6B, 0x25, 0xE8, + 0x20, 0x49, 0xEE, 0x55, 0x08, 0xEC, 0x18, 0x16, 0x68, 0x5F, 0x1D, 0xCE, 0xC2, 0x38, 0x19, 0x12, 0x16, 0xDF, + 0xFB, 0xA0, 0x64, 0x7D, 0x32, 0xA1, 0x55, 0x9D, 0x99, 0x64, 0x0A, 0x15, 0xF2, 0x4A, 0xB0, 0x21, 0x72, 0xDB, + 0x3D, 0x56, 0x44, 0x54, 0xE7, 0xA0, 0x34, 0x6C, 0x09, 0x93, 0x99, 0xB8, 0x77, 0x0A, 0x9B, 0xDA, 0xA6, 0x19, + 0xC7, 0xC5, 0x23, 0x05, 0x26, 0xD6, 0xB8, 0x04, 0x99, 0xA0, 0xEE, 0x0C, 0xD4, 0xFC, 0x46, 0xA6, 0x77, 0xAF, + 0x68, 0xEF, 0x4D, 0xEE, 0x9E, 0x76, 0xB7, 0x8A, 0xC8, 0xA0, 0x4F, 0x43, 0x1D, 0xB3, 0x98, 0x6F, 0xCF, 0xD4, + 0x5E, 0xE1, 0xF1, 0x07, 0x99, 0xAF, 0x0B, 0x79, 0x4A, 0x41, 0x03, 0x73, 0x5D, 0x6D, 0xDA, 0x38, 0x29, 0x17, + 0x3F, 0x80, 0xF1, 0x38, 0xA5, 0x30, 0x1A, 0xFD, 0xF1, 0xDB, 0x87, 0x74, 0xF0, 0xC4, 0x0E, 0x6E, 0x71, 0x26, + 0x4B, 0x33, 0x4A, 0x94, 0x3F, 0xC1, 0x05, 0x52, 0xB0, 0x75, 0x2F, 0x3A, 0x0D, 0x7F, 0xD8, 0x04, 0x58, 0x59, + 0x54, 0x08, 0xF2, 0x85, 0x0F, 0x17, }; + + /** + * Tests decrypting of a single complete Smarty telegram. + */ + @Test + public void testSmartyDecrypter() { + AtomicReference telegramResult = new AtomicReference(""); + P1TelegramListener telegramListener = telegram -> telegramResult.set(telegram.getRawTelegram()); + SmartyDecrypter decoder = new SmartyDecrypter(new P1TelegramParser(telegramListener), + new DSMRTelegramListener(KEY), KEY); + decoder.setLenientMode(true); + byte[] data = new byte[TELEGRAM.length]; + + for (int i = 0; i < TELEGRAM.length; i++) { + data[i] = (byte) TELEGRAM[i]; + } + + decoder.parse(data, data.length); + String expected = new String(TelegramReaderUtil.readRawTelegram("smarty"), StandardCharsets.UTF_8); + + assertThat("Should have correctly decrypted the telegram", telegramResult.get(), is(equalTo(expected))); + } +} diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java similarity index 88% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java rename to bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java index 5f44b0f49a2ea..1b099f3569db1 100644 --- a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java +++ b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/device/p1telegram/P1TelegramParserTest.java @@ -45,7 +45,7 @@ public static final List data() { { "Landis_Gyr_E350", 10, }, { "Landis_Gyr_ZCF110", 25, }, { "Sagemcom_XS210", 41, }, - + { "smarty", 24, }, }); } // @formatter:on @@ -59,7 +59,8 @@ public static final List data() { @Test public void testParsing() { P1Telegram telegram = TelegramReaderUtil.readTelegram(telegramName, TelegramState.OK); + assertEquals("Should not have any unknown cosem objects", 0, telegram.getUnknownCosemObjects().size()); assertEquals("Expected number of objects", numberOfCosemObjects, - telegram.getCosemObjects().stream().mapToInt(o -> o.getCosemValues().size()).sum()); + telegram.getCosemObjects().stream().mapToInt(co -> co.getCosemValues().size()).sum()); } } diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java similarity index 82% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java rename to bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java index d5deff9205769..906f8d98af10c 100644 --- a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java +++ b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDetectorTest.java @@ -56,6 +56,7 @@ public static final List data() { { "Landis_Gyr_E350", EnumSet.of( DEVICE_V2_V3, ELECTRICITY_V3_0)}, { "Landis_Gyr_ZCF110", EnumSet.of( DEVICE_V4, ELECTRICITY_V4_2, M3_V5_0)}, { "Sagemcom_XS210", EnumSet.of( DEVICE_V4, ELECTRICITY_V4_2)}, + { "smarty", EnumSet.of( DEVICE_V5, ELECTRICITY_SMARTY_V1_0)}, }); } // @formatter:on @@ -71,15 +72,17 @@ public void testDetectMeters() { P1Telegram telegram = TelegramReaderUtil.readTelegram(telegramName, TelegramState.OK); DSMRMeterDetector detector = new DSMRMeterDetector(); Entry, Map> entry = detector - .detectMeters(telegram); + .detectMeters(telegram); Collection detectMeters = entry.getKey(); - assertEquals("Should detect correct number of meters", expectedMeters.size(), detectMeters.size()); + assertEquals("Should detect correct number of meters: " + Arrays.toString(detectMeters.toArray()), + expectedMeters.size(), detectMeters.size()); assertEquals("Should not have any undetected cosem objects", 0, entry.getValue().size()); + assertEquals("Should not have any unknown cosem objects", 0, telegram.getUnknownCosemObjects().size()); for (DSMRMeterType meter : expectedMeters) { assertEquals( - String.format("Meter '%s' not found: %s", meter, - Arrays.toString(detectMeters.toArray(new DSMRMeterDescriptor[0]))), - 1, detectMeters.stream().filter(e -> e.getMeterType() == meter).count()); + String.format("Meter '%s' not found: %s", meter, + Arrays.toString(detectMeters.toArray(new DSMRMeterDescriptor[0]))), + 1, detectMeters.stream().filter(e -> e.getMeterType() == meter).count()); } } diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryServiceTest.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryServiceTest.java similarity index 100% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryServiceTest.java rename to bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/discovery/DSMRMeterDiscoveryServiceTest.java diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterTest.java b/bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterTest.java rename to bundles/org.openhab.binding.dsmr/src/test/java/org/openhab/binding/dsmr/internal/meter/DSMRMeterTest.java diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/.gitattributes b/bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/.gitattributes similarity index 100% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/.gitattributes rename to bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/.gitattributes diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/Iskra_AM550.telegram b/bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/Iskra_AM550.telegram similarity index 100% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/Iskra_AM550.telegram rename to bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/Iskra_AM550.telegram diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/Landis_Gyr_E350.telegram b/bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/Landis_Gyr_E350.telegram similarity index 100% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/Landis_Gyr_E350.telegram rename to bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/Landis_Gyr_E350.telegram diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/Landis_Gyr_ZCF110.telegram b/bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/Landis_Gyr_ZCF110.telegram similarity index 100% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/Landis_Gyr_ZCF110.telegram rename to bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/Landis_Gyr_ZCF110.telegram diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/Sagemcom_XS210.telegram b/bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/Sagemcom_XS210.telegram similarity index 100% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/Sagemcom_XS210.telegram rename to bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/Sagemcom_XS210.telegram diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/ace4000.telegram b/bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/ace4000.telegram similarity index 100% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/ace4000.telegram rename to bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/ace4000.telegram diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/dsmr_40.telegram b/bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/dsmr_40.telegram similarity index 100% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/dsmr_40.telegram rename to bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/dsmr_40.telegram diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/dsmr_42.telegram b/bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/dsmr_42.telegram similarity index 100% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/dsmr_42.telegram rename to bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/dsmr_42.telegram diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/dsmr_50.telegram b/bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/dsmr_50.telegram similarity index 100% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/dsmr_50.telegram rename to bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/dsmr_50.telegram diff --git a/bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/smarty.telegram b/bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/smarty.telegram new file mode 100644 index 0000000000000..cec79406ede19 --- /dev/null +++ b/bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/smarty.telegram @@ -0,0 +1,31 @@ +/Lux5\253663629_D + +1-3:0.2.8(42) +0-0:1.0.0(180130102122W) +0-0:42.0.0(53414731303330373030313134303034) +1-0:1.8.0(000006.695*kWh) +1-0:2.8.0(000000.025*kWh) +1-0:3.8.0(000000.818*kvarh) +1-0:4.8.0(000002.745*kvarh) +1-0:1.7.0(00.000*kW) +1-0:2.7.0(00.000*kW) +1-0:3.7.0(00.000) +1-0:4.7.0(00.007) +0-0:17.0.0(77.376) +0-0:96.3.10(1) +0-0:96.7.21(00069) +1-0:32.32.0(00044) +1-0:52.32.0(00003) +1-0:72.32.0(00002) +1-0:32.36.0(00000) +1-0:52.36.0(00000) +1-0:72.36.0(00000) +0-0:96.13.0() +0-0:96.13.2() +0-0:96.13.3() +0-0:96.13.4() +0-0:96.13.5() +1-0:31.7.0(000*A) +1-0:51.7.0(000*A) +1-0:71.7.0(000*A) +!CFDE diff --git a/addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/unregistered_meter.telegram b/bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/unregistered_meter.telegram similarity index 100% rename from addons/binding/org.openhab.binding.dsmr.test/src/test/resources/org/openhab/binding/dsmr/internal/unregistered_meter.telegram rename to bundles/org.openhab.binding.dsmr/src/test/resources/org/openhab/binding/dsmr/internal/unregistered_meter.telegram diff --git a/bundles/org.openhab.binding.enocean/.classpath b/bundles/org.openhab.binding.enocean/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.enocean/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.enocean/.project b/bundles/org.openhab.binding.enocean/.project new file mode 100644 index 0000000000000..acd074c37c313 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.enocean + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.chromecast.test/NOTICE b/bundles/org.openhab.binding.enocean/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.chromecast.test/NOTICE rename to bundles/org.openhab.binding.enocean/NOTICE diff --git a/bundles/org.openhab.binding.enocean/README.md b/bundles/org.openhab.binding.enocean/README.md new file mode 100644 index 0000000000000..4bbf8884a449f --- /dev/null +++ b/bundles/org.openhab.binding.enocean/README.md @@ -0,0 +1,355 @@ +# EnOcean Binding + +The EnOcean binding connects openHAB to the EnOcean ecosystem. + +The binding uses an EnOcean gateway to retrieve sensor data and control actuators. +For _bidirectional_ actuators it is even possible to update the openHAB item state if the actuator gets modified outside of openHAB. +This binding has been developed on an USB300 gateway and was also tested with an EnOceanPi. +As this binding implements a full EnOcean stack, we have full control over these gateways. +This binding can enable the repeater function (level 1 or 2) of these gateways and retrieve detailed information about them. + +## Concepts/Configuration + +First of all you have to configure an EnOcean transceiver (gateway). +A directly connected USB300 can be auto discovered, an EnOceanPi has to be added manually to openHAB. +Both gateways are represented by an _EnOcean gateway_ in openHAB. +If you want to place the gateway for better reception apart from your openHAB server, you can forward its serial messages over TCP/IP (_ser2net_). In this case you have to define the path to the gateway like this rfc2217://x.x.x.x:3001. +If everything is running fine you should see the _base id_ of your gateway in the properties of your bridge. + +The vast majority of EnOcean messages are sent as broadcast messages without an explicit receiver address. +However each EnOcean device is identified by an unique id, called EnOceanId, which is used as the sender address in these messages. +To receive messages from an EnOcean device you have to determine its EnOceanId and add an appropriate thing to openHAB. + +If the device is an actuator which you want to control with your gateway from openHAB, you also have to create an unique sender id and announce it to the actuator (_teach-in_). +For security reasons you cannot choose a random id, instead each gateway has 127 unique ids build in, from which you can choose. +A SenderId of your gateway is made up its base id and a number between 1 and 127. +This number can be chosen manually or the next free/unused number can be determined by the binding. + +## Supported Things + +This binding is developed on and tested with the following devices + + * USB300 and EnOceanPi gateways + * The following Eltako actuators: + * FSR14 (light switch) + * FSB14 (rollershutter) + * FUD14 (dimmer) + * FSSA-230V (smart plug) + * FWZ12-65A (energy meter) + * FTKE (window / door contact) + * TF-FGB (window handle) + * TF-FKB (window contact) + * TF-AHDSB (outdoor brightness sensor) + * FAFT60 (outdoor temperature & humidity sensor) + * The following Opus actuators: + * GN-A-R12V-SR-4 (light) + * GN-A-R12V-MF-2 (light) + * GN-A-R12V-LZ/UD (dimmer) + * GN-A-R12V-JRG-2 (rollershutter) + * GN-A-U230V-JRG (rollershutter) + * OPUS-FUNK PLUS Jalousieaktor 1fach UP (rollershutter) + * OPUS-Funk PLUS Steckdosenleiste (smart multiple socket) + * NodOn: + * Smart Plug (ASP-2-1-10) + * In Wall Switch (SIN-2-2-00, SIN-2-1-0x) + * Temperature & humidity sensor (STPH-2-1-05) + * Permundo + * PSC234 (smart plug with metering) = Afriso APR234 + * PSC132 (smart switch actor with metering) + * PSC152 (smart blinds control) + * Thermokon SR04 room control + * Hoppe SecuSignal window handles + * Rocker switches (NodOn, Eltako FT55 etc) + +However, because of the standardized EnOcean protocol it is more important which EEP this binding supports. +Hence if your device supports one of the following EEPs the chances are good that your device is also supported by this binding. + +|Thing type | EEP family | EEP Types | Channels¹ | Devices² | Pairing | +|---------------------------------|-------------|---------------|------------------------------|--------------------------------|-----------| +| bridge | - | - | repeaterMode, setBaseId | USB300, EnOceanPi | - | +| pushButton | F6-01 | 0x01 | pushButton | | Manually | +| rockerSwitch | F6-02 | 0x01-02 | rockerswitchA, rockerswitchB | Eltako FT55 | Discovery | +| mechanicalHandle | F6-10 | 0x00-01 | windowHandleState, contact | Hoppe SecuSignal handles, Eltako TF-FGB | Discovery | +| contact | D5-00 | 0x01 | contact | Eltako FTK(E) & TF-FKB | Discovery | +| temperatureSensor | A5-02 | 0x01-30 | temperature | Thermokon SR65 | Discovery | +| temperatureHumiditySensor | A5-04 | 0x01-03 | humidity, temperature | Eltako FTSB | Discovery | +| occupancySensor | A5-07 | 0x01-03 | illumination, batteryVoltage,
motionDetection | NodON PIR-2-1-01 | Discovery | +| lightTemperatureOccupancySensor | A5-08 | 0x01-03 | illumination, temperature,
occupancy, motionDetection | Eltako FABH | Discovery | +| lightSensor | A5-06 | 0x01 | illumination | Eltako TF-AHDSB | Discovery | +| roomOperatingPanel | A5-10 | 0x01-23 | temperature, setPoint, fanSpeedStage,
occupancy | Thermokon SR04 | Discovery | +| automatedMeterSensor | A5-12 | 0x00-03 | counter, currentNumber, instantpower,
totalusage, amrLitre, amrCubicMetre | FWZ12 | Discovery | +| centralCommand | A5-38 | 0x08 | dimmer, generalSwitch | Eltako FUD14, FSR14 | Teach-in | +| rollershutter | A5-3F/D2-05/A5-38 | 0x7F/00/08 | rollershutter | Eltako FSB14, NodOn SIN-2-RS-01| Teach-in/Discovery | +| measurementSwitch | D2-01 | 0x00-0F,11,12 | generalSwitch(/A/B), instantpower,
totalusage, repeaterMode | NodOn In Wall Switch | Discovery | +| classicDevice | F6-02 | 0x01-02 | virtualRockerswitchA, virtualRockerswitchB | - | Teach-in | + +¹ Not all channels are supported by all devices, it depends which specific EEP type is used by the device, all thing types additionally support `rssi`, `repeatCount` and `lastReceived` channels + +² These are just examples of supported devices + +Furthermore following supporting EEP family is available too: A5-11, types 0x03 (rollershutter position status) and 0x04 (extended light status). + +A `rockerSwitch` is used to receive messages from a physical EnOcean Rocker Switch. +A `classicDevice` is used to for older EnOcean devices which react only on rocker switch messages (like Opus GN-A-R12V-SR-4). +As these devices do not send their current status, you have to add additional listener channels for each physical Rocker Switch to your thing. +In this way you can still sync your item status with the physical status of your device whenever it gets modified by a physical rocker switch. +The classic device simulates a physical Rocker Switch. +Per default the classic device uses the channel A of the simulated rocker switch. +If you want to use channel B you have to use virtualRockerswitchB in conjunction with rules to send commands. + +## Pairing + +Most of the EnOcean devices can be automatically created and configured as an openHAB thing through the discovery service. +The EnOcean protocol defines a so called "teach-in" process to announce the abilities and services of an EnOcean device and pair devices. +To pair an EnOcean device with its openHAB thing representation, you have to differentiate between sensors and actuators. + +### Sensors + +To pair a sensor with its thing, you first have to start the discovery scan for this binding in PaperUI. +Then press the "teach-in" button of the sensor. +The sensor sends a teach-in message which contains the information about the EEP and the EnOceanId of the sensor. +If the EEP is known by this binding the thing representation of the device is created. +The corresponding channels are created dynamically, too. + +### Actuators + +If the actuator supports UTE teach-in, the corresponding thing can be created and paired automatically. +First you have to **start the discovery scan for a gateway** in PaperUI. +Then press the teach-in button of the actuator. +If the EEP of the actuator is known, the binding sends an UTE teach-in response with a new SenderId and creates a new thing with its channels. + +If the actuator does not support UTE teach-ins, you have to create, configure and choose the right EEP of the thing manually. +It is important to link the teach-in channel of this thing to a switch item. +Afterwards you have to **activate the pairing mode of the actuator**. +Then switch on the teach-in item to send a teach-in message to the actuator. +If the pairing was successful, you can control the actuator and unlink the teach-in channel now. +The content of this teach-in message is device specific and can be configured through the teach-in channel. + +To pair a classicDevice with an EnOcean device, you first have to activate the pairing mode of the actuator. +Then switch the virtualRockerSwitchA On/Off. + +Each EnOcean gateway supports 127 unique SenderIds. +The SenderId of a thing can be set manually or determined automatically by the binding. +In case of an UTE teach-in the next unused SenderId is taken automatically. +To set this SenderId to a specific one, you have to use the nextSenderId parameter of your gateway. + +## Thing Configuration + +The pairing process of an openHAB thing and an EnOcean device has to be triggered within Paper UI. +Therefore if you do not want to use Paper UI, a mixed mode configuration approach has to be done. +To determine the EEP and EnOceanId of the device and announce a SenderId to it, you first have to pair an openHAB thing with the EnOcean device. +Afterwards you can delete this thing and manage it with its necessary parameters through a configuration file. +If you change the SenderId of your thing, you have to pair again the thing with your device. + +|Thing type | Parameter | Meaning | Possible Values | +|---------------------------------|-------------------|-----------------------------|---| +| bridge | path | Path to the EnOcean Gateway | COM3, /dev/ttyAMA0, rfc2217://x.x.x.x:3001 | +| | nextSenderId | Set SenderId of next created thing.
If omitted, the next unused SenderId is taken | 1-127 | +| pushButton | receivingEEPId | EEP used for receiving msg | F6_01_01 | +| | enoceanId | EnOceanId of device this thing belongs to | hex value as string | +| rockerSwitch | receivingEEPId | | F6_02_01, F6_02_02 | +| | enoceanId | | | +| mechanicalHandle | receivingEEPId | | F6_10_00, F6_10_01, A5_14_09 | +| | enoceanId | | | +| contact | receivingEEPId | | D5_00_01, A5_14_01_ELTAKO | +| | enoceanId | | | +| temperatureSensor | receivingEEPId | | A5_02_01-0B, A5_02_10-1B, A5_02_20, A5_02_30 | +| | enoceanId | | | +| temperatureHumiditySensor | receivingEEPId | | A5_04_01-03 | +| | enoceanId | | | +| occupancySensor | receivingEEPId | | A5_07_01-03 | +| | enoceanId | | | +| lightTemperatureOccupancySensor | receivingEEPId | | A5_08_01-03, A5_08_01_FXBH | +| | enoceanId | | | +| lightSensor | receivingEEPId | | A5_06_01, A5_06_01_ELTAKO | +| | enoceanId | | | +| roomOperatingPanel | receivingEEPId | | A5_10_01-0D, A5_10_10-1F, A5_10_20-23 | +| | enoceanId | | | +| automatedMeterSensor | receivingEEPId | | A5_12_00-03 | +| | enoceanId | | | +| centralCommand | senderIdOffset | SenderId used for sending msg.
If omitted, nextSenderId of bridge is used | 1-127 | +| | enoceanId | | | +| | sendingEEPId | EEP used for sending msg | A5_38_08_01, A5_38_08_02 | +| | broadcastMessages | Send broadcast or addressed msg | true, false | +| | receivingEEPId | | F6_00_00, A5_38_08_02, A5_11_04 | +| | suppressRepeating | Suppress repeating of msg | true, false | +| rollershutter | senderIdOffset | | 1-127 | +| | enoceanId | | | +| | sendingEEPId | | A5_3F_7F_EltakoFSB, A5_38_08_07, D2_05_00 | +| | broadcastMessages | | true, false | +| | receivingEEPId¹ | | A5_3F_7F_EltakoFSB, A5_11_03, D2_05_00 | +| | suppressRepeating | | true, false | +| | pollingInterval | Refresh interval in seconds | Integer | +| measurementSwitch | senderIdOffset | | 1-127 | +| | enoceanId | | | +| | sendingEEPId | | D2_01_00-0F, D2_01_11, D2_01_12,
D2_01_09_PERMUNDO, D2_01_0F_NODON, D2_01_12_NODON | +| | receivingEEPId¹ | | D2_01_00-0F, D2_01_11, D2_01_12,
D2_01_09_PERMUNDO, D2_01_0F_NODON, D2_01_12_NODON,
A5_12_01 | +| | broadcastMessages | | true, false | +| | pollingInterval | | Integer | +| | suppressRepeating | | true, false | +| classicDevice | senderIdOffset | | 1-127 | +| | sendingEEPId | | F6_02_01, F6_02_02 | +| | broadcastMessages | | true, false | +| | receivingEEPId | | F6_02_01, F6_02_02 | +| | suppressRepeating | | true, false | + +¹ multiple values possible, EEPs have to be of different EEP families. +If you want to receive messages of your EnOcean devices you have to set **the enoceanId to the EnOceanId of your device**. + +## Channels + +The channels of a thing are determined automatically based on the chosen EEP. + +|Channel | Item | Description | +|---------------------|--------------------|---------------------------------| +| repeaterMode | String | Set repeater level to 1, 2 or disable | +| setBaseId | String | Changes the BaseId of your gateway. This can only be done 10 times! So use it with care. | +| pushButton | Trigger | Channel type system:rawbutton, emits PRESSED and RELEASED events | +| rockerswitchA/B | Trigger | Channel type system:rawrocker, emits DIR1_PRESSED, DIR1_RELEASED, DIR2_PRESSED, DIR2_RELEASED events | +| windowHandleState | String | Textual representation of handle position (OPEN, CLOSED, TILTED) | +| contact | Contact | State OPEN/CLOSED (tilted handle => OPEN) | +| temperature | Number:Temperature | Temperature in degree Celsius | +| humidity | Number | Relative humidity level in percentages | +| illumination | Number:Illuminance | Illumination in lux | +| occupancy | Switch | Occupancy button pressed (ON) or released (OFF) | +| motionDetection | Switch | On=Motion detected, Off=not | +| setPoint | Number | linear set point | +| fanSpeedStage | String | Fan speed: -1 (Auto), 0, 1, 2, 3 | +| dimmer | Dimmer | Dimmer value in percent | +| generalSwitch(/A/B) | Switch | Switch something (channel A/B) ON/OFF | +| rollershutter | Rollershutter | Shut time (shutTime) in seconds can be configured | +| angle | Number:Angle | The angle for blinds | +| instantpower | Number:Power | Instant power consumption in Watts | +| totalusage | Number:Energy | Used energy in Kilowatt hours | +| teachInCMD | Switch | Sends a teach-in msg, content can configured with parameter teachInMSG | +| virtualSwitchA | Switch | Used to convert switch item commands into rocker switch messages (channel A used)
Time in ms between sending a pressed and release message can be defined with channel parameter duration.
The switch mode (rocker switch: use DIR1 and DIR2, toggle: use just one DIR) can be set with channel parameter switchMode (rockerSwitch, toggleButtonDir1, toggleButtonDir2) | +| virtualRollershutterA | Rollershutter | Used to convert rollershutter item commands into rocker switch messages (channel A used) | +| rockerswitchListenerSwitch | Switch | Used to convert rocker switch messages into switch item state updates | +| rockerswitchListenerRollershutter | Rollershutter | Used to convert rocker switch messages into rollershutter item state updates | +| virtualRockerswitchB | String | Used to send plain rocker switch messages (channel B used) | +| batteryVoltage | Number:ElectricPotential | Battery voltage for things with battery | +| energyStorage | Number:ElectricPotential | Energy storage, don't know what this means... | +| rssi | Number | Received Signal Strength Indication (dBm) of last received message | +| repeatCount | Number | Number of repeaters involved in the transmission of the telegram | +| lastReceived | DateTime | Date and time the last telegram was received | + +Items linked to bi-directional actuators (actuator sends status messages back) should always disable the `autoupdate`. +This is especially true for Eltako rollershutter, as their position is calculated out of the current position and the moving time. + +## Channel Configuration + +Some channels can be configured with parameters. + +| Channel type | Parameter | Meaning | Possible values | +|---------------|----------------|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------| +| rollershutter | shutTime | Time (in seconds) to completely close the rollershutter | | +| dimmer | rampingTime | Duration of dimming | A5-38-08: Ramping Time (in seconds), 0 = default ramping, 1..255 = seconds to 100%; D2-01-01: 0 = switch, 1-3 = timer 1-3, 4 = stop | +| | eltakoDimmer | Flag for Eltako dimmers, because Eltako does interpret this EEP differently | True for Eltako dimmer, false otherwise. Defaults to true for compatibility purpose. | +| | storeValue | Store final value. For Eltako devices, block dimming value. | True or false. Defaults to false. | +| teachInCMD | manufacturerId | Id is used for 4BS teach in with EEP | HEX | +| | teachInMSG | Use this message if teach in type and/or manufacturer id are unknown | HEX | +| totalusage | validateValue | Filter out increases more than 10.0 kWh and decreases less than 1.0 kWh | true / false | + +Possible declaration in Thing DSL: + +```xtend +Thing centralCommand 11223344 "Light" @ "Living room" [ enoceanId="11223344", senderIdOffset=15, sendingEEPId="A5_38_08_02", receivingEEPId="A5_38_08_02" ] { + Channels: + Type teachInCMD : teachInCMD [ teachInMSG="E0400D80" ] + Type dimmer : dimmer [ rampingTime=0 ] +} +``` + +## Rules and Profiles + +The rockerSwitch things use _system:rawrocker_ channel types. +So they trigger _DIR1[/2]_\__PRESSED_ and DIR1[/2]_\__RELEASED_ events. +These channels can be directly linked to simple items like Switch, Dimmer or Player with the help of _profiles_. +If you want to do more advanced stuff, you have to implement rules which react to these events + +```xtend +rule "Advanced rocker rule" +when + Channel 'enocean:rockerSwitch:gtwy:AABBCC00:rockerswitchA' triggered DIR1_PRESSED +then + // do some advanced stuff +end +``` + +## Example + +```xtend +Bridge enocean:bridge:gtwy "EnOcean Gateway" [ path="/dev/ttyAMA0" ] { + Thing rockerSwitch rs01 "Rocker" @ "Kitchen" [ enoceanId="aabbcc01", receivingEEPId="F6_02_01" ] + Thing mechanicalHandle mh01 "Door handle" @ "Living room" [ enoceanId="aabbcc02", receivingEEPId="F6_10_00" ] + Thing roomOperatingPanel p01 "Panel" @ "Floor" [ enoceanId="aabbcc03", receivingEEPId="A5_10_06" ] + Thing centralCommand cc01 "Light" @ "Kitchen" [ enoceanId="aabbcc04", senderIdOffset=1, sendingEEPId="A5_38_08_01", receivingEEPId="F6_00_00", broadcastMessages=true, suppressRepeating=false ] + Thing centralCommand cc02 "Dimmer" @ "Living room" [ enoceanId="aabbcc05", senderIdOffset=2, sendingEEPId="A5_38_08_02", receivingEEPId="A5_38_08_02", broadcastMessages=true, suppressRepeating=false ] + Thing rollershutter r01 "Rollershutter" @ "Kitchen" [ enoceanId="aabbcc06", senderIdOffset=3, sendingEEPId="A5_3F_7F_EltakoFSB", receivingEEPId="A5_3F_7F_EltakoFSB", broadcastMessages=true, suppressRepeating=false ] {Channels: Type rollershutter:rollershutter [shutTime=25]} + Thing measurementSwitch ms01 "TV Smart Plug" @ "Living room" [ enoceanId="aabbcc07", senderIdOffset=4, sendingEEPId="D2_01_09", broadcastMessages=false, receivingEEPId="D2_01_09","A5_12_01", suppressRepeating=false, pollingInterval=300] + Thing classicDevice cd01 "Garage_Light" @ "Garage" [ + senderIdOffset=5, + sendingEEPId="F6_02_01", + broadcastMessages=true, + receivingEEPId="F6_02_01", + suppressRepeating=false + ] { + Type virtualSwitchA : virtualSwitchA [duration=300, switchMode="rockerSwitch"] + Type rockerswitchListenerSwitch : Listener1 "Schalter links" [enoceanId="aabbcc08", channel="channelA", switchMode="toggleButtonDir1"] + Type rockerswitchListenerSwitch : Listener2 "Schalter rechts" [enoceanId="aabbcc09", channel="channelB", switchMode="toggleButtonDir2"] + } +} +``` + +```xtend +Player Kitchen_Sonos "Sonos" (Kitchen) {channel="sonos:PLAY1:ID:control", channel="enocean:rockerSwitch:gtwy:rs01:rockerswitchA" [profile="system:rawrocker-to-play-pause"]} +Dimmer Kitchen_Hue "Hue" {channel="enocean:rockerSwitch:gtwy:rs01:rockerswitchB" [profile="system:rawrocker-to-dimmer"], channel="hue:0220:0017884f6626:9:brightness"} +Rollershutter Kitchen_Rollershutter "Roller shutter" (Kitchen) {channel="enocean:rollershutter:gtwy:r01:rollershutter", autoupdate="false"} +Switch Garage_Light "Switch" { + channel="enocean:classicDevice:gtwy:cd01:virtualRockerswitchA", + channel="enocean:classicDevice:gtwy:cd01:Listener1", + channel="enocean:classicDevice:gtwy:cd01:Listener2" +} +``` + +## Generic Things + +If an EnOcean device uses an unsupported EEP or _A5-3F-7F_, you have to create a `genericThing`. +Generic things support all channels like switch, number, string etc as generic channels. +However you have to specify how to convert the EnOcean messages of the device into openHAB state updates and how to convert the openHAB commands into EnOcean messages. +These conversion functions can be defined with the help of transformation functions like MAP. + +|Thing type | Parameter | Meaning | Possible Values | +|---------------------------------|-------------------|----------------------------|---| +| genericThing | senderIdOffset | | 1-127 | +| | enoceanId | EnOceanId of device this thing belongs to | hex value as string | +| | sendingEEPId | EEP used for sending msg | F6_FF_FF, A5_FF_FF, D2_FF_FF | +| | receivingEEPId | EEP used for receiving msg | F6_FF_FF, A5_FF_FF, D2_FF_FF | +| | broadcastMessages | | true, false | +| | suppressRepeating | | true, false | + +Supported channels: genericSwitch, genericRollershutter, genericDimmer, genericNumber, genericString, genericColor, genericTeachInCMD. +You have to define the transformationType (e.g. MAP) and transformationFunction (e.g. for MAP: file name of mapping file) for each of these channels. + +For an inbound transformation (EnOcean message => openHAB state) you receive the channel id and the EnOcean data in hex separated by a pipe. +Your transformation function has to return the openHAB State type and value separated by a pipe. +If you want to use a mapping transformation, your mapping file has to look like this for a genericThing using EEP F6_FF_FF: + +``` +ChannelId|EnoceanData(Hex)=openHABState|Value +genericSwitch|70=OnOffType|ON +genericSwitch|50=OnOffType|OFF +genericRollershutter|70=PercentType|0 +genericRollershutter|50=PercentType|100 +``` + +For an outbound transformation (openHAB command => EnOcean message) you receive the channel id and the command separated by a pipe. +Your transformation function has to return the payload of the EnOcean message. +You do not have to worry about CRC and header data. +If you want to use a mapping transformation, your mapping file has to look like this for a genericThing using EEP A5_FF_FF: + +``` +ChannelId|openHABCommand=EnoceanData(Hex) +genericSwitch|ON=01000009 +genericSwitch|OFF=01000008 +``` diff --git a/bundles/org.openhab.binding.enocean/pom.xml b/bundles/org.openhab.binding.enocean/pom.xml new file mode 100644 index 0000000000000..c500731d0fced --- /dev/null +++ b/bundles/org.openhab.binding.enocean/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + + org.openhab.addons.reactor.bundles + org.openhab.addons.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.enocean + + openHAB Add-ons :: Bundles :: EnOcean Binding + + diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java similarity index 83% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java index 57e9988a4c0a5..5f04d1a8a5f63 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanBindingConstants.java @@ -53,6 +53,7 @@ public class EnOceanBindingConstants { "temperatureHumiditySensor"); public final static ThingTypeUID THING_TYPE_AUTOMATEDMETERSENSOR = new ThingTypeUID(BINDING_ID, "automatedMeterSensor"); + public final static ThingTypeUID THING_TYPE_THERMOSTAT = new ThingTypeUID(BINDING_ID, "thermostat"); public final static ThingTypeUID THING_TYPE_OCCUPANCYSENSOR = new ThingTypeUID(BINDING_ID, "occupancySensor"); public final static ThingTypeUID THING_TYPE_LIGHTTEMPERATUREOCCUPANCYSENSOR = new ThingTypeUID(BINDING_ID, @@ -61,12 +62,13 @@ public class EnOceanBindingConstants { public final static ThingTypeUID THING_TYPE_GENERICTHING = new ThingTypeUID(BINDING_ID, "genericThing"); public final static ThingTypeUID THING_TYPE_ROLLERSHUTTER = new ThingTypeUID(BINDING_ID, "rollershutter"); - public static final Set SUPPORTED_DEVICE_THING_TYPES_UIDS = new HashSet(Arrays.asList( - THING_TYPE_PUSHBUTTON, THING_TYPE_ROCKERSWITCH, THING_TYPE_CLASSICDEVICE, THING_TYPE_CENTRALCOMMAND, - THING_TYPE_ROOMOPERATINGPANEL, THING_TYPE_MECHANICALHANDLE, THING_TYPE_CONTACT, - THING_TYPE_MEASUREMENTSWITCH, THING_TYPE_TEMPERATURESENSOR, THING_TYPE_TEMPERATUREHUMIDITYSENSOR, - THING_TYPE_GENERICTHING, THING_TYPE_ROLLERSHUTTER, THING_TYPE_OCCUPANCYSENSOR, - THING_TYPE_LIGHTTEMPERATUREOCCUPANCYSENSOR, THING_TYPE_LIGHTSENSOR, THING_TYPE_AUTOMATEDMETERSENSOR)); + public static final Set SUPPORTED_DEVICE_THING_TYPES_UIDS = new HashSet( + Arrays.asList(THING_TYPE_PUSHBUTTON, THING_TYPE_ROCKERSWITCH, THING_TYPE_CLASSICDEVICE, + THING_TYPE_CENTRALCOMMAND, THING_TYPE_ROOMOPERATINGPANEL, THING_TYPE_MECHANICALHANDLE, + THING_TYPE_CONTACT, THING_TYPE_MEASUREMENTSWITCH, THING_TYPE_TEMPERATURESENSOR, + THING_TYPE_TEMPERATUREHUMIDITYSENSOR, THING_TYPE_GENERICTHING, THING_TYPE_ROLLERSHUTTER, + THING_TYPE_OCCUPANCYSENSOR, THING_TYPE_LIGHTTEMPERATUREOCCUPANCYSENSOR, THING_TYPE_LIGHTSENSOR, + THING_TYPE_AUTOMATEDMETERSENSOR, THING_TYPE_THERMOSTAT)); // List of all Channel Type Ids, these type ids are also used as channel ids during dynamic creation of channels // this makes it a lot easier as we do not have to manage a type id and an id, drawback long channel names @@ -100,6 +102,7 @@ public class EnOceanBindingConstants { public final static String CHANNEL_VIRTUALROCKERSWITCHB = "virtualRockerswitchB"; public final static String CHANNEL_ROCKERSWITCHLISTENERSWITCH = "rockerswitchListenerSwitch"; public final static String CHANNEL_ROCKERSWITCHLISTENERROLLERSHUTTER = "rockerswitchListenerRollershutter"; + public final static String CHANNEL_ROCKERSWITCHLISTENER_START = "rockerswitchListener"; public final static String CHANNEL_WINDOWHANDLESTATE = "windowHandleState"; public final static String CHANNEL_CONTACT = "contact"; @@ -129,6 +132,18 @@ public class EnOceanBindingConstants { public final static String CHANNEL_GENERIC_COLOR = "genericColor"; public final static String CHANNEL_GENERIC_TEACHINCMD = "genericTeachInCMD"; + public final static String CHANNEL_VALVE_POSITION = "valvePosition"; + public final static String CHANNEL_BUTTON_LOCK = "buttonLock"; + public final static String CHANNEL_DISPLAY_ORIENTATION = "displayOrientation"; + public final static String CHANNEL_TEMPERATURE_SETPOINT = "temperatureSetPoint"; + public final static String CHANNEL_FEED_TEMPERATURE = "feedTemperature"; + public final static String CHANNEL_MEASUREMENT_CONTROL = "measurementControl"; + public final static String CHANNEL_FAILURE_CODE = "failureCode"; + public final static String CHANNEL_WAKEUPCYCLE = "wakeUpCycle"; + public final static String CHANNEL_SERVICECOMMAND = "serviceCommand"; + public final static String CHANNEL_STATUS_REQUEST_EVENT = "statusRequestEvent"; + public final static String CHANNEL_SEND_COMMAND = "sendCommand"; + public static final Map CHANNELID2CHANNELDESCRIPTION = Collections .unmodifiableMap(new HashMap() { private static final long serialVersionUID = 1L; @@ -247,6 +262,30 @@ public class EnOceanBindingConstants { put(CHANNEL_GENERIC_TEACHINCMD, new EnOceanChannelDescription( new ChannelTypeUID(BINDING_ID, CHANNEL_GENERIC_TEACHINCMD), CoreItemFactory.SWITCH)); + put(CHANNEL_VALVE_POSITION, new EnOceanChannelDescription( + new ChannelTypeUID(BINDING_ID, CHANNEL_VALVE_POSITION), CoreItemFactory.NUMBER)); + put(CHANNEL_BUTTON_LOCK, new EnOceanChannelDescription( + new ChannelTypeUID(BINDING_ID, CHANNEL_BUTTON_LOCK), CoreItemFactory.SWITCH)); + put(CHANNEL_DISPLAY_ORIENTATION, new EnOceanChannelDescription( + new ChannelTypeUID(BINDING_ID, CHANNEL_DISPLAY_ORIENTATION), CoreItemFactory.NUMBER)); + put(CHANNEL_TEMPERATURE_SETPOINT, new EnOceanChannelDescription( + new ChannelTypeUID(BINDING_ID, CHANNEL_TEMPERATURE_SETPOINT), CoreItemFactory.NUMBER)); + put(CHANNEL_FEED_TEMPERATURE, new EnOceanChannelDescription( + new ChannelTypeUID(BINDING_ID, CHANNEL_FEED_TEMPERATURE), CoreItemFactory.NUMBER)); + put(CHANNEL_MEASUREMENT_CONTROL, new EnOceanChannelDescription( + new ChannelTypeUID(BINDING_ID, CHANNEL_MEASUREMENT_CONTROL), CoreItemFactory.SWITCH)); + put(CHANNEL_FAILURE_CODE, new EnOceanChannelDescription( + new ChannelTypeUID(BINDING_ID, CHANNEL_FAILURE_CODE), CoreItemFactory.NUMBER)); + put(CHANNEL_WAKEUPCYCLE, new EnOceanChannelDescription( + new ChannelTypeUID(BINDING_ID, CHANNEL_WAKEUPCYCLE), CoreItemFactory.NUMBER)); + put(CHANNEL_SERVICECOMMAND, new EnOceanChannelDescription( + new ChannelTypeUID(BINDING_ID, CHANNEL_SERVICECOMMAND), CoreItemFactory.NUMBER)); + + put(CHANNEL_STATUS_REQUEST_EVENT, new EnOceanChannelDescription( + new ChannelTypeUID(BINDING_ID, CHANNEL_STATUS_REQUEST_EVENT), null, "", false, true)); + put(CHANNEL_SEND_COMMAND, new EnOceanChannelDescription( + new ChannelTypeUID(BINDING_ID, CHANNEL_SEND_COMMAND), CoreItemFactory.SWITCH)); + put(CHANNEL_REPEATERMODE, new EnOceanChannelDescription( new ChannelTypeUID(BINDING_ID, CHANNEL_REPEATERMODE), CoreItemFactory.STRING)); } diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanChannelDescription.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanChannelDescription.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanChannelDescription.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanChannelDescription.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanConfigStatusMessage.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanConfigStatusMessage.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanConfigStatusMessage.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanConfigStatusMessage.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanException.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanException.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanException.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanException.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanHandlerFactory.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanHandlerFactory.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/EnOceanHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanActuatorConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanActuatorConfig.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanActuatorConfig.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanActuatorConfig.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBaseConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBaseConfig.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBaseConfig.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanBaseConfig.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelDimmerConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelDimmerConfig.java similarity index 82% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelDimmerConfig.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelDimmerConfig.java index 1289658a44403..93be459974919 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelDimmerConfig.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelDimmerConfig.java @@ -19,8 +19,8 @@ */ public class EnOceanChannelDimmerConfig { - public Integer rampingTime = 0; - public Boolean eltakoDimmer = true; - public Boolean storeValue = false; + public int rampingTime = 0; + public boolean eltakoDimmer = true; + public boolean storeValue = false; } diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelRockerSwitchConfigBase.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelRockerSwitchConfigBase.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelRockerSwitchConfigBase.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelRockerSwitchConfigBase.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelRockerSwitchListenerConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelRockerSwitchListenerConfig.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelRockerSwitchListenerConfig.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelRockerSwitchListenerConfig.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelRollershutterConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelRollershutterConfig.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelRollershutterConfig.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelRollershutterConfig.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTeachInConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTeachInConfig.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTeachInConfig.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTeachInConfig.java diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTotalusageConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTotalusageConfig.java new file mode 100644 index 0000000000000..75b19049ec8e2 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTotalusageConfig.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.enocean.internal.config; + +/** + * + * @author Dominik Vorreiter - Initial contribution + */ +public class EnOceanChannelTotalusageConfig { + public boolean validateValue = false; +} diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTransformationConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTransformationConfig.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTransformationConfig.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelTransformationConfig.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelVirtualRockerSwitchConfig.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelVirtualRockerSwitchConfig.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelVirtualRockerSwitchConfig.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/config/EnOceanChannelVirtualRockerSwitchConfig.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java similarity index 78% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java index fe2a22b22b054..6b8ebca751ab6 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanDeviceDiscoveryService.java @@ -25,6 +25,7 @@ import org.openhab.binding.enocean.internal.eep.EEP; import org.openhab.binding.enocean.internal.eep.EEPFactory; import org.openhab.binding.enocean.internal.eep.Base.UTEResponse; +import org.openhab.binding.enocean.internal.eep.Base._4BSMessage; import org.openhab.binding.enocean.internal.handler.EnOceanBridgeHandler; import org.openhab.binding.enocean.internal.messages.ERP1Message; import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; @@ -103,7 +104,7 @@ public void espPacketReceived(ESP3Packet packet) { ThingTypeUID thingTypeUID = eep.getThingTypeUID(); ThingUID thingUID = new ThingUID(thingTypeUID, bridgeHandler.getThing().getUID(), enoceanId); - int deviceId = 0; + int senderIdOffset = 0; boolean broadcastMessages = true; // check for bidirectional communication => do not use broadcast in this case @@ -114,21 +115,16 @@ public void espPacketReceived(ESP3Packet packet) { // if ute => send response if needed if (msg.getRORG() == RORG.UTE && (msg.getPayload(1, 1)[0] & UTEResponse.ResponseNeeded_MASK) == 0) { + logger.info("Sending UTE response to {}", enoceanId); + senderIdOffset = sendTeachInResponse(msg, enoceanId); + } - // get new sender Id - deviceId = bridgeHandler.getNextSenderId(enoceanId); - if (deviceId > 0) { - byte[] newSenderId = bridgeHandler.getBaseId(); - newSenderId[3] += deviceId; - - // send response - EEP response = EEPFactory.buildResponseEEPFromTeachInERP1(msg, newSenderId); - response.setSuppressRepeating(true); - bridgeHandler.sendMessage(response.getERP1Message(), null); - logger.info("Send teach in response for {}", enoceanId); - } - + // if 4BS teach in variation 3 => send response + if ((eep instanceof _4BSMessage) && ((_4BSMessage) eep).isTeachInVariation3Supported()) { + logger.info("Sending 4BS teach in variation 3 response to {}", enoceanId); + senderIdOffset = sendTeachInResponse(msg, enoceanId); } + DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(thingUID) .withRepresentationProperty(enoceanId).withBridge(bridgeHandler.getThing().getUID()); @@ -136,9 +132,9 @@ public void espPacketReceived(ESP3Packet packet) { discoveryResultBuilder.withProperty(PARAMETER_BROADCASTMESSAGES, broadcastMessages); discoveryResultBuilder.withProperty(PARAMETER_ENOCEANID, enoceanId); - if (deviceId > 0) { + if (senderIdOffset > 0) { // advance config with new device id - discoveryResultBuilder.withProperty(PARAMETER_SENDERIDOFFSET, deviceId); + discoveryResultBuilder.withProperty(PARAMETER_SENDERIDOFFSET, senderIdOffset); } thingDiscovered(discoveryResultBuilder.build()); @@ -148,6 +144,27 @@ public void espPacketReceived(ESP3Packet packet) { // this is true except for UTE teach in => we always have to send a response here } + private int sendTeachInResponse(ERP1Message msg, String enoceanId) { + int offset; + // get new sender Id + offset = bridgeHandler.getNextSenderId(enoceanId); + if (offset > 0) { + byte[] newSenderId = bridgeHandler.getBaseId(); + newSenderId[3] += offset; + + // send response + EEP response = EEPFactory.buildResponseEEPFromTeachInERP1(msg, newSenderId); + if (response != null) { + bridgeHandler.sendMessage(response.getERP1Message(), null); + logger.info("Teach in response for {} with new senderId {} (= offset {}) sent", enoceanId, + HexUtils.bytesToHex(newSenderId), offset); + } else { + logger.warn("Teach in response for enoceanId {} not supported!", enoceanId); + } + } + return offset; + } + @Override public long getSenderIdToListenTo() { // we just want teach in msg, so return zero here diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanUsbSerialDiscoveryParticipant.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanUsbSerialDiscoveryParticipant.java similarity index 88% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanUsbSerialDiscoveryParticipant.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanUsbSerialDiscoveryParticipant.java index bf6547b18e2a9..add4e8446d3ef 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanUsbSerialDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/discovery/EnOceanUsbSerialDiscoveryParticipant.java @@ -81,17 +81,20 @@ private ThingUID createBridgeThingType(UsbSerialDeviceInformation deviceInformat } private boolean isEnoceanUSB300Dongle(UsbSerialDeviceInformation deviceInformation) { + String manufacturer = deviceInformation.getManufacturer(); + String product = deviceInformation.getProduct(); + return deviceInformation.getVendorId() == ENOCEAN_USB300_DONGLE_VENDOR_ID - && deviceInformation.getProductId() == ENOCEAN_USB300_DONGLE_PRODUCT_ID - && deviceInformation.getManufacturer() != null - && deviceInformation.getManufacturer().equalsIgnoreCase(ENOCEAN_USB300_DONGLE_MANUFACTURER) - && deviceInformation.getProduct() != null - && deviceInformation.getProduct().toLowerCase().contains(ENOCEAN_USB300_DONGLE_PRODUCT); + && deviceInformation.getProductId() == ENOCEAN_USB300_DONGLE_PRODUCT_ID && manufacturer != null + && manufacturer.equalsIgnoreCase(ENOCEAN_USB300_DONGLE_MANUFACTURER) && product != null + && product.toLowerCase().contains(ENOCEAN_USB300_DONGLE_PRODUCT); } private @Nullable String createEnoceanUSB300DongleLabel(UsbSerialDeviceInformation deviceInformation) { - if (deviceInformation.getSerialNumber() != null && !deviceInformation.getSerialNumber().isEmpty()) { - return String.format("%s (%s)", ENOCEAN_USB300_DONGLE_DEFAULT_LABEL, deviceInformation.getSerialNumber()); + String serialNumber = deviceInformation.getSerialNumber(); + + if (serialNumber != null && !serialNumber.isEmpty()) { + return String.format("%s (%s)", ENOCEAN_USB300_DONGLE_DEFAULT_LABEL, serialNumber); } else { return ENOCEAN_USB300_DONGLE_DEFAULT_LABEL; } diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_01.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_01.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_02.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_02.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_02.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_02.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_03.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_03.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_03.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_03.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_04.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_04.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_04.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_04.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_05.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_05.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_05.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_05.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_06.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_06.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_06.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_06.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_07.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_07.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_07.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_07.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_08.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_08.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_08.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_08.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_09.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_09.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_09.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_09.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_0A.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_0A.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_0A.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_0A.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_0B.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_0B.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_0B.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_0B.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_10.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_10.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_10.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_10.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_11.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_11.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_11.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_11.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_12.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_12.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_12.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_12.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_13.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_13.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_13.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_13.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_14.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_14.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_14.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_14.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_15.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_15.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_15.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_15.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_16.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_16.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_16.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_16.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_17.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_17.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_17.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_17.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_18.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_18.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_18.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_18.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_19.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_19.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_19.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_19.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_1A.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_1A.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_1A.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_1A.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_1B.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_1B.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_1B.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_1B.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_20.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_20.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_20.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_20.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_30.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_30.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_30.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_02/A5_02_30.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_01.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_01.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_02.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_02.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_02.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_02.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_02_Eltako.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_02_Eltako.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_02_Eltako.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_02_Eltako.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_03.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_03.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_03.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_04/A5_04_03.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_06/A5_06_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_06/A5_06_01.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_06/A5_06_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_06/A5_06_01.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_06/A5_06_01_ELTAKO.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_06/A5_06_01_ELTAKO.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_06/A5_06_01_ELTAKO.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_06/A5_06_01_ELTAKO.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07_01.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07_01.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07_02.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07_02.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07_02.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07_02.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07_03.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07_03.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07_03.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_07/A5_07_03.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_01.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_01.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_01_FXBH.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_01_FXBH.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_01_FXBH.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_01_FXBH.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_02.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_02.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_02.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_02.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_03.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_03.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_03.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_08/A5_08_03.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_01.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_01.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_02.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_02.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_02.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_02.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_03.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_03.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_03.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_03.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_04.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_04.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_04.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_04.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_05.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_05.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_05.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_05.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_06.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_06.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_06.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_06.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_07.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_07.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_07.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_07.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_08.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_08.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_08.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_08.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_09.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_09.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_09.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_09.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0A.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0A.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0A.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0A.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0B.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0B.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0B.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0B.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0C.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0C.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0C.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0C.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0D.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0D.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0D.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_0D.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_10.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_10.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_10.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_10.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_11.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_11.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_11.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_11.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_12.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_12.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_12.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_12.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_13.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_13.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_13.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_13.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_14.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_14.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_14.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_14.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_15.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_15.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_15.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_15.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_16.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_16.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_16.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_16.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_17.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_17.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_17.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_17.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_18.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_18.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_18.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_18.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_19.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_19.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_19.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_19.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1A.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1A.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1A.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1A.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1B.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1B.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1B.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1B.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1C.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1C.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1C.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1C.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1D.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1D.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1D.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1D.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1E.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1E.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1E.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1E.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1F.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1F.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1F.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_1F.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_20.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_20.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_20.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_20.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_21.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_21.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_21.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_21.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_22.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_22.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_22.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_22.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_23.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_23.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_23.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_10/A5_10_23.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_11/A5_11_03.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_11/A5_11_03.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_11/A5_11_03.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_11/A5_11_03.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_11/A5_11_04.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_11/A5_11_04.java similarity index 95% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_11/A5_11_04.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_11/A5_11_04.java index 4d6dd32d8dcf0..10d396c70b4bb 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_11/A5_11_04.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_11/A5_11_04.java @@ -23,6 +23,7 @@ import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; import org.eclipse.smarthome.core.util.HexUtils; +import org.openhab.binding.enocean.internal.eep.EEPHelper; import org.openhab.binding.enocean.internal.eep.Base._4BSMessage; import org.openhab.binding.enocean.internal.messages.ERP1Message; import org.slf4j.Logger; @@ -150,10 +151,10 @@ protected State getPowerMeasurementData() { case WATT: factor = 1; break; - case KILOWATT: - factor *= 1000; case MEGAWATT: factor *= 1000; + case KILOWATT: + factor *= 1000; break; default: return UnDefType.UNDEF; @@ -176,7 +177,8 @@ protected State getOperatingHours() { } @Override - protected State convertToStateImpl(String channelId, String channelTypeId, State currentState, Configuration config) { + protected State convertToStateImpl(String channelId, String channelTypeId, State currentState, + Configuration config) { if (isErrorState()) { return UnDefType.UNDEF; } @@ -189,7 +191,9 @@ protected State convertToStateImpl(String channelId, String channelTypeId, State case CHANNEL_INSTANTPOWER: return getPowerMeasurementData(); case CHANNEL_TOTALUSAGE: - return getEnergyMeasurementData(); + State value = getEnergyMeasurementData(); + + return EEPHelper.validateTotalUsage(value, currentState, config); case CHANNEL_COUNTER: return getOperatingHours(); } diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12.java similarity index 95% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12.java index 3f86c14caac10..86b08b77873d3 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12.java @@ -20,6 +20,7 @@ import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; import org.eclipse.smarthome.core.util.HexUtils; +import org.openhab.binding.enocean.internal.eep.EEPHelper; import org.openhab.binding.enocean.internal.eep.Base._4BSMessage; import org.openhab.binding.enocean.internal.messages.ERP1Message; @@ -118,6 +119,9 @@ protected State convertToStateImpl(String channelId, String channelTypeId, State case CHANNEL_CURRENTNUMBER: return getCurrentValue(); case CHANNEL_TOTALUSAGE: + State value = getCumulativeValue(); + + return EEPHelper.validateTotalUsage(value, currentState, config); case CHANNEL_TOTALCUBICMETRE: case CHANNEL_COUNTER: return getCumulativeValue(); diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_00.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_00.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_00.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_00.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_01.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_01.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_02.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_02.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_02.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_02.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_03.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_03.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_03.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_12/A5_12_03.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14_01.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14_01.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14_01_ELTAKO.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14_01_ELTAKO.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14_01_ELTAKO.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14_01_ELTAKO.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14_09.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14_09.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14_09.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_14/A5_14_09.java diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_20/A5_20.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_20/A5_20.java new file mode 100644 index 0000000000000..4e5b7f17c1f13 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_20/A5_20.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.enocean.internal.eep.A5_20; + +import org.openhab.binding.enocean.internal.eep.Base._4BSMessage; +import org.openhab.binding.enocean.internal.messages.ERP1Message; + +/** + * Heating radiator valve actuating drive with feed and room temperature measurement, local set point control and + * display + * + * @author Dominik Vorreiter - Initial contribution + */ +public class A5_20 extends _4BSMessage { + + public A5_20() { + super(); + + this.supportsTeachInVariation3 = true; + } + + public A5_20(ERP1Message packet) { + super(packet); + + this.supportsTeachInVariation3 = true; + } +} diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_20/A5_20_04.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_20/A5_20_04.java new file mode 100644 index 0000000000000..eccc54a492205 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_20/A5_20_04.java @@ -0,0 +1,272 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.enocean.internal.eep.A5_20; + +import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; + +import java.util.Map; + +import javax.measure.quantity.Temperature; + +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.library.unit.SIUnits; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.enocean.internal.messages.ERP1Message; + +/** + * Heating radiator valve actuating drive with feed and room temperature measurement, local set point control and + * display + * + * @author Dominik Vorreiter - Initial contribution + */ +public class A5_20_04 extends A5_20 { + + public A5_20_04() { + super(); + } + + public A5_20_04(ERP1Message packet) { + super(packet); + } + + @Override + protected String convertToEventImpl(String channelId, String channelTypeId, String lastEvent, + Configuration config) { + switch (channelId) { + case CHANNEL_STATUS_REQUEST_EVENT: + return getStatusRequestEvent(); + } + + return null; + } + + private String getStatusRequestEvent() { + return Boolean.valueOf(getBit(getDB_0Value(), 6)).toString(); + // return getBit(getDB_0Value(), 6) ? "triggered" : null; + } + + private byte getPos(Map currentState) { + State current = currentState.get(CHANNEL_VALVE_POSITION); + + if ((current != null) && (current instanceof DecimalType)) { + DecimalType state = current.as(DecimalType.class); + + if (state != null) { + return state.byteValue(); + } + } + + return 25; // 25 % + } + + private byte getTsp(Map currentState) { + State current = currentState.get(CHANNEL_TEMPERATURE_SETPOINT); + + double value = 20.0; // 20 °C + + if ((current != null) && (current instanceof QuantityType)) { + @SuppressWarnings("unchecked") + QuantityType raw = current.as(QuantityType.class); + + if (raw != null) { + QuantityType celsius = raw.toUnit(SIUnits.CELSIUS); + + if (celsius != null) { + value = celsius.doubleValue(); + } + } + } + + return (byte) ((value - 10.0) * (255.0 / 20.0)); + } + + private byte getMc(Map currentState) { + State current = currentState.get(CHANNEL_MEASUREMENT_CONTROL); + + if ((current != null) && (current instanceof OnOffType)) { + OnOffType state = current.as(OnOffType.class); + + if (state != null) { + return (byte) (state.equals(OnOffType.ON) ? 0x00 : 0x40); + } + } + + return 0x00; // on + } + + private byte getWuc(Map currentState) { + State current = currentState.get(CHANNEL_WAKEUPCYCLE); + + if ((current != null) && (current instanceof DecimalType)) { + DecimalType state = current.as(DecimalType.class); + + if (state != null) { + return (byte) (state.byteValue() & 0x3F); + } + } + + return 0x13; // 19 = 600 sec = 10 min + } + + private byte getDso(Map currentState) { + State current = currentState.get(CHANNEL_DISPLAY_ORIENTATION); + + if ((current != null) && (current instanceof DecimalType)) { + DecimalType state = current.as(DecimalType.class); + + if (state != null) { + return (byte) (((state.byteValue() / 90) << 4) & 0x30); + } + } + + return 0x00; // 0° + } + + private byte getBlc(Map currentState) { + State current = currentState.get(CHANNEL_BUTTON_LOCK); + + if ((current != null) && (current instanceof OnOffType)) { + OnOffType state = current.as(OnOffType.class); + + if (state != null) { + return (byte) (state.equals(OnOffType.ON) ? 0x04 : 0x00); + } + } + + return 0x00; // unlocked + } + + private byte getSer(Map currentState) { + State current = currentState.get(CHANNEL_SERVICECOMMAND); + + if ((current != null) && (current instanceof DecimalType)) { + DecimalType state = current.as(DecimalType.class); + + if (state != null) { + return (byte) (state.byteValue() & 0x03); + } + } + + return 0x00; // 0 = no change + } + + @Override + protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { + + if (CHANNEL_SEND_COMMAND.equals(channelId) && (command.equals(OnOffType.ON))) { + byte db3 = getPos(currentState); + byte db2 = getTsp(currentState); + byte db1 = (byte) (0x00 | getMc(currentState) | getWuc(currentState)); + byte db0 = (byte) (0x00 | getDso(currentState) | TeachInBit | getBlc(currentState) | getSer(currentState)); + + setData(db3, db2, db1, db0); + + return; + } + + if (command instanceof State) { + currentState.put(channelId, (State) command); + } + } + + @Override + protected State convertToStateImpl(String channelId, String channelTypeId, State currentState, + Configuration config) { + + switch (channelId) { + case CHANNEL_VALVE_POSITION: + return getValvePosition(); + case CHANNEL_BUTTON_LOCK: + return getButtonLock(); + case CHANNEL_TEMPERATURE_SETPOINT: + return getTemperatureSetpoint(); + case CHANNEL_TEMPERATURE: + return getTemperature(); + case CHANNEL_FEED_TEMPERATURE: + return getFeedTemperature(); + case CHANNEL_MEASUREMENT_CONTROL: + return getMeasurementControl(); + case CHANNEL_FAILURE_CODE: + return getFailureCode(); + } + + return UnDefType.UNDEF; + } + + private State getTemperature() { + boolean fl = getBit(getDB_0Value(), 0); + boolean mst = getBit(getDB_0Value(), 7); + + if (fl || mst) { + return UnDefType.UNDEF; + } + + double value = getDB_1Value() * (20.0 / 255.0) + 10.0; + + return new QuantityType<>(value, SIUnits.CELSIUS); + } + + private State getFailureCode() { + boolean fl = getBit(getDB_0Value(), 0); + + if (!fl) { + return new QuantityType<>(-1, SmartHomeUnits.ONE); + } + + return new QuantityType<>(getDB_1Value(), SmartHomeUnits.ONE); + } + + private State getMeasurementControl() { + return getBit(getDB_0Value(), 7) ? OnOffType.OFF : OnOffType.ON; + } + + private State getFeedTemperature() { + boolean ts = getBit(getDB_0Value(), 1); + boolean mst = getBit(getDB_0Value(), 7); + + if (ts || mst) { + return UnDefType.UNDEF; + } + + double value = getDB_2Value() * (60.0 / 255.0) + 20.0; + + return new QuantityType<>(value, SIUnits.CELSIUS); + } + + private State getTemperatureSetpoint() { + boolean ts = getBit(getDB_0Value(), 1); + + if (!ts) { + return UnDefType.UNDEF; + } + + double value = getDB_2Value() * (20.0 / 255.0) + 10.0; + + return new QuantityType<>(value, SIUnits.CELSIUS); + } + + private State getButtonLock() { + return getBit(getDB_0Value(), 2) ? OnOffType.ON : OnOffType.OFF; + } + + private State getValvePosition() { + return new QuantityType<>(getDB_3Value(), SmartHomeUnits.PERCENT); + } +} diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Blinds.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Blinds.java similarity index 98% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Blinds.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Blinds.java index 21d7478a2ea60..6c7bf730dce3e 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Blinds.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Blinds.java @@ -14,6 +14,8 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; +import java.util.Map; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.OnOffType; @@ -61,7 +63,7 @@ public A5_38_08_Blinds(ERP1Message packet) { @Override protected void convertFromCommandImpl(String channelId, String channelTypeId, Command outputCommand, - State currentState, Configuration config) { + Map currentState, Configuration config) { switch (channelId) { case CHANNEL_ROLLERSHUTTER: diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Dimming.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Dimming.java similarity index 89% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Dimming.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Dimming.java index 37a7ead325f9e..5f2e51c6c956a 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Dimming.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Dimming.java @@ -14,6 +14,8 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.CHANNEL_DIMMER; +import java.util.Map; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; @@ -52,7 +54,7 @@ public A5_38_08_Dimming(ERP1Message packet) { @Override protected void convertFromCommandImpl(String channelId, String channelTypeId, Command outputCommand, - State currentState, Configuration config) { + Map currentState, Configuration config) { switch (channelId) { case CHANNEL_DIMMER: byte dimmValue; @@ -73,24 +75,21 @@ protected void convertFromCommandImpl(String channelId, String channelTypeId, Co EnOceanChannelDimmerConfig c = config.as(EnOceanChannelDimmerConfig.class); - boolean eltakoDimmer = (c.eltakoDimmer == null) ? true : c.eltakoDimmer; - boolean storeValue = (c.storeValue == null) ? false : c.storeValue; - byte storeByte = 0x00; // "Store final value" (standard) vs. "block value" (Eltako) - if (!eltakoDimmer) { + if (!c.eltakoDimmer) { dimmValue *= 2.55; // 0-100% = 0-255 - if (storeValue) { + if (c.storeValue) { storeByte = 0x02; // set DB0.1 } } else { - if (storeValue) { + if (c.storeValue) { storeByte = 0x04; // set DB0.2 } } - byte rampingTime = (c.rampingTime == null) ? Zero : c.rampingTime.byteValue(); + byte rampingTime = Integer.valueOf(c.rampingTime).byteValue(); byte switchingCommand = (dimmValue == Zero) ? SwitchOff : SwitchOn; setData(CommandId, dimmValue, rampingTime, (byte) (TeachInBit | storeByte | switchingCommand)); @@ -110,9 +109,7 @@ public State convertToStateImpl(String channelId, String channelTypeId, State cu EnOceanChannelDimmerConfig c = config.as(EnOceanChannelDimmerConfig.class); - boolean eltakoDimmer = (c.eltakoDimmer == null) ? true : c.eltakoDimmer; - - if (!eltakoDimmer) { + if (!c.eltakoDimmer) { if (getBit(0, 2)) { // relative value } else { diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Switching.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Switching.java similarity index 94% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Switching.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Switching.java index 996b7da8b29ee..e8eb94fa54e26 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Switching.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_38/A5_38_08_Switching.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.enocean.internal.eep.A5_38; +import java.util.Map; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.types.Command; @@ -39,7 +41,7 @@ public A5_38_08_Switching(ERP1Message packet) { @Override protected void convertFromCommandImpl(String channelId, String channelTypeId, Command outputCommand, - State currentState, Configuration config) { + Map currentState, Configuration config) { if ((OnOffType) outputCommand == OnOffType.ON) { setData(CommandId, Zero, Zero, (byte) (TeachInBit | SwitchOn)); diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_EltakoFSB.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_EltakoFSB.java similarity index 91% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_EltakoFSB.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_EltakoFSB.java index 167e6b622b654..ccb6e080dfc9f 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_EltakoFSB.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_EltakoFSB.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.enocean.internal.eep.A5_3F; +import java.util.Map; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.PercentType; import org.eclipse.smarthome.core.library.types.StopMoveType; @@ -45,7 +47,8 @@ public A5_3F_7F_EltakoFSB(ERP1Message packet) { } @Override - protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, State currentState, Configuration config) { + protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { int shutTime = 0xFF; if (config != null) { @@ -53,13 +56,15 @@ protected void convertFromCommandImpl(String channelId, String channelTypeId, Co } if (command instanceof PercentType) { + State channelState = currentState.get(channelId); + PercentType target = (PercentType) command; if (target.intValue() == PercentType.ZERO.intValue()) { setData(Zero, (byte) shutTime, MoveUp, TeachInBit); // => move completely up } else if (target.intValue() == PercentType.HUNDRED.intValue()) { setData(Zero, (byte) shutTime, MoveDown, TeachInBit); // => move completely down - } else if (currentState != null) { - PercentType current = currentState.as(PercentType.class); + } else if (channelState != null) { + PercentType current = channelState.as(PercentType.class); if (config != null && current != null) { if (current.intValue() != target.intValue()) { byte direction = current.intValue() > target.intValue() ? MoveUp : MoveDown; @@ -86,7 +91,8 @@ protected void convertFromCommandImpl(String channelId, String channelTypeId, Co } @Override - protected State convertToStateImpl(String channelId, String channelTypeId, State currentState, Configuration config) { + protected State convertToStateImpl(String channelId, String channelTypeId, State currentState, + Configuration config) { if (currentState != null) { int direction = getDB_1() == MoveUp ? -1 : 1; diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_Universal.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_Universal.java similarity index 94% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_Universal.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_Universal.java index 99006ee009b6c..b91fbd00c9726 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_Universal.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/A5_3F/A5_3F_7F_Universal.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.enocean.internal.eep.A5_3F; +import java.util.Map; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.transform.actions.Transformation; import org.eclipse.smarthome.core.types.Command; @@ -38,7 +40,8 @@ public A5_3F_7F_Universal(ERP1Message packet) { } @Override - protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, State currentState, Configuration config) { + protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { if (config != null) { try { diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/PTM200Message.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/PTM200Message.java similarity index 91% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/PTM200Message.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/PTM200Message.java index 351f9f0093603..12d1d0052f5f1 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/PTM200Message.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/PTM200Message.java @@ -14,6 +14,8 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; +import java.util.Map; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.OpenClosedType; @@ -45,12 +47,14 @@ public PTM200Message(ERP1Message packet) { } @Override - protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, State currentState, Configuration config) { + protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { } @Override - protected State convertToStateImpl(String channelId, String channelTypeId, State currentState, Configuration config) { + protected State convertToStateImpl(String channelId, String channelTypeId, State currentState, + Configuration config) { if (!isValid()) { return UnDefType.UNDEF; } diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java similarity index 97% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java index ebfcdfc4fc438..593fdbda6358a 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/UTEResponse.java @@ -33,7 +33,7 @@ public UTEResponse(ERP1Message packet) { bytes[0] = (byte) 0x91; // bidirectional communication, teach in accepted, teach in response setStatus((byte) 0x80); - + setSuppressRepeating(true); setDestinationId(packet.getSenderId()); } } diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_1BSMessage.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_1BSMessage.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_1BSMessage.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_1BSMessage.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSMessage.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSMessage.java similarity index 94% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSMessage.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSMessage.java index 0e24e32dfa56a..938a67d954f5b 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSMessage.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSMessage.java @@ -25,6 +25,12 @@ */ public abstract class _4BSMessage extends EEP { + protected boolean supportsTeachInVariation3 = false; + + public boolean isTeachInVariation3Supported() { + return supportsTeachInVariation3; + } + public _4BSMessage(ERP1Message packet) { super(packet); } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSTeachInVariation3Response.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSTeachInVariation3Response.java new file mode 100644 index 0000000000000..51d7c7b6c0f10 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_4BSTeachInVariation3Response.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.enocean.internal.eep.Base; + +import org.openhab.binding.enocean.internal.messages.ERP1Message; +import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; + +/** + * + * @author Dominik Vorreiter - Initial contribution + */ +public class _4BSTeachInVariation3Response extends _4BSMessage { + + public _4BSTeachInVariation3Response(ERP1Message packet) { + byte[] payload = packet.getPayload(RORGLength, RORG._4BS.getDataLength()); + + payload[3] = (byte) 0xF0; // telegram with EEP number and Manufacturer ID, + // EEP supported, Sender ID stored, Response + + setData(payload); + setDestinationId(packet.getSenderId()); + setSuppressRepeating(false); + setStatus((byte) 0x00); + } +} diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_RPSMessage.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_RPSMessage.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_RPSMessage.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_RPSMessage.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_VLDMessage.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_VLDMessage.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_VLDMessage.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Base/_VLDMessage.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01.java similarity index 91% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01.java index 02fa4afdf577f..91bfd6baf0f24 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01.java @@ -14,6 +14,8 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; +import java.util.Map; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; import org.eclipse.smarthome.core.library.types.DecimalType; @@ -27,6 +29,7 @@ import org.eclipse.smarthome.core.types.UnDefType; import org.eclipse.smarthome.core.util.HexUtils; import org.openhab.binding.enocean.internal.config.EnOceanChannelDimmerConfig; +import org.openhab.binding.enocean.internal.eep.EEPHelper; import org.openhab.binding.enocean.internal.eep.Base._VLDMessage; import org.openhab.binding.enocean.internal.messages.ERP1Message; @@ -114,7 +117,7 @@ protected void setDimmingData(Command command, byte outputChannel, Configuration } EnOceanChannelDimmerConfig c = config.as(EnOceanChannelDimmerConfig.class); - byte rampingTime = (c.rampingTime == null) ? Zero : c.rampingTime.byteValue(); + byte rampingTime = Integer.valueOf(c.rampingTime).byteValue(); setData(CMD_ACTUATOR_SET_STATUS, (byte) ((rampingTime << 5) | outputChannel), outputValue); } @@ -140,13 +143,14 @@ protected State getEnergyMeasurementData() { float factor = 1; switch (bytes[1] >>> 5) { - case 0: - factor /= 3600.0; + case 0: // value is given as watt seconds, so divide it by 3600 to get watt hours, and 1000 to get + // kilowatt hours + factor /= (3600 * 1000); break; - case 1: + case 1: // value is given as watt hours, so divide it by 1000 to get kilowatt hours factor /= 1000; break; - case 2: + case 2: // value is given as kilowatt hours factor = 1; break; default: @@ -166,10 +170,10 @@ protected State getPowerMeasurementData() { float factor = 1; switch (bytes[1] >>> 5) { - case 3: + case 3: // value is given as watt factor = 1; break; - case 4: + case 4: // value is given as kilowatt factor /= 1000; break; default: @@ -192,8 +196,8 @@ public void addConfigPropertiesTo(DiscoveryResultBuilder discoveredThingResultBu } @Override - protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, State currentState, - Configuration config) { + protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { if (channelId.equals(CHANNEL_GENERAL_SWITCHING)) { if (command == RefreshType.REFRESH) { @@ -242,7 +246,9 @@ protected State convertToStateImpl(String channelId, String channelTypeId, State case CHANNEL_INSTANTPOWER: return getPowerMeasurementData(); case CHANNEL_TOTALUSAGE: - return getEnergyMeasurementData(); + State value = getEnergyMeasurementData(); + + return EEPHelper.validateTotalUsage(value, currentState, config); } return UnDefType.UNDEF; diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_00.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_00.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_00.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_00.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_01.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_01.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_02.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_02.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_02.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_02.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_03.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_03.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_03.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_03.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_04.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_04.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_04.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_04.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_05.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_05.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_05.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_05.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_06.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_06.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_06.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_06.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_07.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_07.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_07.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_07.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_08.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_08.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_08.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_08.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_09.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_09.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_09.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_09.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_09_Permundo.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_09_Permundo.java similarity index 95% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_09_Permundo.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_09_Permundo.java index 5de4fb42d8e65..4a70f4e78dbd1 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_09_Permundo.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_09_Permundo.java @@ -14,6 +14,8 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; +import java.util.Map; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.StringType; @@ -39,7 +41,8 @@ public D2_01_09_Permundo(ERP1Message packet) { } @Override - protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, State currentState, Configuration config) { + protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { if (channelId.equals(CHANNEL_REPEATERMODE)) { setRepeaterMode(command); diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0A.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0A.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0A.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0A.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0B.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0B.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0B.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0B.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0C.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0C.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0C.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0C.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0D.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0D.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0D.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0D.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0E.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0E.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0E.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0E.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0F.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0F.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0F.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0F.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0F_NodON.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0F_NodON.java similarity index 94% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0F_NodON.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0F_NodON.java index 375787f1c52ee..c9c14642baaf8 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0F_NodON.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_0F_NodON.java @@ -14,6 +14,8 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.CHANNEL_REPEATERMODE; +import java.util.Map; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.StringType; import org.eclipse.smarthome.core.types.Command; @@ -38,7 +40,8 @@ public D2_01_0F_NodON(ERP1Message packet) { } @Override - protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, State currentState, Configuration config) { + protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { if (channelId.equalsIgnoreCase(CHANNEL_REPEATERMODE)) { diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_11.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_11.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_11.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_11.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_12.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_12.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_12.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_12.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_12_NodON.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_12_NodON.java similarity index 94% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_12_NodON.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_12_NodON.java index e2126768c88c7..02cc956450082 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_12_NodON.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_01/D2_01_12_NodON.java @@ -14,6 +14,8 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.CHANNEL_REPEATERMODE; +import java.util.Map; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.StringType; import org.eclipse.smarthome.core.types.Command; @@ -38,7 +40,8 @@ public D2_01_12_NodON(ERP1Message packet) { } @Override - protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, State currentState, Configuration config) { + protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { if (channelId.equalsIgnoreCase(CHANNEL_REPEATERMODE)) { diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_05/D2_05_00.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_05/D2_05_00.java similarity index 97% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_05/D2_05_00.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_05/D2_05_00.java index 987ad54421e57..505d01e5c6af6 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_05/D2_05_00.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D2_05/D2_05_00.java @@ -14,6 +14,8 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; +import java.util.Map; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; import org.eclipse.smarthome.core.library.types.PercentType; @@ -101,8 +103,8 @@ public void addConfigPropertiesTo(DiscoveryResultBuilder discoveredThingResultBu } @Override - protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, State currentState, - Configuration config) { + protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { if (channelId.equals(CHANNEL_ROLLERSHUTTER)) { if (command == RefreshType.REFRESH) { diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D5_00/D5_00_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D5_00/D5_00_01.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D5_00/D5_00_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/D5_00/D5_00_01.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEP.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEP.java similarity index 93% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEP.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEP.java index 94f4705983d43..45f43bed48733 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEP.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEP.java @@ -15,6 +15,7 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; import java.util.Arrays; +import java.util.Map; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; @@ -76,8 +77,8 @@ public EEP(ERP1Message packet) { setOptionalData(packet.getOptionalPayload()); } - public EEP convertFromCommand(String channelId, String channelTypeId, Command command, State currentState, - Configuration config) { + public EEP convertFromCommand(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { if (!getEEPType().isChannelSupported(channelId, channelTypeId)) { throw new IllegalArgumentException(String.format("Command %s of channel %s(%s) is not supported", command.toString(), channelId, channelTypeId)); @@ -140,6 +141,10 @@ public EEP setData(byte... bytes) { return this; } + public boolean hasData() { + return (this.bytes != null) && (this.bytes.length > 0); + } + public EEP setOptionalData(byte... bytes) { if (bytes != null) { this.optionalData = Arrays.copyOf(bytes, bytes.length); @@ -194,6 +199,8 @@ public final ERP1Message getERP1Message() { message.setOptionalPayload(optionalData); return message; + } else { + logger.warn("ERP1Message for EEP {} is not valid!", this.getClass().getName()); } return null; @@ -223,9 +230,9 @@ protected int getDataLength() { return 0; } - protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, State currentState, - Configuration config) { - + protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { + logger.warn("No implementation for sending data from channel {}/{} for this EEP!", channelId, channelTypeId); } protected State convertToStateImpl(String channelId, String channelTypeId, State currentState, @@ -239,7 +246,7 @@ protected String convertToEventImpl(String channelId, String channelTypeId, Stri } protected void teachInQueryImpl(Configuration config) { - + logger.warn("No implementation for sending a response for this teach in!"); } protected boolean getBit(int byteData, int bit) { diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java similarity index 91% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java index 2bd97d1500a55..4f2affb8141e3 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPFactory.java @@ -17,6 +17,7 @@ import org.eclipse.smarthome.core.util.HexUtils; import org.openhab.binding.enocean.internal.eep.Base.UTEResponse; import org.openhab.binding.enocean.internal.eep.Base._4BSMessage; +import org.openhab.binding.enocean.internal.eep.Base._4BSTeachInVariation3Response; import org.openhab.binding.enocean.internal.eep.D5_00.D5_00_01; import org.openhab.binding.enocean.internal.eep.F6_01.F6_01_01; import org.openhab.binding.enocean.internal.eep.F6_02.F6_02_01; @@ -110,8 +111,8 @@ public static EEP buildEEPFromTeachInERP1(ERP1Message msg) { return new D5_00_01(msg); case _4BS: { int db_0 = msg.getPayload()[4]; - if ((db_0 & _4BSMessage.LRN_Type_Mask) == 0) { - logger.info("Received 4BS Teach In without EEP"); + if ((db_0 & _4BSMessage.LRN_Type_Mask) == 0) { // Variation 1 + logger.info("Received 4BS Teach In variation 1 without EEP"); return null; } @@ -175,9 +176,19 @@ public static EEP buildEEPFromTeachInERP1(ERP1Message msg) { } public static EEP buildResponseEEPFromTeachInERP1(ERP1Message msg, byte[] senderId) { - EEP result = new UTEResponse(msg); - result.setSenderId(senderId); + switch (msg.getRORG()) { + case UTE: + EEP result = new UTEResponse(msg); + result.setSenderId(senderId); + + return result; + case _4BS: + result = new _4BSTeachInVariation3Response(msg); + result.setSenderId(senderId); - return result; + return result; + default: + return null; + } } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPHelper.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPHelper.java new file mode 100644 index 0000000000000..d8b45aa45be8a --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPHelper.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.enocean.internal.eep; + +import javax.measure.quantity.Energy; + +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.enocean.internal.config.EnOceanChannelTotalusageConfig; + +/** + * + * @author Dominik Vorreiter - initial contribution + * + */ +public abstract class EEPHelper { + public static State validateTotalUsage(State value, State currentState, Configuration config) { + EnOceanChannelTotalusageConfig c = config.as(EnOceanChannelTotalusageConfig.class); + + if (c.validateValue && (value instanceof QuantityType) && (currentState instanceof QuantityType)) { + @SuppressWarnings("unchecked") + QuantityType newValue = value.as(QuantityType.class); + + if (newValue != null) { + newValue = newValue.toUnit(SmartHomeUnits.KILOWATT_HOUR); + } + + @SuppressWarnings("unchecked") + QuantityType oldValue = currentState.as(QuantityType.class); + + if (oldValue != null) { + oldValue = oldValue.toUnit(SmartHomeUnits.KILOWATT_HOUR); + } + + if ((newValue != null) && (oldValue != null)) { + if (newValue.compareTo(oldValue) < 0) { + if ((oldValue.subtract(newValue).doubleValue() < 1.0)) { + return UnDefType.UNDEF; + } + } else { + if (newValue.subtract(oldValue).doubleValue() > 10.0) { + return UnDefType.UNDEF; + } + } + } + } + + return value; + } +} diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java similarity index 96% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java index ca7ead207237b..3ea9ba2ce78ad 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/EEPType.java @@ -22,6 +22,7 @@ import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.thing.Channel; import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; import org.openhab.binding.enocean.internal.EnOceanChannelDescription; import org.openhab.binding.enocean.internal.eep.A5_02.A5_02_01; import org.openhab.binding.enocean.internal.eep.A5_02.A5_02_02; @@ -103,12 +104,14 @@ import org.openhab.binding.enocean.internal.eep.A5_14.A5_14_01; import org.openhab.binding.enocean.internal.eep.A5_14.A5_14_01_ELTAKO; import org.openhab.binding.enocean.internal.eep.A5_14.A5_14_09; +import org.openhab.binding.enocean.internal.eep.A5_20.A5_20_04; import org.openhab.binding.enocean.internal.eep.A5_38.A5_38_08_Blinds; import org.openhab.binding.enocean.internal.eep.A5_38.A5_38_08_Dimming; import org.openhab.binding.enocean.internal.eep.A5_38.A5_38_08_Switching; import org.openhab.binding.enocean.internal.eep.A5_3F.A5_3F_7F_EltakoFSB; import org.openhab.binding.enocean.internal.eep.Base.PTM200Message; import org.openhab.binding.enocean.internal.eep.Base.UTEResponse; +import org.openhab.binding.enocean.internal.eep.Base._4BSTeachInVariation3Response; import org.openhab.binding.enocean.internal.eep.D2_01.D2_01_00; import org.openhab.binding.enocean.internal.eep.D2_01.D2_01_01; import org.openhab.binding.enocean.internal.eep.D2_01.D2_01_02; @@ -150,6 +153,7 @@ public enum EEPType { Undef(RORG.Unknown, 0, 0, false, null, null, 0), UTEResponse(RORG.UTE, 0, 0, false, UTEResponse.class, null), + _4BSTeachInVariation3Response(RORG._4BS, 0, 0, false, _4BSTeachInVariation3Response.class, null), GenericRPS(RORG.RPS, 0xFF, 0xFF, false, GenericRPS.class, THING_TYPE_GENERICTHING, CHANNEL_GENERIC_SWITCH, CHANNEL_GENERIC_ROLLERSHUTTER, CHANNEL_GENERIC_DIMMER, CHANNEL_GENERIC_NUMBER, CHANNEL_GENERIC_STRING, @@ -292,10 +296,12 @@ public enum EEPType { CHANNEL_FANSPEEDSTAGE), RoomPanel_A5_10_0A(RORG._4BS, 0x10, 0x0A, false, A5_10_0A.class, THING_TYPE_ROOMOPERATINGPANEL, CHANNEL_TEMPERATURE, CHANNEL_SETPOINT), - RoomPanel_A5_10_0B(RORG._4BS, 0x10, 0x0B, false, A5_10_0B.class, THING_TYPE_ROOMOPERATINGPANEL, CHANNEL_TEMPERATURE), + RoomPanel_A5_10_0B(RORG._4BS, 0x10, 0x0B, false, A5_10_0B.class, THING_TYPE_ROOMOPERATINGPANEL, + CHANNEL_TEMPERATURE), RoomPanel_A5_10_0C(RORG._4BS, 0x10, 0x0C, false, A5_10_0C.class, THING_TYPE_ROOMOPERATINGPANEL, CHANNEL_TEMPERATURE, CHANNEL_OCCUPANCY), - RoomPanel_A5_10_0D(RORG._4BS, 0x10, 0x0D, false, A5_10_0D.class, THING_TYPE_ROOMOPERATINGPANEL, CHANNEL_TEMPERATURE), + RoomPanel_A5_10_0D(RORG._4BS, 0x10, 0x0D, false, A5_10_0D.class, THING_TYPE_ROOMOPERATINGPANEL, + CHANNEL_TEMPERATURE), RoomPanel_A5_10_10(RORG._4BS, 0x10, 0x10, false, A5_10_10.class, THING_TYPE_ROOMOPERATINGPANEL, CHANNEL_TEMPERATURE, CHANNEL_SETPOINT, CHANNEL_OCCUPANCY), RoomPanel_A5_10_11(RORG._4BS, 0x10, 0x11, false, A5_10_11.class, THING_TYPE_ROOMOPERATINGPANEL, CHANNEL_TEMPERATURE, @@ -304,8 +310,10 @@ public enum EEPType { CHANNEL_SETPOINT), RoomPanel_A5_10_13(RORG._4BS, 0x10, 0x13, false, A5_10_13.class, THING_TYPE_ROOMOPERATINGPANEL, CHANNEL_TEMPERATURE, CHANNEL_OCCUPANCY), - RoomPanel_A5_10_14(RORG._4BS, 0x10, 0x14, false, A5_10_14.class, THING_TYPE_ROOMOPERATINGPANEL, CHANNEL_TEMPERATURE), - RoomPanel_A5_10_15(RORG._4BS, 0x10, 0x15, false, A5_10_15.class, THING_TYPE_ROOMOPERATINGPANEL, CHANNEL_TEMPERATURE), + RoomPanel_A5_10_14(RORG._4BS, 0x10, 0x14, false, A5_10_14.class, THING_TYPE_ROOMOPERATINGPANEL, + CHANNEL_TEMPERATURE), + RoomPanel_A5_10_15(RORG._4BS, 0x10, 0x15, false, A5_10_15.class, THING_TYPE_ROOMOPERATINGPANEL, + CHANNEL_TEMPERATURE), RoomPanel_A5_10_16(RORG._4BS, 0x10, 0x16, false, A5_10_16.class, THING_TYPE_ROOMOPERATINGPANEL, CHANNEL_TEMPERATURE, CHANNEL_OCCUPANCY), RoomPanel_A5_10_17(RORG._4BS, 0x10, 0x17, false, A5_10_17.class, THING_TYPE_ROOMOPERATINGPANEL, CHANNEL_TEMPERATURE, @@ -371,6 +379,11 @@ public enum EEPType { } }), + Thermostat(RORG._4BS, 0x20, 0x04, false, A5_20_04.class, THING_TYPE_THERMOSTAT, CHANNEL_VALVE_POSITION, + CHANNEL_BUTTON_LOCK, CHANNEL_DISPLAY_ORIENTATION, CHANNEL_TEMPERATURE_SETPOINT, CHANNEL_TEMPERATURE, + CHANNEL_FEED_TEMPERATURE, CHANNEL_MEASUREMENT_CONTROL, CHANNEL_FAILURE_CODE, CHANNEL_WAKEUPCYCLE, + CHANNEL_SERVICECOMMAND, CHANNEL_STATUS_REQUEST_EVENT, CHANNEL_SEND_COMMAND), + SwitchWithEnergyMeasurment_00(RORG.VLD, 0x01, 0x00, true, D2_01_00.class, THING_TYPE_MEASUREMENTSWITCH, CHANNEL_GENERAL_SWITCHING, CHANNEL_TOTALUSAGE), SwitchWithEnergyMeasurment_01(RORG.VLD, 0x01, 0x01, true, D2_01_01.class, THING_TYPE_MEASUREMENTSWITCH, @@ -528,7 +541,10 @@ public Map GetSupportedChannels() { } public boolean isChannelSupported(Channel channel) { - return isChannelSupported(channel.getUID().getId(), channel.getChannelTypeUID().getId()); + ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); + String id = channelTypeUID == null ? "" : channelTypeUID.getId(); + + return isChannelSupported(channel.getUID().getId(), id); } public boolean isChannelSupported(String channelId, String channelTypeId) { @@ -573,7 +589,7 @@ public static EEPType getType(String receivingEEPId) { } } - throw new IllegalArgumentException(String.format("EEP with id {} could not be found", receivingEEPId)); + throw new IllegalArgumentException(String.format("EEP with id %s could not be found", receivingEEPId)); } public static EEPType getType(Class eepClass) { @@ -583,7 +599,7 @@ public static EEPType getType(Class eepClass) { } } - throw new IllegalArgumentException(String.format("EEP with class {} could not be found", eepClass)); + throw new IllegalArgumentException(String.format("EEP with class %s could not be found", eepClass.getName())); } public static EEPType getType(RORG rorg, int func, int type, int manufId) { diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_01/F6_01_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_01/F6_01_01.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_01/F6_01_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_01/F6_01_01.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_01.java similarity index 98% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_01.java index f93f3f1ead424..0a40a3f889ad2 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_01.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_01.java @@ -14,6 +14,8 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; +import java.util.Map; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.StringType; @@ -81,8 +83,8 @@ protected String convertToEventImpl(String channelId, String channelTypeId, Stri } @Override - protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, State currentState, - Configuration config) { + protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { if (command instanceof StringType) { diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_02.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_02.java similarity index 98% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_02.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_02.java index dba7b4eaf31f2..a873d3bf57887 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_02.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_02/F6_02_02.java @@ -14,6 +14,8 @@ import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; +import java.util.Map; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.StringType; @@ -78,8 +80,8 @@ protected String convertToEventImpl(String channelId, String channelTypeId, Stri } @Override - protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, State currentState, - Configuration config) { + protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { if (command instanceof StringType) { diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_00.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_00.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_00.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_00.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_01.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_01.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_01.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/F6_10/F6_10_01.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/Generic4BS.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/Generic4BS.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/Generic4BS.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/Generic4BS.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java similarity index 95% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java index 50a0994bfdff8..3dbd0b5940bd6 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericEEP.java @@ -18,6 +18,7 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; @@ -78,10 +79,10 @@ public GenericEEP(ERP1Message packet) { } @Override - protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, State currentState, Configuration config) { + protected void convertFromCommandImpl(String channelId, String channelTypeId, Command command, + Map currentState, Configuration config) { if (config != null) { - EnOceanChannelTransformationConfig transformationInfo = config - .as(EnOceanChannelTransformationConfig.class); + EnOceanChannelTransformationConfig transformationInfo = config.as(EnOceanChannelTransformationConfig.class); String input = channelId + "|" + command.toString(); String output = Transformation.transform(transformationInfo.transformationType, @@ -98,11 +99,11 @@ protected void convertFromCommandImpl(String channelId, String channelTypeId, Co } @Override - protected State convertToStateImpl(String channelId, String channelTypeId, State currentState, Configuration config) { + protected State convertToStateImpl(String channelId, String channelTypeId, State currentState, + Configuration config) { if (config != null) { - EnOceanChannelTransformationConfig transformationInfo = config - .as(EnOceanChannelTransformationConfig.class); + EnOceanChannelTransformationConfig transformationInfo = config.as(EnOceanChannelTransformationConfig.class); String payload = HexUtils.bytesToHex(bytes); String input = channelId + "|" + payload; diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericRPS.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericRPS.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericRPS.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericRPS.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericVLD.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericVLD.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericVLD.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/eep/Generic/GenericVLD.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java similarity index 85% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java index 058724365df8c..d2fe9be9f4443 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseActuatorHandler.java @@ -29,7 +29,6 @@ import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; -import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.util.HexUtils; import org.openhab.binding.enocean.internal.config.EnOceanActuatorConfig; import org.openhab.binding.enocean.internal.eep.EEP; @@ -48,7 +47,7 @@ public class EnOceanBaseActuatorHandler extends EnOceanBaseSensorHandler { // List of thing types which support sending of eep messages public final static Set SUPPORTED_THING_TYPES = new HashSet( Arrays.asList(THING_TYPE_CENTRALCOMMAND, THING_TYPE_MEASUREMENTSWITCH, THING_TYPE_GENERICTHING, - THING_TYPE_ROLLERSHUTTER)); + THING_TYPE_ROLLERSHUTTER, THING_TYPE_THERMOSTAT)); protected byte[] senderId; // base id of bridge + senderIdOffset, used for sending msg protected byte[] destinationId; // in case of broadcast FFFFFFFF otherwise the enocean id of the device @@ -194,6 +193,9 @@ public void handleCommand(ChannelUID channelUID, Command command) { return; } + ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); + String channelTypeId = (channelTypeUID != null) ? channelTypeUID.getId() : ""; + // check if we do support refreshs if (command == RefreshType.REFRESH) { if (!sendingEEPType.getSupportsRefresh()) { @@ -201,16 +203,11 @@ public void handleCommand(ChannelUID channelUID, Command command) { } // receiving status cannot be refreshed - ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); - if (channelTypeUID != null) { - String channelTypeId = channelTypeUID.getId(); - - switch (channelTypeId) { - case CHANNEL_RSSI: - case CHANNEL_REPEATCOUNT: - case CHANNEL_LASTRECEIVED: - return; - } + switch (channelTypeId) { + case CHANNEL_RSSI: + case CHANNEL_REPEATCOUNT: + case CHANNEL_LASTRECEIVED: + return; } } @@ -220,23 +217,20 @@ public void handleCommand(ChannelUID channelUID, Command command) { } try { - EEP eep = EEPFactory.createEEP(sendingEEPType); Configuration channelConfig = channel.getConfiguration(); - // Eltako rollershutter do not support absolute value just values relative to the current position - // If we want to go to 80% we must know the current position to determine how long the rollershutter has - // to drive up/down. However items seems to be stateless, so we have to store the state by ourself. - // The currentState is updated by EnOceanBaseSensorHandler after receiving a response. - State currentState = getCurrentState(channelId); + EEP eep = EEPFactory.createEEP(sendingEEPType); + eep.convertFromCommand(channelId, channelTypeId, command, channelState, channelConfig); + + if (eep.hasData()) { + ESP3Packet msg = eep.setSenderId(senderId).setDestinationId(destinationId) + .setSuppressRepeating(getConfiguration().suppressRepeating).getERP1Message(); - ESP3Packet msg = eep - .setSenderId(senderId).setDestinationId(destinationId).convertFromCommand(channelId, - channel.getChannelTypeUID().getId(), command, currentState, channelConfig) - .setSuppressRepeating(getConfiguration().suppressRepeating).getERP1Message(); + getBridgeHandler().sendMessage(msg, null); + } - getBridgeHandler().sendMessage(msg, null); - } catch (Exception e) { - logger.debug(e.getMessage()); + } catch (IllegalArgumentException e) { + logger.warn("Exception while sending telegram!", e); } } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java new file mode 100644 index 0000000000000..ead828d678811 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseSensorHandler.java @@ -0,0 +1,201 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.enocean.internal.handler; + +import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Set; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.type.ChannelKind; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.eclipse.smarthome.core.util.HexUtils; +import org.openhab.binding.enocean.internal.config.EnOceanBaseConfig; +import org.openhab.binding.enocean.internal.eep.EEP; +import org.openhab.binding.enocean.internal.eep.EEPFactory; +import org.openhab.binding.enocean.internal.eep.EEPType; +import org.openhab.binding.enocean.internal.messages.ERP1Message; +import org.openhab.binding.enocean.internal.messages.ERP1Message.RORG; +import org.openhab.binding.enocean.internal.messages.ESP3Packet; +import org.openhab.binding.enocean.internal.transceiver.ESP3PacketListener; + +/** + * + * @author Daniel Weber - Initial contribution + * This class defines base functionality for receiving eep messages. + */ +public class EnOceanBaseSensorHandler extends EnOceanBaseThingHandler implements ESP3PacketListener { + + // List of all thing types which support receiving of eep messages + public final static Set SUPPORTED_THING_TYPES = new HashSet( + Arrays.asList(THING_TYPE_ROOMOPERATINGPANEL, THING_TYPE_MECHANICALHANDLE, THING_TYPE_CONTACT, + THING_TYPE_TEMPERATURESENSOR, THING_TYPE_TEMPERATUREHUMIDITYSENSOR, THING_TYPE_ROCKERSWITCH, + THING_TYPE_OCCUPANCYSENSOR, THING_TYPE_LIGHTTEMPERATUREOCCUPANCYSENSOR, THING_TYPE_LIGHTSENSOR, + THING_TYPE_PUSHBUTTON, THING_TYPE_AUTOMATEDMETERSENSOR)); + + protected Hashtable receivingEEPTypes = null; + + public EnOceanBaseSensorHandler(Thing thing) { + super(thing); + } + + @Override + void initializeConfig() { + config = getConfigAs(EnOceanBaseConfig.class); + } + + @Override + boolean validateConfig() { + receivingEEPTypes = null; + + try { + if (config.receivingEEPId != null && !config.receivingEEPId.isEmpty()) { + boolean first = true; + receivingEEPTypes = new Hashtable<>(); + + for (String receivingEEP : config.receivingEEPId) { + if (receivingEEP == null) { + continue; + } + + EEPType receivingEEPType = EEPType.getType(receivingEEP); + if (receivingEEPTypes.containsKey(receivingEEPType.getRORG())) { + configurationErrorDescription = "Receiving more than one EEP of the same RORG is not supported"; + return false; + } + + receivingEEPTypes.put(receivingEEPType.getRORG(), receivingEEPType); + updateChannels(receivingEEPType, first); + first = false; + } + } else { + receivingEEPTypes = null; + } + } catch (Exception e) { + configurationErrorDescription = "Receiving EEP is not supported"; + return false; + } + + if (receivingEEPTypes != null) { + if (!validateEnoceanId(config.enoceanId)) { + configurationErrorDescription = "EnOceanId is not a valid EnOceanId"; + return false; + } + + if (!config.enoceanId.equals(EMPTYENOCEANID)) { + getBridgeHandler().addPacketListener(this); + } + } + + return true; + } + + @Override + public long getSenderIdToListenTo() { + return Long.parseLong(config.enoceanId, 16); + } + + @Override + public void handleRemoval() { + + if (getBridgeHandler() != null) { + getBridgeHandler().removePacketListener(this); + } + super.handleRemoval(); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + // sensor things cannot send any messages, hence they are not allowed to handle any command + // The only possible command would be "Refresh" + } + + protected Predicate stateFilter(EEPType eepType, byte[] senderId) { + return c -> ChannelKind.STATE.equals(c.getKind()) + && eepType.GetSupportedChannels().containsKey(c.getUID().getId()); + } + + protected Predicate triggerFilter(EEPType eepType, byte[] senderId) { + return c -> ChannelKind.TRIGGER.equals(c.getKind()) + && eepType.GetSupportedChannels().containsKey(c.getUID().getId()); + } + + @Override + public void espPacketReceived(ESP3Packet packet) { + + if (receivingEEPTypes == null) { + return; + } + + ERP1Message msg = (ERP1Message) packet; + EEPType receivingEEPType = receivingEEPTypes.get(msg.getRORG()); + if (receivingEEPType == null) { + return; + } + + EEP eep = EEPFactory.buildEEP(receivingEEPType, (ERP1Message) packet); + logger.debug("ESP Packet payload {} for {} received", HexUtils.bytesToHex(packet.getPayload()), + config.enoceanId); + + if (eep.isValid()) { + Supplier> streamSupplier = () -> getLinkedChannels().stream(); + byte[] senderId = msg.getSenderId(); + + // try to interpret received message for all linked channels + streamSupplier.get().filter(stateFilter(receivingEEPType, senderId)).forEach(channel -> { + ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); + + String channelTypeId = (channelTypeUID != null) ? channelTypeUID.getId() : ""; + String channelId = channel.getUID().getId(); + Configuration channelConfig = channel.getConfiguration(); + + State currentState = getCurrentState(channelId); + State result = eep.convertToState(channelId, channelTypeId, channelConfig, currentState); + + // if message can be interpreted (result != UnDefType.UNDEF) => update item state + if (result != null && result != UnDefType.UNDEF) { + updateState(channelId, result); + setCurrentState(channelTypeId, result); // update internal state map + } + }); + + streamSupplier.get().filter(triggerFilter(receivingEEPType, senderId)).forEach(channel -> { + ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); + + String channelTypeId = (channelTypeUID != null) ? channelTypeUID.getId() : ""; + String channelId = channel.getUID().getId(); + Configuration channelConfig = channel.getConfiguration(); + + String lastEvent = lastEvents.get(channelId); + String event = eep.convertToEvent(channelId, channelTypeId, lastEvent, channelConfig); + if (event != null) { + triggerChannel(channel.getUID(), event); + lastEvents.put(channelId, event); + } + }); + } + } +} diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseThingHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseThingHandler.java similarity index 96% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseThingHandler.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseThingHandler.java index d7965485ff440..6efb5dba9a950 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseThingHandler.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBaseThingHandler.java @@ -33,6 +33,7 @@ import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder; import org.eclipse.smarthome.core.thing.type.ChannelKind; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.enocean.internal.EnOceanChannelDescription; @@ -134,8 +135,12 @@ protected void updateChannels(EEPType eep, boolean removeUnsupportedChannels) { } // if we already created a channel with the same type => skip - if (channelList.stream() - .anyMatch(channel -> cd.channelTypeUID.getId().equals(channel.getChannelTypeUID().getId()))) { + if (channelList.stream().anyMatch(channel -> { + ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); + String id = channelTypeUID == null ? "" : channelTypeUID.getId(); + + return cd.channelTypeUID.getId().equals(id); + })) { continue; } diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java similarity index 97% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java index ca75540c1a69e..0c70d39d6464b 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanBridgeHandler.java @@ -149,8 +149,8 @@ public void responseReceived(BaseResponse response) { }); - } catch (Exception e) { - updateState(channelUID, new StringType("Id could not be parsed")); + } catch (IllegalArgumentException e) { + updateState(channelUID, new StringType("BaseId could not be parsed")); } } break; @@ -194,8 +194,7 @@ private synchronized void initTransceiver() { transceiver.ShutDown(); } - transceiver = new EnOceanSerialTransceiver((String) getThing().getConfiguration().get(PATH), this, - scheduler, serialPortManager); + transceiver = new EnOceanSerialTransceiver((String) c.get(PATH), this, scheduler, serialPortManager); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "opening serial port..."); transceiver.Initialize(); @@ -250,13 +249,10 @@ public void responseReceived(RDVersionResponse response) { }); } catch (IOException e) { - logger.debug("error during bridge init occured", e); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Port could not be found"); } catch (PortInUseException e) { - logger.debug("error during bridge init occured", e); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Port already in use"); } catch (Exception e) { - logger.debug("error during bridge init occured", e); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Port could not be initialized"); return; } diff --git a/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java new file mode 100644 index 0000000000000..2f73535bf0cbf --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/handler/EnOceanClassicDeviceHandler.java @@ -0,0 +1,308 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.enocean.internal.handler; + +import static org.openhab.binding.enocean.internal.EnOceanBindingConstants.*; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.StopMoveType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.CommonTriggerEvents; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.type.ChannelKind; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.util.HexUtils; +import org.openhab.binding.enocean.internal.config.EnOceanActuatorConfig; +import org.openhab.binding.enocean.internal.config.EnOceanChannelRockerSwitchConfigBase.SwitchMode; +import org.openhab.binding.enocean.internal.config.EnOceanChannelRockerSwitchListenerConfig; +import org.openhab.binding.enocean.internal.config.EnOceanChannelVirtualRockerSwitchConfig; +import org.openhab.binding.enocean.internal.eep.EEP; +import org.openhab.binding.enocean.internal.eep.EEPFactory; +import org.openhab.binding.enocean.internal.eep.EEPType; +import org.openhab.binding.enocean.internal.messages.ESP3Packet; + +/** + * + * @author Daniel Weber - Initial contribution + * This class defines base functionality for sending eep messages. This class extends EnOceanBaseSensorHandler + * class as most actuator things send status or response messages, too. + */ +public class EnOceanClassicDeviceHandler extends EnOceanBaseActuatorHandler { + + // List of thing types which support sending of eep messages + public final static Set SUPPORTED_THING_TYPES = new HashSet( + Arrays.asList(THING_TYPE_CLASSICDEVICE)); + + private Hashtable channelConfigById; + private String currentEnOceanId; + private StringType lastTriggerEvent = StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED); + + public EnOceanClassicDeviceHandler(Thing thing) { + super(thing); + + channelConfigById = new Hashtable<>(); + } + + @Override + void initializeConfig() { + super.initializeConfig(); + ((EnOceanActuatorConfig) config).broadcastMessages = true; + ((EnOceanActuatorConfig) config).enoceanId = EMPTYENOCEANID; + } + + @Override + public long getSenderIdToListenTo() { + return Long.parseLong(currentEnOceanId, 16); + } + + @Override + public void channelLinked(@NonNull ChannelUID channelUID) { + super.channelLinked(channelUID); + + // if linked channel is a listening channel => put listener + String id = channelUID.getId(); + Channel channel = getThing().getChannel(id); + addListener(channel); + } + + @Override + public void thingUpdated(Thing thing) { + super.thingUpdated(thing); + + // it seems that there does not exist a channel update callback + // => remove all listeners and add them again + while (!channelConfigById.isEmpty()) { + removeListener(getThing().getChannel(channelConfigById.keys().nextElement())); + } + + getLinkedChannels().forEach(c -> { + if (!addListener(c)) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Wrong channel configuration"); + } + }); + } + + @Override + public void channelUnlinked(@NonNull ChannelUID channelUID) { + super.channelUnlinked(channelUID); + + // if unlinked channel is listening channel => remove listener + String id = channelUID.getId(); + Channel channel = getThing().getChannel(id); + removeListener(channel); + } + + protected boolean addListener(Channel channel) { + if (channel == null) { + return true; + } + + ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); + String id = channelTypeUID == null ? "" : channelTypeUID.getId(); + + if (id.startsWith(CHANNEL_ROCKERSWITCHLISTENER_START)) { + EnOceanChannelRockerSwitchListenerConfig config = channel.getConfiguration() + .as(EnOceanChannelRockerSwitchListenerConfig.class); + + try { + Long.parseLong(config.enoceanId, 16); + channelConfigById.put(channel.getUID().getId(), config); + currentEnOceanId = config.enoceanId; + getBridgeHandler().addPacketListener(this); + + return true; + } catch (Exception e) { + + } + + return false; + } + return true; + } + + protected void removeListener(Channel channel) { + if (channel == null) { + return; + } + + ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); + String id = channelTypeUID == null ? "" : channelTypeUID.getId(); + + if (id.startsWith(CHANNEL_ROCKERSWITCHLISTENER_START)) { + String channelId = channel.getUID().getId(); + + if (channelConfigById.containsKey(channelId)) { + currentEnOceanId = channelConfigById.get(channelId).enoceanId; + channelConfigById.remove(channelId); + getBridgeHandler().removePacketListener(this); + } + } + } + + @Override + protected State getCurrentState(String channelId) { + // Always use the same channelId of CHANNEL_VIRTUALSWITCHA + return super.getCurrentState(CHANNEL_VIRTUALSWITCHA); + } + + @Override + protected void setCurrentState(String channelId, State state) { + // Always use the same channelId of CHANNEL_VIRTUALSWITCHA + super.setCurrentState(CHANNEL_VIRTUALSWITCHA, state); + } + + @Override + protected Predicate stateFilter(EEPType eepType, byte[] senderId) { + return c -> { + ChannelTypeUID channelTypeUID = c.getChannelTypeUID(); + String id = channelTypeUID == null ? "" : channelTypeUID.getId(); + + return ChannelKind.STATE.equals(c.getKind()) && id.startsWith(CHANNEL_ROCKERSWITCHLISTENER_START) + && c.getConfiguration().as(EnOceanChannelRockerSwitchListenerConfig.class).enoceanId + .equalsIgnoreCase(HexUtils.bytesToHex(senderId)); + }; + } + + @Override + protected Predicate triggerFilter(EEPType eepType, byte[] senderId) { + return c -> { + ChannelTypeUID channelTypeUID = c.getChannelTypeUID(); + String id = channelTypeUID == null ? "" : channelTypeUID.getId(); + + return ChannelKind.TRIGGER.equals(c.getKind()) && id.startsWith(CHANNEL_ROCKERSWITCHLISTENER_START) + && c.getConfiguration().as(EnOceanChannelRockerSwitchListenerConfig.class).enoceanId + .equalsIgnoreCase(HexUtils.bytesToHex(senderId)); + }; + } + + @SuppressWarnings("unlikely-arg-type") + private StringType convertToReleasedCommand(StringType command) { + return command.equals(CommonTriggerEvents.DIR1_PRESSED) ? StringType.valueOf(CommonTriggerEvents.DIR1_RELEASED) + : StringType.valueOf(CommonTriggerEvents.DIR2_RELEASED); + } + + private StringType convertToPressedCommand(Command command, SwitchMode switchMode) { + if (command instanceof StringType) { + return (StringType) command; + } else if (command instanceof OnOffType) { + switch (switchMode) { + case RockerSwitch: + return (command == OnOffType.ON) ? StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED) + : StringType.valueOf(CommonTriggerEvents.DIR2_PRESSED); + case ToggleDir1: + return StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED); + case ToggleDir2: + return StringType.valueOf(CommonTriggerEvents.DIR2_PRESSED); + default: + return null; + } + } else if (command instanceof UpDownType) { + switch (switchMode) { + case RockerSwitch: + return (command == UpDownType.UP) ? StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED) + : StringType.valueOf(CommonTriggerEvents.DIR2_PRESSED); + case ToggleDir1: + return StringType.valueOf(CommonTriggerEvents.DIR1_PRESSED); + case ToggleDir2: + return StringType.valueOf(CommonTriggerEvents.DIR2_PRESSED); + default: + return null; + } + } else if (command instanceof StopMoveType) { + if (command == StopMoveType.STOP) { + return lastTriggerEvent; + } + } + + return null; + } + + @Override + public void handleCommand(@NonNull ChannelUID channelUID, @NonNull Command command) { + + // We must have a valid sendingEEPType and sender id to send commands + if (sendingEEPType == null || senderId == null || command == RefreshType.REFRESH) { + return; + } + + String channelId = channelUID.getId(); + Channel channel = getThing().getChannel(channelId); + // check if the channel is linked otherwise do nothing + if (channel == null || !getLinkedChannels().contains(channel)) { + return; + } + + ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); + String channelTypeId = (channelTypeUID != null) ? channelTypeUID.getId() : ""; + + if (channelTypeId.contains("Listener")) { + return; + } + + EnOceanChannelVirtualRockerSwitchConfig channelConfig = channel.getConfiguration() + .as(EnOceanChannelVirtualRockerSwitchConfig.class); + StringType result = convertToPressedCommand(command, channelConfig.getSwitchMode()); + + if (result != null) { + lastTriggerEvent = result; + + EEP eep = EEPFactory.createEEP(sendingEEPType); + eep.convertFromCommand(channelId, channelTypeId, result, channelState, channel.getConfiguration()); + + if (eep.hasData()) { + ESP3Packet press = eep.setSenderId(senderId).setDestinationId(destinationId) + .setSuppressRepeating(getConfiguration().suppressRepeating).getERP1Message(); + + getBridgeHandler().sendMessage(press, null); + + if (channelConfig.duration > 0) { + scheduler.schedule(() -> { + eep.convertFromCommand(channelId, channelTypeId, convertToReleasedCommand(lastTriggerEvent), + channelState, channel.getConfiguration()); + + ESP3Packet release = eep.getERP1Message(); + + getBridgeHandler().sendMessage(release, null); + + }, channelConfig.duration, TimeUnit.MILLISECONDS); + } + } + } + } + + @Override + public void handleRemoval() { + for (Channel channel : getLinkedChannels()) { + removeListener(channel); + } + currentEnOceanId = EMPTYENOCEANID; + super.handleRemoval(); + } +} diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BaseResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BaseResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BaseResponse.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/BaseResponse.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/CCMessage.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/CCMessage.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/CCMessage.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/CCMessage.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ERP1Message.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ERP1Message.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ERP1Message.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ERP1Message.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3Packet.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3Packet.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3Packet.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3Packet.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3PacketFactory.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3PacketFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3PacketFactory.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/ESP3PacketFactory.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDBaseIdResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDBaseIdResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDBaseIdResponse.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDBaseIdResponse.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDRepeaterResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDRepeaterResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDRepeaterResponse.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDRepeaterResponse.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDVersionResponse.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDVersionResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDVersionResponse.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/RDVersionResponse.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Response.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Response.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Response.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/messages/Response.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ESP3PacketListener.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ESP3PacketListener.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ESP3PacketListener.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ESP3PacketListener.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanSerialTransceiver.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanSerialTransceiver.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanSerialTransceiver.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanSerialTransceiver.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java similarity index 92% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java index 97b7527b327f5..b626ec80b6ecc 100644 --- a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java +++ b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/EnOceanTransceiver.java @@ -44,8 +44,8 @@ public abstract class EnOceanTransceiver { // Thread management - private Future readingTask; - private Future timeOut; + private Future readingTask = null; + private Future timeOut = null; private Logger logger = LoggerFactory.getLogger(EnOceanTransceiver.class); @@ -98,6 +98,10 @@ private synchronized void send() throws IOException { outputStream.write(b); outputStream.flush(); + if (timeOut != null) { + timeOut.cancel(true); + } + // slowdown sending of message to avoid hickups at receivers // Todo tweak sending intervall (250 ist just a first try) timeOut = scheduler.schedule(() -> { @@ -164,8 +168,12 @@ public void run() { } public void ShutDown() { - logger.debug("Interrupt rx Thread"); + + if (timeOut != null) { + timeOut.cancel(true); + } + if (readingTask != null) { readingTask.cancel(true); try { @@ -175,6 +183,7 @@ public void ShutDown() { } readingTask = null; + timeOut = null; listeners.clear(); teachInListener = null; errorListener = null; @@ -387,39 +396,34 @@ public void sendESP3Packet(ESP3Packet packet, ResponseListener pl = listeners.get(s); - if (pl != null) { - pl.forEach(l -> l.espPacketReceived(msg)); - } + long s = Long.parseLong(HexUtils.bytesToHex(senderId), 16); + HashSet pl = listeners.get(s); + if (pl != null) { + pl.forEach(l -> l.espPacketReceived(msg)); } - } catch (Exception e) { - logger.error("Exception in informListeners", e); } } diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/Helper.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/Helper.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/Helper.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/Helper.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ResponseListener.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ResponseListener.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ResponseListener.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ResponseListener.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ResponseListenerIgnoringTimeouts.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ResponseListenerIgnoringTimeouts.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ResponseListenerIgnoringTimeouts.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/ResponseListenerIgnoringTimeouts.java diff --git a/addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/TransceiverErrorListener.java b/bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/TransceiverErrorListener.java similarity index 100% rename from addons/binding/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/TransceiverErrorListener.java rename to bundles/org.openhab.binding.enocean/src/main/java/org/openhab/binding/enocean/internal/transceiver/TransceiverErrorListener.java diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/config/config.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/config/config.xml similarity index 100% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/config/config.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/config/config.xml diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/i18n/ocean.properties b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/i18n/ocean.properties similarity index 100% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/i18n/ocean.properties rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/i18n/ocean.properties diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/AutomatedMeterSensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/AutomatedMeterSensor.xml similarity index 97% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/AutomatedMeterSensor.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/AutomatedMeterSensor.xml index 880953cd2b1cf..0b5d021a11210 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/AutomatedMeterSensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/AutomatedMeterSensor.xml @@ -34,4 +34,4 @@
-
\ No newline at end of file +
diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/CentralCommand.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/CentralCommand.xml similarity index 100% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/CentralCommand.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/CentralCommand.xml diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/ClassicDevice.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/ClassicDevice.xml similarity index 100% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/ClassicDevice.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/ClassicDevice.xml diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/Contact.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/Contact.xml similarity index 98% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/Contact.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/Contact.xml index 43bfd200d7b3a..8d48a5c488932 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/Contact.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/Contact.xml @@ -35,4 +35,4 @@
-
\ No newline at end of file +
diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/GenericThing.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/GenericThing.xml similarity index 98% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/GenericThing.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/GenericThing.xml index c922d59908a2a..dbf64a32eeec3 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/GenericThing.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/GenericThing.xml @@ -54,4 +54,4 @@
-
\ No newline at end of file +
diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/LightSensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/LightSensor.xml similarity index 97% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/LightSensor.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/LightSensor.xml index 89ae62ce8aeb6..ffe6c2644906c 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/LightSensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/LightSensor.xml @@ -32,4 +32,4 @@
-
\ No newline at end of file +
diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/LightTemperatureOccupancySensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/LightTemperatureOccupancySensor.xml similarity index 98% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/LightTemperatureOccupancySensor.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/LightTemperatureOccupancySensor.xml index c957d0a4da8b3..d8b7513b33cb5 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/LightTemperatureOccupancySensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/LightTemperatureOccupancySensor.xml @@ -34,4 +34,4 @@
-
\ No newline at end of file +
diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/MeasurementSwitch.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/MeasurementSwitch.xml similarity index 99% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/MeasurementSwitch.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/MeasurementSwitch.xml index 64b7a163a3299..df06bdb5c4cb7 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/MeasurementSwitch.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/MeasurementSwitch.xml @@ -98,4 +98,4 @@
-
\ No newline at end of file +
diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/MechanicalHandle.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/MechanicalHandle.xml similarity index 100% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/MechanicalHandle.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/MechanicalHandle.xml diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/OccupancySensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/OccupancySensor.xml similarity index 97% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/OccupancySensor.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/OccupancySensor.xml index 5c6c695d1f06a..b2098239a431e 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/OccupancySensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/OccupancySensor.xml @@ -33,4 +33,4 @@
-
\ No newline at end of file +
diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/PushButton.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/PushButton.xml similarity index 97% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/PushButton.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/PushButton.xml index 87499093132e6..81efd31dba0a0 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/PushButton.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/PushButton.xml @@ -32,4 +32,4 @@
-
\ No newline at end of file +
diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/RockerSwitch.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/RockerSwitch.xml similarity index 100% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/RockerSwitch.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/RockerSwitch.xml diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/Rollershutter.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/Rollershutter.xml similarity index 98% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/Rollershutter.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/Rollershutter.xml index 7c7dc305e3b81..541d58f82ded1 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/Rollershutter.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/Rollershutter.xml @@ -61,4 +61,4 @@ -
\ No newline at end of file + diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/RoomOperatingPanel.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/RoomOperatingPanel.xml similarity index 99% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/RoomOperatingPanel.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/RoomOperatingPanel.xml index ffbf1a5943426..48a3b3e33acde 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/RoomOperatingPanel.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/RoomOperatingPanel.xml @@ -63,4 +63,4 @@ - \ No newline at end of file + diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/TemperatureHumiditySensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/TemperatureHumiditySensor.xml similarity index 98% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/TemperatureHumiditySensor.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/TemperatureHumiditySensor.xml index 88455623cd182..fe27114fa49ee 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/TemperatureHumiditySensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/TemperatureHumiditySensor.xml @@ -34,4 +34,4 @@ - \ No newline at end of file + diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/TemperatureSensor.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/TemperatureSensor.xml similarity index 99% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/TemperatureSensor.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/TemperatureSensor.xml index 0070c56ef0ed4..b6165db6be345 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/TemperatureSensor.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/TemperatureSensor.xml @@ -55,4 +55,4 @@ - \ No newline at end of file + diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/Thermostat.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/Thermostat.xml new file mode 100644 index 0000000000000..6b34267ae21c8 --- /dev/null +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/Thermostat.xml @@ -0,0 +1,57 @@ + + + + + + + + + + Sensor and actuator to control radiator thermostats + + + + + EnOceanId of device this thing belongs to + true + + + + Id is used to generate the EnOcean Id (Int between [1-127]). If not specified the next free Id will be determined by bridge + true + + + + EEP which is used by sensor + + + + "A5_20_04" + true + true + + + + + + + "A5_20_04" + true + true + + + + false + + + + false + + + + + + diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/bridge.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/bridge.xml similarity index 100% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/bridge.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/bridge.xml diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/channels.xml similarity index 75% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/channels.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/channels.xml index a594a72d333e1..192aed67fbbad 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/channels.xml @@ -162,6 +162,13 @@ Used energy in Kilowatt hours + + + + Filter out increases more than 10.0 kWh and decreases less than 1.0 kWh + false + + @@ -312,4 +319,100 @@ - \ No newline at end of file + + Number + + Indicates the actual percent position of the valve. + thermostat + + + + + Switch + + Enables or disables buttons on the device. + thermostat + + + + Switch + + Enables or disables temperature measurement on the device. + thermostat + + + + Number + + Orientation of the display. + thermostat + + + + + + + + + + + + Number:Temperature + + Desired temperature + + + + + Number:Temperature + + Water temperature in the radiator input + + + + + Number + + Failure code + thermostat + + + + + Number + + Defines the cyclic wake-up time. The time corresponding to the number has to be looked up. + thermostat + + + + + Number + + Command to be sent to actuator + thermostat + + + + + + + + + + + + trigger + + Is triggered when the actuator wakes up from sleep and asks for the current status. + + + + + Switch + + You can send telegrams to the device by switching this channel on + thermostat + + + diff --git a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/gernericChannels.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/gernericChannels.xml similarity index 98% rename from addons/binding/org.openhab.binding.enocean/ESH-INF/thing/gernericChannels.xml rename to bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/gernericChannels.xml index 52817a9b876f9..4aad1b40fb5e5 100644 --- a/addons/binding/org.openhab.binding.enocean/ESH-INF/thing/gernericChannels.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/gernericChannels.xml @@ -59,4 +59,4 @@ - \ No newline at end of file + diff --git a/bundles/org.openhab.binding.exec/.classpath b/bundles/org.openhab.binding.exec/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.exec/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.exec/.project b/bundles/org.openhab.binding.exec/.project new file mode 100644 index 0000000000000..023aa0fcb7ceb --- /dev/null +++ b/bundles/org.openhab.binding.exec/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.exec + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.dsmr.test/NOTICE b/bundles/org.openhab.binding.exec/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.dsmr.test/NOTICE rename to bundles/org.openhab.binding.exec/NOTICE diff --git a/addons/binding/org.openhab.binding.exec/README.md b/bundles/org.openhab.binding.exec/README.md similarity index 100% rename from addons/binding/org.openhab.binding.exec/README.md rename to bundles/org.openhab.binding.exec/README.md diff --git a/bundles/org.openhab.binding.exec/pom.xml b/bundles/org.openhab.binding.exec/pom.xml new file mode 100644 index 0000000000000..fdfe2159b3522 --- /dev/null +++ b/bundles/org.openhab.binding.exec/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.exec + + openHAB Add-ons :: Bundles :: Exec Binding + + diff --git a/addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/ExecBindingConstants.java b/bundles/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/ExecBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/ExecBindingConstants.java rename to bundles/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/ExecBindingConstants.java diff --git a/addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/ExecHandlerFactory.java b/bundles/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/ExecHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/ExecHandlerFactory.java rename to bundles/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/ExecHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/handler/ExecHandler.java b/bundles/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/handler/ExecHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/handler/ExecHandler.java rename to bundles/org.openhab.binding.exec/src/main/java/org/openhab/binding/exec/internal/handler/ExecHandler.java diff --git a/addons/binding/org.openhab.binding.exec/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.exec/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.exec/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.exec/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.exec/ESH-INF/i18n/exec_de.properties b/bundles/org.openhab.binding.exec/src/main/resources/ESH-INF/i18n/exec_de.properties similarity index 100% rename from addons/binding/org.openhab.binding.exec/ESH-INF/i18n/exec_de.properties rename to bundles/org.openhab.binding.exec/src/main/resources/ESH-INF/i18n/exec_de.properties diff --git a/addons/binding/org.openhab.binding.exec/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.exec/src/main/resources/ESH-INF/thing/thing-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.exec/ESH-INF/thing/thing-types.xml rename to bundles/org.openhab.binding.exec/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/bundles/org.openhab.binding.feed/.classpath b/bundles/org.openhab.binding.feed/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.feed/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.feed/.project b/bundles/org.openhab.binding.feed/.project new file mode 100644 index 0000000000000..855ac39f7f0a1 --- /dev/null +++ b/bundles/org.openhab.binding.feed/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.feed + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.dsmr/NOTICE b/bundles/org.openhab.binding.feed/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.dsmr/NOTICE rename to bundles/org.openhab.binding.feed/NOTICE diff --git a/addons/binding/org.openhab.binding.feed/README.md b/bundles/org.openhab.binding.feed/README.md similarity index 100% rename from addons/binding/org.openhab.binding.feed/README.md rename to bundles/org.openhab.binding.feed/README.md diff --git a/bundles/org.openhab.binding.feed/pom.xml b/bundles/org.openhab.binding.feed/pom.xml new file mode 100644 index 0000000000000..db55ee8875654 --- /dev/null +++ b/bundles/org.openhab.binding.feed/pom.xml @@ -0,0 +1,37 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.feed + + openHAB Add-ons :: Bundles :: Feed Binding + + + + com.rometools + rome + 1.12.0 + provided + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.jdom + 2.0.6_1 + provided + + + org.slf4j + slf4j-api + + + + + + diff --git a/addons/binding/org.openhab.binding.feed/src/main/java/org/openhab/binding/feed/internal/FeedBindingConstants.java b/bundles/org.openhab.binding.feed/src/main/java/org/openhab/binding/feed/internal/FeedBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.feed/src/main/java/org/openhab/binding/feed/internal/FeedBindingConstants.java rename to bundles/org.openhab.binding.feed/src/main/java/org/openhab/binding/feed/internal/FeedBindingConstants.java diff --git a/addons/binding/org.openhab.binding.feed/src/main/java/org/openhab/binding/feed/internal/FeedHandlerFactory.java b/bundles/org.openhab.binding.feed/src/main/java/org/openhab/binding/feed/internal/FeedHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.feed/src/main/java/org/openhab/binding/feed/internal/FeedHandlerFactory.java rename to bundles/org.openhab.binding.feed/src/main/java/org/openhab/binding/feed/internal/FeedHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.feed/src/main/java/org/openhab/binding/feed/internal/handler/FeedHandler.java b/bundles/org.openhab.binding.feed/src/main/java/org/openhab/binding/feed/internal/handler/FeedHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.feed/src/main/java/org/openhab/binding/feed/internal/handler/FeedHandler.java rename to bundles/org.openhab.binding.feed/src/main/java/org/openhab/binding/feed/internal/handler/FeedHandler.java diff --git a/addons/binding/org.openhab.binding.feed/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.feed/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.feed/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.feed/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.feed/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.feed/src/main/resources/ESH-INF/thing/thing-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.feed/ESH-INF/thing/thing-types.xml rename to bundles/org.openhab.binding.feed/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/bundles/org.openhab.binding.fsinternetradio/.classpath b/bundles/org.openhab.binding.fsinternetradio/.classpath index a5d95095ccaaf..1a0c5608f3440 100644 --- a/bundles/org.openhab.binding.fsinternetradio/.classpath +++ b/bundles/org.openhab.binding.fsinternetradio/.classpath @@ -28,5 +28,11 @@
+ + + + + + diff --git a/bundles/org.openhab.binding.ftpupload/.classpath b/bundles/org.openhab.binding.ftpupload/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.ftpupload/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.ftpupload/.project b/bundles/org.openhab.binding.ftpupload/.project new file mode 100644 index 0000000000000..b2472d2e5ecae --- /dev/null +++ b/bundles/org.openhab.binding.ftpupload/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.ftpupload + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.ftpupload/NOTICE b/bundles/org.openhab.binding.ftpupload/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/NOTICE rename to bundles/org.openhab.binding.ftpupload/NOTICE diff --git a/addons/binding/org.openhab.binding.ftpupload/README.md b/bundles/org.openhab.binding.ftpupload/README.md similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/README.md rename to bundles/org.openhab.binding.ftpupload/README.md diff --git a/bundles/org.openhab.binding.ftpupload/pom.xml b/bundles/org.openhab.binding.ftpupload/pom.xml new file mode 100644 index 0000000000000..c6325e5652eec --- /dev/null +++ b/bundles/org.openhab.binding.ftpupload/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.addons.bundles + org.openhab.binding.ftpupload + 2.5.0-SNAPSHOT + + openHAB Add-ons :: Bundles :: FTP Upload Binding + + + + org.apache.ftpserver + ftplet-api + 1.1.0 + provided + + + org.apache.ftpserver + ftpserver-core + 1.1.0 + provided + + + org.apache.mina + mina-core + 2.0.16 + provided + + + + diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadBindingConstants.java b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadBindingConstants.java rename to bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadBindingConstants.java diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadHandlerFactory.java b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadHandlerFactory.java rename to bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/FtpUploadHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/config/FtpUploadConfig.java b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/config/FtpUploadConfig.java similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/config/FtpUploadConfig.java rename to bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/config/FtpUploadConfig.java diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUser.java b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUser.java similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUser.java rename to bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUser.java diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUserManager.java b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUserManager.java similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUserManager.java rename to bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FTPUserManager.java diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServer.java b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServer.java similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServer.java rename to bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServer.java diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServerEventListener.java b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServerEventListener.java similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServerEventListener.java rename to bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/FtpServerEventListener.java diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFileSystemView.java b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFileSystemView.java similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFileSystemView.java rename to bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFileSystemView.java diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFtpFile.java b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFtpFile.java similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFtpFile.java rename to bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/SimpleFtpFile.java diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/UsernamePassword.java b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/UsernamePassword.java similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/UsernamePassword.java rename to bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/ftp/UsernamePassword.java diff --git a/addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/handler/FtpUploadHandler.java b/bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/handler/FtpUploadHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/handler/FtpUploadHandler.java rename to bundles/org.openhab.binding.ftpupload/src/main/java/org/openhab/binding/ftpupload/internal/handler/FtpUploadHandler.java diff --git a/addons/binding/org.openhab.binding.ftpupload/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.ftpupload/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.ftpupload/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.ftpupload/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.ftpupload/src/main/resources/ESH-INF/thing/thing-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.ftpupload/ESH-INF/thing/thing-types.xml rename to bundles/org.openhab.binding.ftpupload/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/bundles/org.openhab.binding.globalcache/src/main/java/org/openhab/binding/globalcache/internal/handler/GlobalCacheHandler.java b/bundles/org.openhab.binding.globalcache/src/main/java/org/openhab/binding/globalcache/internal/handler/GlobalCacheHandler.java index eb64f43ad3cb3..14e8456678823 100644 --- a/bundles/org.openhab.binding.globalcache/src/main/java/org/openhab/binding/globalcache/internal/handler/GlobalCacheHandler.java +++ b/bundles/org.openhab.binding.globalcache/src/main/java/org/openhab/binding/globalcache/internal/handler/GlobalCacheHandler.java @@ -130,7 +130,7 @@ public void dispose() { @Override public void handleCommand(ChannelUID channelUID, Command command) { if (command == null) { - logger.warn("Command passed to handler for thing {} is null"); + logger.warn("Command passed to handler for thing {} is null", thingID()); return; } diff --git a/bundles/org.openhab.binding.harmonyhub/src/main/java/org/openhab/binding/harmonyhub/internal/discovery/HarmonyHubDiscoveryService.java b/bundles/org.openhab.binding.harmonyhub/src/main/java/org/openhab/binding/harmonyhub/internal/discovery/HarmonyHubDiscoveryService.java index 22532ec957642..7e32c28c95efd 100644 --- a/bundles/org.openhab.binding.harmonyhub/src/main/java/org/openhab/binding/harmonyhub/internal/discovery/HarmonyHubDiscoveryService.java +++ b/bundles/org.openhab.binding.harmonyhub/src/main/java/org/openhab/binding/harmonyhub/internal/discovery/HarmonyHubDiscoveryService.java @@ -17,6 +17,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.io.Reader; import java.io.StringReader; import java.net.DatagramPacket; import java.net.DatagramSocket; @@ -235,8 +236,9 @@ public void stop() { private void run() { while (running) { - try (Socket socket = serverSocket.accept()) { - BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + try (Socket socket = serverSocket.accept(); + Reader isr = new InputStreamReader(socket.getInputStream()); + BufferedReader in = new BufferedReader(isr)) { String input; while ((input = in.readLine()) != null) { if (!running) { diff --git a/bundles/org.openhab.binding.hyperion/src/main/java/org/openhab/binding/hyperion/internal/connection/JsonTcpConnection.java b/bundles/org.openhab.binding.hyperion/src/main/java/org/openhab/binding/hyperion/internal/connection/JsonTcpConnection.java index d93d212f677c4..f8edad2b68ad5 100644 --- a/bundles/org.openhab.binding.hyperion/src/main/java/org/openhab/binding/hyperion/internal/connection/JsonTcpConnection.java +++ b/bundles/org.openhab.binding.hyperion/src/main/java/org/openhab/binding/hyperion/internal/connection/JsonTcpConnection.java @@ -16,6 +16,7 @@ import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.io.Reader; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; @@ -59,9 +60,10 @@ public void setAddress(InetAddress address) { public String send(String json) throws IOException { String response = null; - try (Socket hyperionServer = new Socket(address, port)) { - DataOutputStream outToServer = new DataOutputStream(hyperionServer.getOutputStream()); - BufferedReader inFromServer = new BufferedReader(new InputStreamReader(hyperionServer.getInputStream())); + try (Socket hyperionServer = new Socket(address, port); + DataOutputStream outToServer = new DataOutputStream(hyperionServer.getOutputStream()); + Reader isr = new InputStreamReader(hyperionServer.getInputStream()); + BufferedReader inFromServer = new BufferedReader(isr)) { logger.debug("Sending: {}", json); outToServer.writeBytes(json + System.lineSeparator()); outToServer.flush(); diff --git a/bundles/org.openhab.binding.ihc/.classpath b/bundles/org.openhab.binding.ihc/.classpath new file mode 100644 index 0000000000000..1a0c5608f3440 --- /dev/null +++ b/bundles/org.openhab.binding.ihc/.classpath @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.ihc/.project b/bundles/org.openhab.binding.ihc/.project new file mode 100644 index 0000000000000..48c6541723b86 --- /dev/null +++ b/bundles/org.openhab.binding.ihc/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.ihc + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.ihc/NOTICE b/bundles/org.openhab.binding.ihc/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.ihc/NOTICE rename to bundles/org.openhab.binding.ihc/NOTICE diff --git a/addons/binding/org.openhab.binding.ihc/README.md b/bundles/org.openhab.binding.ihc/README.md old mode 100755 new mode 100644 similarity index 100% rename from addons/binding/org.openhab.binding.ihc/README.md rename to bundles/org.openhab.binding.ihc/README.md diff --git a/addons/binding/org.openhab.binding.ihc/wsdl/airlinkmanagement.wsdl b/bundles/org.openhab.binding.ihc/contrib/wsdl/airlinkmanagement.wsdl similarity index 100% rename from addons/binding/org.openhab.binding.ihc/wsdl/airlinkmanagement.wsdl rename to bundles/org.openhab.binding.ihc/contrib/wsdl/airlinkmanagement.wsdl diff --git a/addons/binding/org.openhab.binding.ihc/wsdl/authentication.wsdl b/bundles/org.openhab.binding.ihc/contrib/wsdl/authentication.wsdl old mode 100755 new mode 100644 similarity index 98% rename from addons/binding/org.openhab.binding.ihc/wsdl/authentication.wsdl rename to bundles/org.openhab.binding.ihc/contrib/wsdl/authentication.wsdl index f935dfff63ed6..a0db4f2dd39ae --- a/addons/binding/org.openhab.binding.ihc/wsdl/authentication.wsdl +++ b/bundles/org.openhab.binding.ihc/contrib/wsdl/authentication.wsdl @@ -1,119 +1,119 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/addons/binding/org.openhab.binding.ihc/wsdl/configuration.wsdl b/bundles/org.openhab.binding.ihc/contrib/wsdl/configuration.wsdl similarity index 100% rename from addons/binding/org.openhab.binding.ihc/wsdl/configuration.wsdl rename to bundles/org.openhab.binding.ihc/contrib/wsdl/configuration.wsdl diff --git a/addons/binding/org.openhab.binding.ihc/wsdl/controller.wsdl b/bundles/org.openhab.binding.ihc/contrib/wsdl/controller.wsdl old mode 100755 new mode 100644 similarity index 98% rename from addons/binding/org.openhab.binding.ihc/wsdl/controller.wsdl rename to bundles/org.openhab.binding.ihc/contrib/wsdl/controller.wsdl index d88a270b08a1a..8cef71964125b --- a/addons/binding/org.openhab.binding.ihc/wsdl/controller.wsdl +++ b/bundles/org.openhab.binding.ihc/contrib/wsdl/controller.wsdl @@ -1,288 +1,288 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/addons/binding/org.openhab.binding.ihc/wsdl/emailcontrollog.wsdl b/bundles/org.openhab.binding.ihc/contrib/wsdl/emailcontrollog.wsdl similarity index 100% rename from addons/binding/org.openhab.binding.ihc/wsdl/emailcontrollog.wsdl rename to bundles/org.openhab.binding.ihc/contrib/wsdl/emailcontrollog.wsdl diff --git a/addons/binding/org.openhab.binding.ihc/wsdl/module.wsdl b/bundles/org.openhab.binding.ihc/contrib/wsdl/module.wsdl similarity index 100% rename from addons/binding/org.openhab.binding.ihc/wsdl/module.wsdl rename to bundles/org.openhab.binding.ihc/contrib/wsdl/module.wsdl diff --git a/addons/binding/org.openhab.binding.ihc/wsdl/notificationmanager.wsdl b/bundles/org.openhab.binding.ihc/contrib/wsdl/notificationmanager.wsdl similarity index 100% rename from addons/binding/org.openhab.binding.ihc/wsdl/notificationmanager.wsdl rename to bundles/org.openhab.binding.ihc/contrib/wsdl/notificationmanager.wsdl diff --git a/addons/binding/org.openhab.binding.ihc/wsdl/resourceinteraction.wsdl b/bundles/org.openhab.binding.ihc/contrib/wsdl/resourceinteraction.wsdl old mode 100755 new mode 100644 similarity index 98% rename from addons/binding/org.openhab.binding.ihc/wsdl/resourceinteraction.wsdl rename to bundles/org.openhab.binding.ihc/contrib/wsdl/resourceinteraction.wsdl index a6a79916170a7..76e17d1921d7e --- a/addons/binding/org.openhab.binding.ihc/wsdl/resourceinteraction.wsdl +++ b/bundles/org.openhab.binding.ihc/contrib/wsdl/resourceinteraction.wsdl @@ -1,588 +1,588 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/addons/binding/org.openhab.binding.ihc/wsdl/timemanager.wsdl b/bundles/org.openhab.binding.ihc/contrib/wsdl/timemanager.wsdl similarity index 100% rename from addons/binding/org.openhab.binding.ihc/wsdl/timemanager.wsdl rename to bundles/org.openhab.binding.ihc/contrib/wsdl/timemanager.wsdl diff --git a/addons/binding/org.openhab.binding.ihc/wsdl/usermanager.wsdl b/bundles/org.openhab.binding.ihc/contrib/wsdl/usermanager.wsdl similarity index 100% rename from addons/binding/org.openhab.binding.ihc/wsdl/usermanager.wsdl rename to bundles/org.openhab.binding.ihc/contrib/wsdl/usermanager.wsdl diff --git a/bundles/org.openhab.binding.ihc/pom.xml b/bundles/org.openhab.binding.ihc/pom.xml new file mode 100644 index 0000000000000..606a5dc7c6645 --- /dev/null +++ b/bundles/org.openhab.binding.ihc/pom.xml @@ -0,0 +1,37 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.ihc + + openHAB Add-ons :: Bundles :: IHC Binding + + + + org.apache.httpcomponents + httpcore-osgi + 4.4.10 + provided + + + org.apache.httpcomponents + httpclient-osgi + 4.5.8 + provided + + + commons-logging + commons-logging + 1.2 + provided + + + + diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ButtonPressDurationDetector.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ButtonPressDurationDetector.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ButtonPressDurationDetector.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ButtonPressDurationDetector.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ChannelUtils.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ChannelUtils.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ChannelUtils.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ChannelUtils.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/EnumDictionary.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/EnumDictionary.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/EnumDictionary.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/EnumDictionary.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/IhcBindingConstants.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/IhcBindingConstants.java old mode 100755 new mode 100644 similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/IhcBindingConstants.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/IhcBindingConstants.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/IhcHandlerFactory.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/IhcHandlerFactory.java old mode 100755 new mode 100644 similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/IhcHandlerFactory.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/IhcHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/SignalLevelConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/SignalLevelConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/SignalLevelConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/SignalLevelConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/config/ChannelParams.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/config/ChannelParams.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/config/ChannelParams.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/config/ChannelParams.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/config/IhcConfiguration.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/config/IhcConfiguration.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/config/IhcConfiguration.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/config/IhcConfiguration.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/Converter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/Converter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/Converter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/Converter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/ConverterAdditionalInfo.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/ConverterAdditionalInfo.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/ConverterAdditionalInfo.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/ConverterAdditionalInfo.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/ConverterFactory.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/ConverterFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/ConverterFactory.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/ConverterFactory.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSDateValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSDateValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSDateValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSDateValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSTimeValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSTimeValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSTimeValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSTimeValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSBooleanValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSBooleanValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSBooleanValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSBooleanValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSEnumValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSEnumValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSEnumValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSEnumValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSFloatingPointValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSFloatingPointValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSFloatingPointValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSFloatingPointValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSIntegerValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSIntegerValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSIntegerValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSIntegerValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSTimerValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSTimerValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSTimerValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSTimerValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSWeekdayValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSWeekdayValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSWeekdayValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSWeekdayValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSBooleanValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSBooleanValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSBooleanValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSBooleanValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSIntegerValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSIntegerValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSIntegerValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSIntegerValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSBooleanValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSBooleanValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSBooleanValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSBooleanValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSIntegerValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSIntegerValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSIntegerValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSIntegerValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/PercentTypeWSIntegerValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/PercentTypeWSIntegerValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/PercentTypeWSIntegerValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/PercentTypeWSIntegerValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/StringTypeWSEnumValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/StringTypeWSEnumValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/StringTypeWSEnumValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/StringTypeWSEnumValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSBooleanValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSBooleanValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSBooleanValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSBooleanValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSIntegerValueConverter.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSIntegerValueConverter.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSIntegerValueConverter.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSIntegerValueConverter.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/handler/IhcHandler.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/handler/IhcHandler.java old mode 100755 new mode 100644 similarity index 97% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/handler/IhcHandler.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/handler/IhcHandler.java index 7a9e6e6b6dc10..864f6153cb02e --- a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/handler/IhcHandler.java +++ b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/handler/IhcHandler.java @@ -428,25 +428,21 @@ private void sendPulseCommand(ChannelUID channelUID, ChannelParams params, WSRes // set resource to ON logger.debug("Update resource value (inverted output={}): {}", params.isInverted(), valOn); if (updateResource(valOn)) { - scheduler.submit(() -> { - // sleep a while - try { - logger.debug("Sleeping: {}ms", pulseWidth); - Thread.sleep(pulseWidth); - } catch (InterruptedException e) { - // do nothing - } - // set resource back to OFF - - logger.debug("Update resource value (inverted output={}): {}", params.isInverted(), valOff); - try { - if (!updateResource(valOff)) { - logger.warn("Channel {} update to resource '{}' failed.", channelUID, valOff); + logger.debug("Sleeping: {}ms", pulseWidth); + scheduler.schedule(new Runnable() { + @Override + public void run() { + // set resource back to OFF + logger.debug("Update resource value (inverted output={}): {}", params.isInverted(), valOff); + try { + if (!updateResource(valOff)) { + logger.warn("Channel {} update to resource '{}' failed.", channelUID, valOff); + } + } catch (IhcExecption e) { + logger.error("Can't update channel '{}' value, cause ", channelUID, e.getMessage(), e); } - } catch (IhcExecption e) { - logger.error("Can't update channel '{}' value, cause ", channelUID, e.getMessage(), e); } - }); + }, pulseWidth, TimeUnit.MILLISECONDS); } else { logger.warn("Channel {} update failed.", channelUID); } diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/profiles/IhcProfileFactory.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/profiles/IhcProfileFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/profiles/IhcProfileFactory.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/profiles/IhcProfileFactory.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/profiles/IhcProfiles.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/profiles/IhcProfiles.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/profiles/IhcProfiles.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/profiles/IhcProfiles.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/profiles/PushButtonToCommandProfile.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/profiles/PushButtonToCommandProfile.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/profiles/PushButtonToCommandProfile.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/profiles/PushButtonToCommandProfile.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/IhcClient.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/IhcClient.java similarity index 96% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/IhcClient.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/IhcClient.java index 706b5c01ac85d..10abfdca4a264 100644 --- a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/IhcClient.java +++ b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/IhcClient.java @@ -20,7 +20,6 @@ import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -536,38 +535,32 @@ private void mysleep(long milli) { } private void sendErrorEvent(IhcExecption err) { - Iterator iterator = eventListeners.iterator(); - - while (iterator.hasNext()) { + eventListeners.forEach(listener -> { try { - iterator.next().errorOccured(err); + listener.errorOccured(err); } catch (RuntimeException e) { - logger.debug("Event listener invoking error. ", e); + logger.debug("Event listener invoking error.", e); } - } + }); } private void sendControllerStateUpdateEvent(WSControllerState state) { - Iterator iterator = eventListeners.iterator(); - - while (iterator.hasNext()) { + eventListeners.forEach(listener -> { try { - iterator.next().statusUpdateReceived(state); + listener.statusUpdateReceived(state); } catch (RuntimeException e) { - logger.debug("Event listener invoking error. ", e); + logger.debug("Event listener invoking error.", e); } - } + }); } private void sendResourceValueUpdateEvent(WSResourceValue value) { - Iterator iterator = eventListeners.iterator(); - - while (iterator.hasNext()) { + eventListeners.forEach(listener -> { try { - iterator.next().resourceValueUpdateReceived(value); + listener.resourceValueUpdateReceived(value); } catch (RuntimeException e) { - logger.debug("Event listener invoking error. ", e); + logger.debug("Event listener invoking error.", e); } - } + }); } } diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/IhcEventListener.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/IhcEventListener.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/IhcEventListener.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/IhcEventListener.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSControllerState.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSControllerState.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSControllerState.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSControllerState.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSDate.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSDate.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSDate.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSDate.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSFile.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSFile.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSFile.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSFile.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSLoginResult.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSLoginResult.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSLoginResult.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSLoginResult.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSNumberOfSegments.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSNumberOfSegments.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSNumberOfSegments.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSNumberOfSegments.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSProjectInfo.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSProjectInfo.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSProjectInfo.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSProjectInfo.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSRFDevice.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSRFDevice.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSRFDevice.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSRFDevice.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSSegmentationSize.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSSegmentationSize.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSSegmentationSize.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSSegmentationSize.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSSystemInfo.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSSystemInfo.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSSystemInfo.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSSystemInfo.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSTimeManagerSettings.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSTimeManagerSettings.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSTimeManagerSettings.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSTimeManagerSettings.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSUser.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSUser.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSUser.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSUser.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSUserGroup.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSUserGroup.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSUserGroup.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/WSUserGroup.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/XPathUtils.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/XPathUtils.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/XPathUtils.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/datatypes/XPathUtils.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/exeptions/ConversionException.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/exeptions/ConversionException.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/exeptions/ConversionException.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/exeptions/ConversionException.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/exeptions/IhcExecption.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/exeptions/IhcExecption.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/exeptions/IhcExecption.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/exeptions/IhcExecption.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/http/IhcConnectionPool.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/http/IhcConnectionPool.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/http/IhcConnectionPool.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/http/IhcConnectionPool.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/http/IhcHttpsClient.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/http/IhcHttpsClient.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/http/IhcHttpsClient.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/http/IhcHttpsClient.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/projectfile/IhcEnumValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/projectfile/IhcEnumValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/projectfile/IhcEnumValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/projectfile/IhcEnumValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/projectfile/ProjectFileUtils.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/projectfile/ProjectFileUtils.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/projectfile/ProjectFileUtils.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/projectfile/ProjectFileUtils.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSBooleanValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSBooleanValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSBooleanValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSBooleanValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSDateValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSDateValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSDateValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSDateValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSEnumValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSEnumValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSEnumValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSEnumValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSFloatingPointValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSFloatingPointValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSFloatingPointValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSFloatingPointValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSIntegerValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSIntegerValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSIntegerValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSIntegerValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSPhoneNumberValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSPhoneNumberValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSPhoneNumberValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSPhoneNumberValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSResourceValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSResourceValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSResourceValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSResourceValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSSceneDimmerValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSSceneDimmerValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSSceneDimmerValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSSceneDimmerValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSSceneRelayValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSSceneRelayValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSSceneRelayValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSSceneRelayValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSSceneShutterSimpleValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSSceneShutterSimpleValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSSceneShutterSimpleValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSSceneShutterSimpleValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSTimeValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSTimeValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSTimeValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSTimeValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSTimerValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSTimerValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSTimerValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSTimerValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSWeekdayValue.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSWeekdayValue.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSWeekdayValue.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/resourcevalues/WSWeekdayValue.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcAirlinkManagementService.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcAirlinkManagementService.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcAirlinkManagementService.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcAirlinkManagementService.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcAuthenticationService.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcAuthenticationService.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcAuthenticationService.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcAuthenticationService.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcBaseService.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcBaseService.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcBaseService.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcBaseService.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcConfigurationService.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcConfigurationService.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcConfigurationService.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcConfigurationService.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcControllerService.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcControllerService.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcControllerService.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcControllerService.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionService.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionService.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionService.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionService.java diff --git a/addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcTimeService.java b/bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcTimeService.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcTimeService.java rename to bundles/org.openhab.binding.ihc/src/main/java/org/openhab/binding/ihc/internal/ws/services/IhcTimeService.java diff --git a/addons/binding/org.openhab.binding.ihc/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.ihc/src/main/resources/ESH-INF/binding/binding.xml old mode 100755 new mode 100644 similarity index 100% rename from addons/binding/org.openhab.binding.ihc/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.ihc/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.ihc/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.ihc/src/main/resources/ESH-INF/thing/channels.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc/ESH-INF/thing/channels.xml rename to bundles/org.openhab.binding.ihc/src/main/resources/ESH-INF/thing/channels.xml diff --git a/addons/binding/org.openhab.binding.ihc/ESH-INF/thing/controller.xml b/bundles/org.openhab.binding.ihc/src/main/resources/ESH-INF/thing/controller.xml old mode 100755 new mode 100644 similarity index 100% rename from addons/binding/org.openhab.binding.ihc/ESH-INF/thing/controller.xml rename to bundles/org.openhab.binding.ihc/src/main/resources/ESH-INF/thing/controller.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ButtonPressDurationDetectorTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ButtonPressDurationDetectorTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ButtonPressDurationDetectorTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ButtonPressDurationDetectorTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/SignalLevelConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/SignalLevelConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/SignalLevelConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/SignalLevelConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSDateValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSDateValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSDateValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSDateValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSTimeValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSTimeValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSTimeValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DateTimeTypeWSTimeValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSBooleanValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSBooleanValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSBooleanValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSBooleanValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSEnumValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSEnumValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSEnumValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSEnumValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSFloatingPointValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSFloatingPointValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSFloatingPointValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSFloatingPointValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSIntegerValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSIntegerValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSIntegerValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSIntegerValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSTimerValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSTimerValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSTimerValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSTimerValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSWeekdayValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSWeekdayValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSWeekdayValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/DecimalTypeWSWeekdayValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSBooleanValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSBooleanValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSBooleanValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSBooleanValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSIntegerValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSIntegerValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSIntegerValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/OnOffTypeWSIntegerValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSBooleanValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSBooleanValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSBooleanValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSBooleanValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSIntegerValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSIntegerValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSIntegerValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/OpenClosedTypeWSIntegerValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/PercentTypeWSIntegerValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/PercentTypeWSIntegerValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/PercentTypeWSIntegerValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/PercentTypeWSIntegerValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/StringTypeWSEnumValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/StringTypeWSEnumValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/StringTypeWSEnumValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/StringTypeWSEnumValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSBooleanValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSBooleanValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSBooleanValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSBooleanValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSIntegerValueConverterTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSIntegerValueConverterTest.java similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSIntegerValueConverterTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/converters/UpDownTypeWSIntegerValueConverterTest.java diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/IhcClientTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/IhcClientTest.java similarity index 89% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/IhcClientTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/IhcClientTest.java index 416503d0aef83..12aa35af2d5f0 100644 --- a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/IhcClientTest.java +++ b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/IhcClientTest.java @@ -45,8 +45,8 @@ public void setUp() throws IhcExecption, SocketTimeoutException { doReturn(2).when(ihcClient).getProjectNumberOfSegments(); doReturn(100).when(ihcClient).getProjectSegmentationSize(); - final String segment0 = ResourceFileUtils.getFileContent("src/test/resources/Segment0.base64"); - final String segment1 = ResourceFileUtils.getFileContent("src/test/resources/Segment1.base64"); + final String segment0 = ResourceFileUtils.getFileContent("Segment0.base64"); + final String segment1 = ResourceFileUtils.getFileContent("Segment1.base64"); WSFile file0 = new WSFile(segment0.getBytes(), ""); doReturn(file0).when(ihcClient).getProjectSegment(ArgumentMatchers.eq(0), ArgumentMatchers.eq(111), @@ -58,10 +58,9 @@ public void setUp() throws IhcExecption, SocketTimeoutException { @Test public void loadProjectFileFromControllerTest() throws IhcExecption, UnsupportedEncodingException { - final String ExpectedFileContent = ResourceFileUtils - .getFileContent("src/test/resources/ProjectFileContent.txt"); + final String expectedFileContent = ResourceFileUtils.getFileContent("ProjectFileContent.txt"); final byte[] result = ihcClient.getProjectFileFromController(); - assertEquals(ExpectedFileContent, new String(result, "UTF-8")); + assertEquals(expectedFileContent, new String(result, "UTF-8")); } } diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/ResourceFileUtils.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/ResourceFileUtils.java similarity index 85% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/ResourceFileUtils.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/ResourceFileUtils.java index dbdcd21e4e14c..5805c56fd8cf9 100644 --- a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/ResourceFileUtils.java +++ b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/ResourceFileUtils.java @@ -24,17 +24,13 @@ * @author Pauli Anttila - Initial contribution */ public class ResourceFileUtils { - static public String getFileContent(String resourceFile) { String result = ""; - - ClassLoader classLoader = ResourceFileUtils.class.getClassLoader(); try { - result = IOUtils.toString(classLoader.getResourceAsStream(resourceFile)); + result = IOUtils.toString(ResourceFileUtils.class.getClassLoader().getResourceAsStream(resourceFile)); } catch (IOException e) { fail("IOException reading xml file '" + resourceFile + "': " + e); } - return result; } } diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcAirlinkManagementServiceTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcAirlinkManagementServiceTest.java similarity index 91% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcAirlinkManagementServiceTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcAirlinkManagementServiceTest.java index 448c7e8ed2507..f641662ae7a6e 100644 --- a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcAirlinkManagementServiceTest.java +++ b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcAirlinkManagementServiceTest.java @@ -45,9 +45,8 @@ public class IhcAirlinkManagementServiceTest { public void setUp() throws IhcExecption, SocketTimeoutException { ihcAirlinkManagementService = spy(new IhcAirlinkManagementService(host, timeout, new IhcConnectionPool())); - final String query = ResourceFileUtils.getFileContent("src/test/resources/EmptyQuery.xml"); - final String response = ResourceFileUtils - .getFileContent("src/test/resources/GetDetectedDeviceListResponse.xml"); + final String query = ResourceFileUtils.getFileContent("EmptyQuery.xml"); + final String response = ResourceFileUtils.getFileContent("GetDetectedDeviceListResponse.xml"); requestProps.clear(); requestProps.put("SOAPAction", "getDetectedDeviceList"); diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcAuthenticationServiceTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcAuthenticationServiceTest.java similarity index 85% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcAuthenticationServiceTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcAuthenticationServiceTest.java index f5fe76ded12f0..88ff0e488f3bc 100644 --- a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcAuthenticationServiceTest.java +++ b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcAuthenticationServiceTest.java @@ -41,17 +41,14 @@ public class IhcAuthenticationServiceTest { public void setUp() throws IhcExecption, SocketTimeoutException { ihcAuthenticationService = spy(new IhcAuthenticationService(host, timeout, new IhcConnectionPool())); - final String querySuccesfulLogin = ResourceFileUtils - .getFileContent("src/test/resources/SuccesfulLoginQuery.xml"); - final String responseSuccesfulLogin = ResourceFileUtils - .getFileContent("src/test/resources/SuccesfulLoginResponse.xml"); + final String querySuccesfulLogin = ResourceFileUtils.getFileContent("SuccesfulLoginQuery.xml"); + final String responseSuccesfulLogin = ResourceFileUtils.getFileContent("SuccesfulLoginResponse.xml"); doReturn(responseSuccesfulLogin).when(ihcAuthenticationService).sendQuery(eq(url), any(), eq(querySuccesfulLogin), eq(timeout)); - final String queryLoginFailed = ResourceFileUtils.getFileContent("src/test/resources/LoginFailedQuery.xml"); - final String responseLoginFailed = ResourceFileUtils - .getFileContent("src/test/resources/LoginFailedResponse.xml"); + final String queryLoginFailed = ResourceFileUtils.getFileContent("LoginFailedQuery.xml"); + final String responseLoginFailed = ResourceFileUtils.getFileContent("LoginFailedResponse.xml"); doReturn(responseLoginFailed).when(ihcAuthenticationService).sendQuery(eq(url), any(), eq(queryLoginFailed), eq(timeout)); diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcConfigurationServiceTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcConfigurationServiceTest.java similarity index 95% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcConfigurationServiceTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcConfigurationServiceTest.java index ba2e57feef3e1..ae0e259047a67 100644 --- a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcConfigurationServiceTest.java +++ b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcConfigurationServiceTest.java @@ -39,7 +39,7 @@ public class IhcConfigurationServiceTest { private IhcConfigurationService ihcConfigurationService; private final String host = "1.1.1.1"; private final String url = "https://1.1.1.1/ws/ConfigurationService"; - final String query = ResourceFileUtils.getFileContent("src/test/resources/EmptyQuery.xml"); + final String query = ResourceFileUtils.getFileContent("EmptyQuery.xml"); private Map requestProps = new HashMap(); private final int timeout = 100; @@ -52,7 +52,7 @@ public void setUp() throws IhcExecption, SocketTimeoutException { @Test public void testv2() throws IhcExecption { - final String response = ResourceFileUtils.getFileContent("src/test/resources/GetSystemInfoResponse.xml"); + final String response = ResourceFileUtils.getFileContent("GetSystemInfoResponse.xml"); doReturn(response).when(ihcConfigurationService).sendQuery(eq(url), eq(requestProps), eq(query), eq(timeout)); @@ -76,7 +76,7 @@ public void testv2() throws IhcExecption { @Test public void testv3() throws IhcExecption { - final String response = ResourceFileUtils.getFileContent("src/test/resources/GetSystemInfoResponse2.xml"); + final String response = ResourceFileUtils.getFileContent("GetSystemInfoResponse2.xml"); doReturn(response).when(ihcConfigurationService).sendQuery(eq(url), eq(requestProps), eq(query), eq(timeout)); final WSSystemInfo result = ihcConfigurationService.getSystemInfo(); diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcControllerServiceTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcControllerServiceTest.java similarity index 84% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcControllerServiceTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcControllerServiceTest.java index 4d99ec882f751..8629f2388b0fc 100644 --- a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcControllerServiceTest.java +++ b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcControllerServiceTest.java @@ -49,15 +49,14 @@ public class IhcControllerServiceTest { public void setUp() throws IhcExecption, SocketTimeoutException { ihcControllerService = spy(new IhcControllerService(host, timeout, new IhcConnectionPool())); - query = ResourceFileUtils.getFileContent("src/test/resources/EmptyQuery.xml"); + query = ResourceFileUtils.getFileContent("EmptyQuery.xml"); requestProps.clear(); } @Test public void projectInfoTest() throws IhcExecption { requestProps.put("SOAPAction", "getProjectInfo"); - final String projectInfoResponse = ResourceFileUtils - .getFileContent("src/test/resources/GetProjectInfoResponse.xml"); + final String projectInfoResponse = ResourceFileUtils.getFileContent("GetProjectInfoResponse.xml"); doReturn(projectInfoResponse).when(ihcControllerService).sendQuery(eq(url), eq(requestProps), eq(query), eq(timeout)); @@ -70,7 +69,7 @@ public void projectInfoTest() throws IhcExecption { @Test public void projectNumberOfSegmentsTest() throws IhcExecption { final String projectNumberOfSegmentsResponse = ResourceFileUtils - .getFileContent("src/test/resources/GetProjectNumberOfSegmentsResponse.xml"); + .getFileContent("GetProjectNumberOfSegmentsResponse.xml"); requestProps.put("SOAPAction", "getIHCProjectNumberOfSegments"); doReturn(projectNumberOfSegmentsResponse).when(ihcControllerService).sendQuery(eq(url), eq(requestProps), @@ -84,7 +83,7 @@ public void projectNumberOfSegmentsTest() throws IhcExecption { @Test public void projectSegmentationSizeTest() throws IhcExecption { final String projectSegmentationSizeResponse = ResourceFileUtils - .getFileContent("src/test/resources/GetProjectSegmentationSizeResponse.xml"); + .getFileContent("GetProjectSegmentationSizeResponse.xml"); requestProps.put("SOAPAction", "getIHCProjectSegmentationSize"); @@ -98,8 +97,7 @@ public void projectSegmentationSizeTest() throws IhcExecption { @Test public void controllerStateTest() throws IhcExecption { - final String controllerStateResponse = ResourceFileUtils - .getFileContent("src/test/resources/ControllerStateResponse.xml"); + final String controllerStateResponse = ResourceFileUtils.getFileContent("ControllerStateResponse.xml"); requestProps.put("SOAPAction", "getState"); doReturn(controllerStateResponse).when(ihcControllerService).sendQuery(eq(url), eq(requestProps), eq(query), @@ -113,9 +111,9 @@ public void controllerStateTest() throws IhcExecption { @Test public void waitForControllerStateChangeQueryTest() throws IhcExecption { final String waitForControllerStateChangeQuery = ResourceFileUtils - .getFileContent("src/test/resources/WaitForControllerStateChangeQuery.xml"); + .getFileContent("WaitForControllerStateChangeQuery.xml"); final String waitForControllerStateChangeResponse = ResourceFileUtils - .getFileContent("src/test/resources/WaitForControllerStateChangeResponse.xml"); + .getFileContent("WaitForControllerStateChangeResponse.xml"); requestProps.put("SOAPAction", "waitForControllerStateChange"); doReturn(waitForControllerStateChangeResponse).when(ihcControllerService).sendQuery(eq(url), eq(requestProps), @@ -130,10 +128,8 @@ public void waitForControllerStateChangeQueryTest() throws IhcExecption { @Test public void projectSegmentQueryTest() throws IhcExecption { - final String projectSegmentQuery = ResourceFileUtils - .getFileContent("src/test/resources/GetProjectSegmentQuery.xml"); - final String projectSegmentResponse = ResourceFileUtils - .getFileContent("src/test/resources/GetProjectSegmentResponse.xml"); + final String projectSegmentQuery = ResourceFileUtils.getFileContent("GetProjectSegmentQuery.xml"); + final String projectSegmentResponse = ResourceFileUtils.getFileContent("GetProjectSegmentResponse.xml"); requestProps.put("SOAPAction", "getIHCProjectSegment"); doReturn(projectSegmentResponse).when(ihcControllerService).sendQuery(eq(url), eq(requestProps), diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionServiceTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionServiceTest.java similarity index 80% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionServiceTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionServiceTest.java index 563e985c2d9ef..3c2ac938782bb 100644 --- a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionServiceTest.java +++ b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcResourceInteractionServiceTest.java @@ -50,23 +50,15 @@ public class IhcResourceInteractionServiceTest { public void setUp() throws IhcExecption, SocketTimeoutException { ihcResourceInteractionService = spy(new IhcResourceInteractionService(host, 0, new IhcConnectionPool())); - final String query = ResourceFileUtils.getFileContent("src/test/resources/ResourceValueQueryTemplate.xml"); - final String response11111 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueQueryResponse11111.xml"); - final String response22222 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueQueryResponse22222.xml"); - final String response33333 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueQueryResponse33333.xml"); - final String response44444 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueQueryResponse44444.xml"); - final String response55555 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueQueryResponse55555.xml"); - final String response66666 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueQueryResponse66666.xml"); - final String response77777 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueQueryResponse77777.xml"); - final String response88888 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueQueryResponse88888.xml"); + final String query = ResourceFileUtils.getFileContent("ResourceValueQueryTemplate.xml"); + final String response11111 = ResourceFileUtils.getFileContent("ResourceValueQueryResponse11111.xml"); + final String response22222 = ResourceFileUtils.getFileContent("ResourceValueQueryResponse22222.xml"); + final String response33333 = ResourceFileUtils.getFileContent("ResourceValueQueryResponse33333.xml"); + final String response44444 = ResourceFileUtils.getFileContent("ResourceValueQueryResponse44444.xml"); + final String response55555 = ResourceFileUtils.getFileContent("ResourceValueQueryResponse55555.xml"); + final String response66666 = ResourceFileUtils.getFileContent("ResourceValueQueryResponse66666.xml"); + final String response77777 = ResourceFileUtils.getFileContent("ResourceValueQueryResponse77777.xml"); + final String response88888 = ResourceFileUtils.getFileContent("ResourceValueQueryResponse88888.xml"); doReturn(response11111).when(ihcResourceInteractionService).sendQuery(eq(url), any(), eq(String.format(query, 11111)), anyInt()); @@ -85,24 +77,15 @@ public void setUp() throws IhcExecption, SocketTimeoutException { doReturn(response88888).when(ihcResourceInteractionService).sendQuery(eq(url), any(), eq(String.format(query, 88888)), anyInt()); - final String updateOkResult = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueUpdateOkResult.xml"); - final String update100001 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueUpdate100001.xml"); - final String update200002 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueUpdate200002.xml"); - final String update300003 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueUpdate300003.xml"); - final String update400004 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueUpdate400004.xml"); - final String update500005 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueUpdate500005.xml"); - final String update600006 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueUpdate600006.xml"); - final String update700007 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueUpdate700007.xml"); - final String update800008 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueUpdate800008.xml"); + final String updateOkResult = ResourceFileUtils.getFileContent("ResourceValueUpdateOkResult.xml"); + final String update100001 = ResourceFileUtils.getFileContent("ResourceValueUpdate100001.xml"); + final String update200002 = ResourceFileUtils.getFileContent("ResourceValueUpdate200002.xml"); + final String update300003 = ResourceFileUtils.getFileContent("ResourceValueUpdate300003.xml"); + final String update400004 = ResourceFileUtils.getFileContent("ResourceValueUpdate400004.xml"); + final String update500005 = ResourceFileUtils.getFileContent("ResourceValueUpdate500005.xml"); + final String update600006 = ResourceFileUtils.getFileContent("ResourceValueUpdate600006.xml"); + final String update700007 = ResourceFileUtils.getFileContent("ResourceValueUpdate700007.xml"); + final String update800008 = ResourceFileUtils.getFileContent("ResourceValueUpdate800008.xml"); doReturn(updateOkResult).when(ihcResourceInteractionService).sendQuery(eq(url), any(), eq(update100001), anyInt()); @@ -121,18 +104,16 @@ public void setUp() throws IhcExecption, SocketTimeoutException { doReturn(updateOkResult).when(ihcResourceInteractionService).sendQuery(eq(url), any(), eq(update800008), anyInt()); - final String updateFailureResult = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueUpdateFailureResult.xml"); - final String update100011 = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueUpdate100011.xml"); + final String updateFailureResult = ResourceFileUtils.getFileContent("ResourceValueUpdateFailureResult.xml"); + final String update100011 = ResourceFileUtils.getFileContent("ResourceValueUpdate100011.xml"); doReturn(updateFailureResult).when(ihcResourceInteractionService).sendQuery(eq(url), any(), eq(update100011), anyInt()); final String resourceValueNotificationsQuery = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueNotificationsQuery.xml"); + .getFileContent("ResourceValueNotificationsQuery.xml"); final String resourceValueNotificationsResponse = ResourceFileUtils - .getFileContent("src/test/resources/ResourceValueNotificationsResponse.xml"); + .getFileContent("ResourceValueNotificationsResponse.xml"); doReturn(resourceValueNotificationsResponse).when(ihcResourceInteractionService).sendQuery(eq(url), any(), eq(resourceValueNotificationsQuery), anyInt()); diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcTimeServiceTest.java b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcTimeServiceTest.java similarity index 94% rename from addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcTimeServiceTest.java rename to bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcTimeServiceTest.java index 9cd39091d9a2f..40b45d7a06a19 100644 --- a/addons/binding/org.openhab.binding.ihc.test/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcTimeServiceTest.java +++ b/bundles/org.openhab.binding.ihc/src/test/java/org/openhab/binding/ihc/internal/ws/services/IhcTimeServiceTest.java @@ -46,8 +46,8 @@ public class IhcTimeServiceTest { public void setUp() throws IhcExecption, SocketTimeoutException { ihcTimeService = spy(new IhcTimeService(host, timeout, new IhcConnectionPool())); - final String query = ResourceFileUtils.getFileContent("src/test/resources/EmptyQuery.xml"); - final String response = ResourceFileUtils.getFileContent("src/test/resources/GetSettingsResponse.xml"); + final String query = ResourceFileUtils.getFileContent("EmptyQuery.xml"); + final String response = ResourceFileUtils.getFileContent("GetSettingsResponse.xml"); requestProps.clear(); requestProps.put("SOAPAction", "getSettings"); diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ControllerStateResponse.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ControllerStateResponse.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ControllerStateResponse.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ControllerStateResponse.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/EmptyQuery.xml b/bundles/org.openhab.binding.ihc/src/test/resources/EmptyQuery.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/EmptyQuery.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/EmptyQuery.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetDetectedDeviceListResponse.xml b/bundles/org.openhab.binding.ihc/src/test/resources/GetDetectedDeviceListResponse.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetDetectedDeviceListResponse.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/GetDetectedDeviceListResponse.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetProjectInfoResponse.xml b/bundles/org.openhab.binding.ihc/src/test/resources/GetProjectInfoResponse.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetProjectInfoResponse.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/GetProjectInfoResponse.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetProjectNumberOfSegmentsResponse.xml b/bundles/org.openhab.binding.ihc/src/test/resources/GetProjectNumberOfSegmentsResponse.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetProjectNumberOfSegmentsResponse.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/GetProjectNumberOfSegmentsResponse.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetProjectSegmentQuery.xml b/bundles/org.openhab.binding.ihc/src/test/resources/GetProjectSegmentQuery.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetProjectSegmentQuery.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/GetProjectSegmentQuery.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetProjectSegmentResponse.xml b/bundles/org.openhab.binding.ihc/src/test/resources/GetProjectSegmentResponse.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetProjectSegmentResponse.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/GetProjectSegmentResponse.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetProjectSegmentationSizeResponse.xml b/bundles/org.openhab.binding.ihc/src/test/resources/GetProjectSegmentationSizeResponse.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetProjectSegmentationSizeResponse.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/GetProjectSegmentationSizeResponse.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetSettingsResponse.xml b/bundles/org.openhab.binding.ihc/src/test/resources/GetSettingsResponse.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetSettingsResponse.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/GetSettingsResponse.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetSystemInfoResponse.xml b/bundles/org.openhab.binding.ihc/src/test/resources/GetSystemInfoResponse.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetSystemInfoResponse.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/GetSystemInfoResponse.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetSystemInfoResponse2.xml b/bundles/org.openhab.binding.ihc/src/test/resources/GetSystemInfoResponse2.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/GetSystemInfoResponse2.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/GetSystemInfoResponse2.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/LoginFailedQuery.xml b/bundles/org.openhab.binding.ihc/src/test/resources/LoginFailedQuery.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/LoginFailedQuery.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/LoginFailedQuery.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/LoginFailedResponse.xml b/bundles/org.openhab.binding.ihc/src/test/resources/LoginFailedResponse.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/LoginFailedResponse.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/LoginFailedResponse.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ProjectFileContent.txt b/bundles/org.openhab.binding.ihc/src/test/resources/ProjectFileContent.txt similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ProjectFileContent.txt rename to bundles/org.openhab.binding.ihc/src/test/resources/ProjectFileContent.txt diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueNotificationsQuery.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueNotificationsQuery.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueNotificationsQuery.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueNotificationsQuery.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueNotificationsResponse.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueNotificationsResponse.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueNotificationsResponse.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueNotificationsResponse.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse11111.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse11111.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse11111.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse11111.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse22222.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse22222.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse22222.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse22222.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse33333.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse33333.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse33333.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse33333.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse44444.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse44444.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse44444.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse44444.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse55555.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse55555.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse55555.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse55555.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse66666.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse66666.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse66666.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse66666.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse77777.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse77777.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse77777.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse77777.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse88888.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse88888.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryResponse88888.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryResponse88888.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryTemplate.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryTemplate.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueQueryTemplate.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueQueryTemplate.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate100001.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate100001.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate100001.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate100001.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate100011.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate100011.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate100011.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate100011.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate200002.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate200002.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate200002.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate200002.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate300003.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate300003.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate300003.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate300003.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate400004.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate400004.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate400004.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate400004.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate500005.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate500005.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate500005.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate500005.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate600006.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate600006.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate600006.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate600006.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate700007.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate700007.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate700007.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate700007.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate800008.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate800008.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdate800008.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdate800008.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdateFailureResult.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdateFailureResult.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdateFailureResult.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdateFailureResult.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdateOkResult.xml b/bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdateOkResult.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/ResourceValueUpdateOkResult.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/ResourceValueUpdateOkResult.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/Segment0.base64 b/bundles/org.openhab.binding.ihc/src/test/resources/Segment0.base64 similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/Segment0.base64 rename to bundles/org.openhab.binding.ihc/src/test/resources/Segment0.base64 diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/Segment1.base64 b/bundles/org.openhab.binding.ihc/src/test/resources/Segment1.base64 similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/Segment1.base64 rename to bundles/org.openhab.binding.ihc/src/test/resources/Segment1.base64 diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/SuccesfulLoginQuery.xml b/bundles/org.openhab.binding.ihc/src/test/resources/SuccesfulLoginQuery.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/SuccesfulLoginQuery.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/SuccesfulLoginQuery.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/SuccesfulLoginResponse.xml b/bundles/org.openhab.binding.ihc/src/test/resources/SuccesfulLoginResponse.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/SuccesfulLoginResponse.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/SuccesfulLoginResponse.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/WaitForControllerStateChangeQuery.xml b/bundles/org.openhab.binding.ihc/src/test/resources/WaitForControllerStateChangeQuery.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/WaitForControllerStateChangeQuery.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/WaitForControllerStateChangeQuery.xml diff --git a/addons/binding/org.openhab.binding.ihc.test/src/test/resources/WaitForControllerStateChangeResponse.xml b/bundles/org.openhab.binding.ihc/src/test/resources/WaitForControllerStateChangeResponse.xml similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/src/test/resources/WaitForControllerStateChangeResponse.xml rename to bundles/org.openhab.binding.ihc/src/test/resources/WaitForControllerStateChangeResponse.xml diff --git a/bundles/org.openhab.binding.innogysmarthome/.classpath b/bundles/org.openhab.binding.innogysmarthome/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.innogysmarthome/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.innogysmarthome/.project b/bundles/org.openhab.binding.innogysmarthome/.project new file mode 100644 index 0000000000000..4372ddc10c84d --- /dev/null +++ b/bundles/org.openhab.binding.innogysmarthome/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.innogysmarthome + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.innogysmarthome/NOTICE b/bundles/org.openhab.binding.innogysmarthome/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/NOTICE rename to bundles/org.openhab.binding.innogysmarthome/NOTICE diff --git a/addons/binding/org.openhab.binding.innogysmarthome/README.md b/bundles/org.openhab.binding.innogysmarthome/README.md similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/README.md rename to bundles/org.openhab.binding.innogysmarthome/README.md diff --git a/bundles/org.openhab.binding.innogysmarthome/pom.xml b/bundles/org.openhab.binding.innogysmarthome/pom.xml new file mode 100644 index 0000000000000..6b6912e089a28 --- /dev/null +++ b/bundles/org.openhab.binding.innogysmarthome/pom.xml @@ -0,0 +1,55 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.innogysmarthome + + openHAB Add-ons :: Bundles :: innogy Smarthome Binding + + + + org.apache.httpcomponents + httpcore-osgi + 4.4.4 + provided + + + org.apache.httpcomponents + httpclient-osgi + 4.5.2 + provided + + + com.google.guava + guava + 20.0 + provided + + + com.google.http-client + google-http-client-gson + 1.27.0 + provided + + + com.google.http-client + google-http-client + 1.27.0 + provided + + + com.google.oauth-client + google-oauth-client + 1.27.0 + provided + + + + diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyBindingConstants.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyBindingConstants.java similarity index 89% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyBindingConstants.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyBindingConstants.java index a621cc4f98708..c46b2936bbe85 100644 --- a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyBindingConstants.java +++ b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyBindingConstants.java @@ -12,13 +12,14 @@ */ package org.openhab.binding.innogysmarthome.internal; +import java.util.Collections; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.thing.ThingTypeUID; -import com.google.common.collect.ImmutableSet; - /** * The {@link InnogyBindingConstants} class defines common constants, which are * used across the whole binding. @@ -101,10 +102,13 @@ public class InnogyBindingConstants { public static final String DEVICE_SMART_METER = "SmartMeter"; public static final String DEVICE_TWO_WAY_METER = "TwoWayMeter"; - public static final Set SUPPORTED_DEVICES = ImmutableSet.of(DEVICE_SHC, DEVICE_PSS, DEVICE_PSSO, - DEVICE_VARIABLE_ACTUATOR, DEVICE_RST, DEVICE_RST2, DEVICE_WRT, DEVICE_WDS, DEVICE_ISS2, DEVICE_WSD, - DEVICE_WSD2, DEVICE_WMD, DEVICE_WMDO, DEVICE_WSC2, DEVICE_BRC8, DEVICE_ISC2, DEVICE_ISD2, DEVICE_ISR2, - DEVICE_PSD, DEVICE_ANALOG_METER, DEVICE_GENERATION_METER, DEVICE_SMART_METER, DEVICE_TWO_WAY_METER); + public static final Set SUPPORTED_DEVICES = Collections + .unmodifiableSet(Stream + .of(DEVICE_SHC, DEVICE_PSS, DEVICE_PSSO, DEVICE_VARIABLE_ACTUATOR, DEVICE_RST, DEVICE_RST2, + DEVICE_WRT, DEVICE_WDS, DEVICE_ISS2, DEVICE_WSD, DEVICE_WSD2, DEVICE_WMD, DEVICE_WMDO, + DEVICE_WSC2, DEVICE_BRC8, DEVICE_ISC2, DEVICE_ISD2, DEVICE_ISR2, DEVICE_PSD, + DEVICE_ANALOG_METER, DEVICE_GENERATION_METER, DEVICE_SMART_METER, DEVICE_TWO_WAY_METER) + .collect(Collectors.toSet())); // List of all Thing Type UIDs public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge"); @@ -133,11 +137,14 @@ public class InnogyBindingConstants { public static final ThingTypeUID THING_TYPE_SMART_METER = new ThingTypeUID(BINDING_ID, DEVICE_SMART_METER); public static final ThingTypeUID THING_TYPE_TWO_WAY_METER = new ThingTypeUID(BINDING_ID, DEVICE_TWO_WAY_METER); - public static final Set SUPPORTED_DEVICE_THING_TYPES = ImmutableSet.of(THING_TYPE_PSS, - THING_TYPE_PSSO, THING_TYPE_VARIABLE_ACTUATOR, THING_TYPE_RST, THING_TYPE_RST2, THING_TYPE_WRT, - THING_TYPE_WDS, THING_TYPE_ISS2, THING_TYPE_WSD, THING_TYPE_WSD2, THING_TYPE_WMD, THING_TYPE_WMDO, - THING_TYPE_WSC2, THING_TYPE_BRC8, THING_TYPE_ISC2, THING_TYPE_ISD2, THING_TYPE_ISR2, THING_TYPE_PSD, - THING_TYPE_ANALOG_METER, THING_TYPE_GENERATION_METER, THING_TYPE_SMART_METER, THING_TYPE_TWO_WAY_METER); + public static final Set SUPPORTED_DEVICE_THING_TYPES = Collections + .unmodifiableSet(Stream + .of(THING_TYPE_PSS, THING_TYPE_PSSO, THING_TYPE_VARIABLE_ACTUATOR, THING_TYPE_RST, THING_TYPE_RST2, + THING_TYPE_WRT, THING_TYPE_WDS, THING_TYPE_ISS2, THING_TYPE_WSD, THING_TYPE_WSD2, + THING_TYPE_WMD, THING_TYPE_WMDO, THING_TYPE_WSC2, THING_TYPE_BRC8, THING_TYPE_ISC2, + THING_TYPE_ISD2, THING_TYPE_ISR2, THING_TYPE_PSD, THING_TYPE_ANALOG_METER, + THING_TYPE_GENERATION_METER, THING_TYPE_SMART_METER, THING_TYPE_TWO_WAY_METER) + .collect(Collectors.toSet())); // List of all Channel ids public static final String CHANNEL_SWITCH = "switch"; diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyHandlerFactory.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyHandlerFactory.java similarity index 92% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyHandlerFactory.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyHandlerFactory.java index 1172706a97557..4a57a73950e34 100644 --- a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyHandlerFactory.java +++ b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyHandlerFactory.java @@ -12,10 +12,13 @@ */ package org.openhab.binding.innogysmarthome.internal; +import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.smarthome.config.discovery.DiscoveryService; import org.eclipse.smarthome.core.thing.Bridge; @@ -33,8 +36,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.Sets; - /** * The {@link InnogyHandlerFactory} is responsible for creating things and thing * handlers. @@ -44,8 +45,9 @@ @Component(service = ThingHandlerFactory.class, configurationPid = "binding.innogysmarthome") public class InnogyHandlerFactory extends BaseThingHandlerFactory implements ThingHandlerFactory { - private static final Set SUPPORTED_THING_TYPES = Sets.union(InnogyBridgeHandler.SUPPORTED_THING_TYPES, - InnogyDeviceHandler.SUPPORTED_THING_TYPES); + private static final Set SUPPORTED_THING_TYPES = Collections + .unmodifiableSet(Stream.concat(InnogyBridgeHandler.SUPPORTED_THING_TYPES.stream(), + InnogyDeviceHandler.SUPPORTED_THING_TYPES.stream()).collect(Collectors.toSet())); private final Logger logger = LoggerFactory.getLogger(InnogyHandlerFactory.class); private final Map> discoveryServiceRegs = new HashMap<>(); diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyWebSocket.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyWebSocket.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyWebSocket.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/InnogyWebSocket.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/Constants.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/Constants.java similarity index 88% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/Constants.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/Constants.java index 0e23842bce2e2..335e0319378a9 100644 --- a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/Constants.java +++ b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/Constants.java @@ -12,12 +12,13 @@ */ package org.openhab.binding.innogysmarthome.internal.client; +import java.util.Collections; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; -import com.google.common.collect.ImmutableSet; - /** * Holds necessary constants for the innogy API. * @@ -28,8 +29,8 @@ public class Constants { // devices - public static final Set BATTERY_POWERED_DEVICES = ImmutableSet.of("RST", "WRT", "WDS", "WSD", "WSD2", "WMD", - "WMDO", "WSC2", "BRC8"); + public static final Set BATTERY_POWERED_DEVICES = Collections.unmodifiableSet( + Stream.of("RST", "WRT", "WDS", "WSD", "WSD2", "WMD", "WMDO", "WSC2", "BRC8").collect(Collectors.toSet())); // API URLs public static final String API_HOST = "api.services-smarthome.de"; diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyClient.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyClient.java similarity index 99% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyClient.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyClient.java index e74e4cbc050e7..e30ce2b7afca3 100644 --- a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyClient.java +++ b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyClient.java @@ -67,7 +67,7 @@ import com.google.api.client.http.json.JsonHttpContent; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.JsonObjectParser; -import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.client.json.gson.GsonFactory; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonSyntaxException; @@ -143,7 +143,7 @@ public void initialize() throws IOException, ApiException, ConfigurationExceptio */ private void initializeHttpClient() { httpTransport = new NetHttpTransport(); - jsonFactory = new JacksonFactory(); + jsonFactory = new GsonFactory(); if (credentialRefreshListener == null) { credentialRefreshListener = new InnogyCredentialRefreshListener(config); diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyConfig.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyConfig.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyConfig.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyConfig.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyCredentialRefreshListener.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyCredentialRefreshListener.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyCredentialRefreshListener.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyCredentialRefreshListener.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/Util.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/Util.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/Util.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/Util.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/ConfigPropertyList.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/ConfigPropertyList.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/ConfigPropertyList.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/ConfigPropertyList.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Constant.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Constant.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Constant.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Constant.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Location.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Location.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Location.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Location.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Message.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Message.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Message.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Message.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Property.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Property.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Property.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/Property.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/PropertyList.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/PropertyList.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/PropertyList.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/PropertyList.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/SHCInfo.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/SHCInfo.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/SHCInfo.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/SHCInfo.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/Action.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/Action.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/Action.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/Action.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/ActionParameter.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/ActionParameter.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/ActionParameter.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/ActionParameter.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/SetStateAction.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/SetStateAction.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/SetStateAction.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/action/SetStateAction.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/capability/Capability.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/capability/Capability.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/capability/Capability.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/capability/Capability.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/device/Device.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/device/Device.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/device/Device.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/device/Device.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/error/ErrorResponse.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/error/ErrorResponse.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/error/ErrorResponse.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/error/ErrorResponse.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/event/Event.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/event/Event.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/event/Event.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/event/Event.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/link/CapabilityLink.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/link/CapabilityLink.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/link/CapabilityLink.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/link/CapabilityLink.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/link/Link.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/link/Link.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/link/Link.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/link/Link.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/CapabilityState.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/CapabilityState.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/CapabilityState.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/CapabilityState.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/DeviceState.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/DeviceState.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/DeviceState.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/DeviceState.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/EntityState.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/EntityState.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/EntityState.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/EntityState.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/StatePropertyList.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/StatePropertyList.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/StatePropertyList.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/state/StatePropertyList.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ApiException.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ApiException.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ApiException.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ApiException.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ConfigurationException.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ConfigurationException.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ConfigurationException.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ConfigurationException.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ControllerOfflineException.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ControllerOfflineException.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ControllerOfflineException.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ControllerOfflineException.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/InvalidActionTriggeredException.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/InvalidActionTriggeredException.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/InvalidActionTriggeredException.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/InvalidActionTriggeredException.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/InvalidAuthCodeException.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/InvalidAuthCodeException.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/InvalidAuthCodeException.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/InvalidAuthCodeException.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/RemoteAccessNotAllowedException.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/RemoteAccessNotAllowedException.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/RemoteAccessNotAllowedException.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/RemoteAccessNotAllowedException.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ServiceUnavailableException.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ServiceUnavailableException.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ServiceUnavailableException.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/ServiceUnavailableException.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/SessionExistsException.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/SessionExistsException.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/SessionExistsException.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/SessionExistsException.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/SessionNotFoundException.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/SessionNotFoundException.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/SessionNotFoundException.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/exception/SessionNotFoundException.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/discovery/InnogyBridgeDiscoveryParticipant.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/discovery/InnogyBridgeDiscoveryParticipant.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/discovery/InnogyBridgeDiscoveryParticipant.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/discovery/InnogyBridgeDiscoveryParticipant.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/discovery/InnogyDeviceDiscoveryService.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/discovery/InnogyDeviceDiscoveryService.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/discovery/InnogyDeviceDiscoveryService.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/discovery/InnogyDeviceDiscoveryService.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandler.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandler.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandler.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyDeviceHandler.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyDeviceHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyDeviceHandler.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyDeviceHandler.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/listener/DeviceStatusListener.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/listener/DeviceStatusListener.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/listener/DeviceStatusListener.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/listener/DeviceStatusListener.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/listener/EventListener.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/listener/EventListener.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/listener/EventListener.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/listener/EventListener.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/manager/DeviceStructureManager.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/manager/DeviceStructureManager.java similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/manager/DeviceStructureManager.java rename to bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/manager/DeviceStructureManager.java diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/config/config.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/config/config.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/config/config.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/config/config.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/i18n/innogysmarthome_de_DE.properties b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/i18n/innogysmarthome_de_DE.properties similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/i18n/innogysmarthome_de_DE.properties rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/i18n/innogysmarthome_de_DE.properties diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/AnalogMeter.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/AnalogMeter.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/AnalogMeter.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/AnalogMeter.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/BRC8.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/BRC8.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/BRC8.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/BRC8.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/GenerationMeter.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/GenerationMeter.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/GenerationMeter.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/GenerationMeter.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/ISC2.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/ISC2.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/ISC2.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/ISC2.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/ISD2.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/ISD2.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/ISD2.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/ISD2.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/ISR2.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/ISR2.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/ISR2.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/ISR2.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/ISS2.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/ISS2.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/ISS2.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/ISS2.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/PSD.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/PSD.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/PSD.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/PSD.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/PSS.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/PSS.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/PSS.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/PSS.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/PSSO.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/PSSO.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/PSSO.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/PSSO.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/RST.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/RST.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/RST.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/RST.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/RST2.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/RST2.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/RST2.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/RST2.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/SmartMeter.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/SmartMeter.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/SmartMeter.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/SmartMeter.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/TwoWayMeter.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/TwoWayMeter.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/TwoWayMeter.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/TwoWayMeter.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/VariableActuator.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/VariableActuator.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/VariableActuator.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/VariableActuator.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WDS.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WDS.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WDS.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WDS.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WMD.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WMD.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WMD.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WMD.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WMDO.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WMDO.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WMDO.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WMDO.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WRT.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WRT.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WRT.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WRT.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WSC2.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WSC2.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WSC2.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WSC2.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WSD.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WSD.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WSD.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WSD.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WSD2.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WSD2.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/WSD2.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/WSD2.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/bridge.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/bridge.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/bridge.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/bridge.xml diff --git a/addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/channels.xml similarity index 100% rename from addons/binding/org.openhab.binding.innogysmarthome/ESH-INF/thing/channels.xml rename to bundles/org.openhab.binding.innogysmarthome/src/main/resources/ESH-INF/thing/channels.xml diff --git a/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/connection/AbstractJeeLinkConnection.java b/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/connection/AbstractJeeLinkConnection.java index a3abcc27b3ed5..c0c06a90dab18 100644 --- a/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/connection/AbstractJeeLinkConnection.java +++ b/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/connection/AbstractJeeLinkConnection.java @@ -77,15 +77,14 @@ public void propagateLine(String line) throws IOException { @Override public void sendCommands(String commands) { - try { - if (commands != null && !commands.trim().isEmpty()) { - initCommands = commands.split(";"); + if (commands != null && !commands.trim().isEmpty()) { + initCommands = commands.split(";"); - if (logger.isDebugEnabled()) { - logger.debug("Writing to device on port {}: {} ", port, Arrays.toString(initCommands)); - } + if (logger.isDebugEnabled()) { + logger.debug("Writing to device on port {}: {} ", port, Arrays.toString(initCommands)); + } - OutputStream initStream = getInitStream(); + try (OutputStream initStream = getInitStream()) { if (initStream == null) { throw new IOException( "Connection on port " + port + " did not provide an init stream for writing init commands"); @@ -98,11 +97,11 @@ public void sendCommands(String commands) { w.write(cmd); } w.flush(); + } catch (IOException ex) { + logger.debug("Error writing to output stream!", ex); + closeConnection(); + notifyAbort("propagate: " + ex.getMessage()); } - } catch (IOException ex) { - logger.debug("Error writing to output stream!", ex); - closeConnection(); - notifyAbort("propagate: " + ex.getMessage()); } } diff --git a/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/connection/JeeLinkSerialConnection.java b/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/connection/JeeLinkSerialConnection.java index cbd393731f4f4..b94a0c42b39b6 100644 --- a/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/connection/JeeLinkSerialConnection.java +++ b/bundles/org.openhab.binding.jeelink/src/main/java/org/openhab/binding/jeelink/internal/connection/JeeLinkSerialConnection.java @@ -16,6 +16,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.Reader; import java.util.TooManyListenersException; import org.slf4j.Logger; @@ -78,25 +79,26 @@ public synchronized void openConnection() { serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); - final BufferedReader input = new BufferedReader(new InputStreamReader(serialPort.getInputStream())); - - serialPort.addEventListener(new SerialPortEventListener() { - @Override - public void serialEvent(SerialPortEvent event) { - try { - if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) { - propagateLine(input.readLine()); + try (Reader isr = new InputStreamReader(serialPort.getInputStream()); + BufferedReader input = new BufferedReader(isr)) { + serialPort.addEventListener(new SerialPortEventListener() { + @Override + public void serialEvent(SerialPortEvent event) { + try { + if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) { + propagateLine(input.readLine()); + } + } catch (IOException ex) { + logger.debug("Error reading from serial port!", ex); + closeConnection(); + notifyAbort("propagate: " + ex.getMessage()); } - } catch (IOException ex) { - logger.debug("Error reading from serial port!", ex); - closeConnection(); - notifyAbort("propagate: " + ex.getMessage()); } - } - }); + }); - serialPort.notifyOnDataAvailable(true); - notifyOpen(); + serialPort.notifyOnDataAvailable(true); + notifyOpen(); + } } } catch (UnsupportedCommOperationException | IOException | TooManyListenersException ex) { closeConnection(); diff --git a/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/KebaBindingConstants.java b/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/KebaBindingConstants.java index 9721887e74d4d..ed94497b9ac7a 100644 --- a/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/KebaBindingConstants.java +++ b/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/KebaBindingConstants.java @@ -64,6 +64,8 @@ public class KebaBindingConstants { public static final String CHANNEL_SESSION_CONSUMPTION = "sessionconsumption"; public static final String CHANNEL_TOTAL_CONSUMPTION = "totalconsumption"; public static final String CHANNEL_DISPLAY = "display"; + public static final String CHANNEL_AUTHON = "authon"; + public static final String CHANNEL_AUTHREQ = "authreq"; public enum KebaType { P20, diff --git a/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/handler/KeContactHandler.java b/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/handler/KeContactHandler.java index c11099033191b..c67b539909725 100644 --- a/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/handler/KeContactHandler.java +++ b/bundles/org.openhab.binding.keba/src/main/java/org/openhab/binding/keba/internal/handler/KeContactHandler.java @@ -281,12 +281,16 @@ protected void onData(ByteBuffer byteBuffer) { maxSystemCurrent = state; State newState = new DecimalType(state); updateState(new ChannelUID(getThing().getUID(), CHANNEL_MAX_SYSTEM_CURRENT), newState); - if (maxSystemCurrent < maxPresetCurrent) { - transceiver.send("curr " + String.valueOf(maxSystemCurrent), this); - updateState(new ChannelUID(getThing().getUID(), CHANNEL_MAX_PRESET_CURRENT), - new DecimalType(maxSystemCurrent)); - updateState(new ChannelUID(getThing().getUID(), CHANNEL_MAX_PRESET_CURRENT_RANGE), - new PercentType((maxSystemCurrent - 6000) * 100 / (maxSystemCurrent - 6000))); + if (maxSystemCurrent != 0) { + if (maxSystemCurrent < maxPresetCurrent) { + transceiver.send("curr " + String.valueOf(maxSystemCurrent), this); + updateState(new ChannelUID(getThing().getUID(), CHANNEL_MAX_PRESET_CURRENT), + new DecimalType(maxSystemCurrent)); + updateState(new ChannelUID(getThing().getUID(), CHANNEL_MAX_PRESET_CURRENT_RANGE), + new PercentType((maxSystemCurrent - 6000) * 100 / (maxSystemCurrent - 6000))); + } + } else { + logger.debug("maxSystemCurrent is 0. Ignoring."); } break; } @@ -295,8 +299,10 @@ protected void onData(ByteBuffer byteBuffer) { maxPresetCurrent = state; updateState(new ChannelUID(getThing().getUID(), CHANNEL_MAX_PRESET_CURRENT), new DecimalType(state)); - updateState(new ChannelUID(getThing().getUID(), CHANNEL_MAX_PRESET_CURRENT_RANGE), - new PercentType(Math.min(100, (state - 6000) * 100 / (maxSystemCurrent - 6000)))); + if (maxSystemCurrent != 0) { + updateState(new ChannelUID(getThing().getUID(), CHANNEL_MAX_PRESET_CURRENT_RANGE), + new PercentType(Math.min(100, (state - 6000) * 100 / (maxSystemCurrent - 6000)))); + } break; } case "Curr FS": { @@ -413,6 +419,18 @@ protected void onData(ByteBuffer byteBuffer) { updateState(new ChannelUID(getThing().getUID(), CHANNEL_TOTAL_CONSUMPTION), newState); break; } + case "AuthON": { + int state = entry.getValue().getAsInt(); + State newState = new DecimalType(state); + updateState(new ChannelUID(getThing().getUID(), CHANNEL_AUTHON), newState); + break; + } + case "Authreq": { + int state = entry.getValue().getAsInt(); + State newState = new DecimalType(state); + updateState(new ChannelUID(getThing().getUID(), CHANNEL_AUTHREQ), newState); + break; + } } } } catch (JsonParseException e) { diff --git a/bundles/org.openhab.binding.keba/src/main/resources/ESH-INF/thing/kecontact.xml b/bundles/org.openhab.binding.keba/src/main/resources/ESH-INF/thing/kecontact.xml index a1c5a0ddaa227..c94f3c1282fd9 100644 --- a/bundles/org.openhab.binding.keba/src/main/resources/ESH-INF/thing/kecontact.xml +++ b/bundles/org.openhab.binding.keba/src/main/resources/ESH-INF/thing/kecontact.xml @@ -35,6 +35,8 @@ + + @@ -155,5 +157,17 @@ Text to show on the P30 Series C or X display + + Number + + Authentication required + + + + Number + + Authentication enabled + + diff --git a/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/KodiDynamicStateDescriptionProvider.java b/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/KodiDynamicStateDescriptionProvider.java index 0442b3bd3c97d..c1aa97e4f859f 100644 --- a/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/KodiDynamicStateDescriptionProvider.java +++ b/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/KodiDynamicStateDescriptionProvider.java @@ -12,53 +12,30 @@ */ package org.openhab.binding.kodi.internal; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.thing.Channel; -import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.binding.BaseDynamicStateDescriptionProvider; +import org.eclipse.smarthome.core.thing.i18n.ChannelTypeI18nLocalizationService; import org.eclipse.smarthome.core.thing.type.DynamicStateDescriptionProvider; -import org.eclipse.smarthome.core.types.StateDescription; -import org.eclipse.smarthome.core.types.StateOption; import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; /** * Dynamic provider of state options while leaving other state description fields as original. * - * @author Gregory Moyer - Initial contribution - * @author Christoph Weitkamp - Adapted to Kodi binding + * @author Christoph Weitkamp - Initial contribution */ @Component(service = { DynamicStateDescriptionProvider.class, KodiDynamicStateDescriptionProvider.class }) @NonNullByDefault -public class KodiDynamicStateDescriptionProvider implements DynamicStateDescriptionProvider { - private final Map> channelOptionsMap = new ConcurrentHashMap<>(); - - public void setStateOptions(ChannelUID channelUID, List options) { - channelOptionsMap.put(channelUID, options); - } - - @Override - public @Nullable StateDescription getStateDescription(Channel channel, @Nullable StateDescription original, - @Nullable Locale locale) { - List options = channelOptionsMap.get(channel.getUID()); - if (options == null) { - return null; - } +public class KodiDynamicStateDescriptionProvider extends BaseDynamicStateDescriptionProvider { - if (original != null) { - return new StateDescription(original.getMinimum(), original.getMaximum(), original.getStep(), - original.getPattern(), original.isReadOnly(), options); - } - return new StateDescription(null, null, null, null, false, options); + @Reference + protected void setChannelTypeI18nLocalizationService( + final ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) { + this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService; } - @Deactivate - public void deactivate() { - channelOptionsMap.clear(); + protected void unsetChannelTypeI18nLocalizationService( + final ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) { + this.channelTypeI18nLocalizationService = null; } } diff --git a/bundles/org.openhab.binding.lgwebos/.classpath b/bundles/org.openhab.binding.lgwebos/.classpath new file mode 100644 index 0000000000000..74d02f15eb192 --- /dev/null +++ b/bundles/org.openhab.binding.lgwebos/.classpath @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.lgwebos/.project b/bundles/org.openhab.binding.lgwebos/.project new file mode 100644 index 0000000000000..fa271988cc4aa --- /dev/null +++ b/bundles/org.openhab.binding.lgwebos/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.lgwebos + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.lgwebos/NOTICE b/bundles/org.openhab.binding.lgwebos/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/NOTICE rename to bundles/org.openhab.binding.lgwebos/NOTICE diff --git a/addons/binding/org.openhab.binding.lgwebos/README.md b/bundles/org.openhab.binding.lgwebos/README.md similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/README.md rename to bundles/org.openhab.binding.lgwebos/README.md diff --git a/addons/binding/org.openhab.binding.lgwebos/lib/Connect-SDK-Java-Core-1.0.jar b/bundles/org.openhab.binding.lgwebos/lib/Connect-SDK-Java-Core-1.0.jar similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/lib/Connect-SDK-Java-Core-1.0.jar rename to bundles/org.openhab.binding.lgwebos/lib/Connect-SDK-Java-Core-1.0.jar diff --git a/bundles/org.openhab.binding.lgwebos/pom.xml b/bundles/org.openhab.binding.lgwebos/pom.xml new file mode 100644 index 0000000000000..59e6a34519d6a --- /dev/null +++ b/bundles/org.openhab.binding.lgwebos/pom.xml @@ -0,0 +1,37 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.lgwebos + + openHAB Add-ons :: Bundles :: LG webOS Binding + + + + xpp3 + xpp3 + 1.1.4c + provided + + + org.json + json + 20140107 + provided + + + org.java-websocket + java-websocket + 1.3.2 + provided + + + + diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/Application.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/Application.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/Application.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/Application.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/LGWebOSActions.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/LGWebOSActions.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/LGWebOSActions.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/action/LGWebOSActions.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/BaseChannelHandler.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/BaseChannelHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/BaseChannelHandler.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/BaseChannelHandler.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/ChannelHandler.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/ChannelHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/ChannelHandler.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/ChannelHandler.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSBindingConstants.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSBindingConstants.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSBindingConstants.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSHandlerFactory.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSHandlerFactory.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LGWebOSHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LauncherApplication.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LauncherApplication.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LauncherApplication.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/LauncherApplication.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/MediaControlPlayer.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/MediaControlPlayer.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/MediaControlPlayer.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/MediaControlPlayer.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/MediaControlStop.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/MediaControlStop.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/MediaControlStop.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/MediaControlStop.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/PowerControlPower.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/PowerControlPower.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/PowerControlPower.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/PowerControlPower.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/TVControlChannel.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/TVControlChannel.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/TVControlChannel.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/TVControlChannel.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/TVControlChannelName.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/TVControlChannelName.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/TVControlChannelName.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/TVControlChannelName.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/ToastControlToast.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/ToastControlToast.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/ToastControlToast.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/ToastControlToast.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/VolumeControlMute.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/VolumeControlMute.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/VolumeControlMute.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/VolumeControlMute.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/VolumeControlVolume.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/VolumeControlVolume.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/VolumeControlVolume.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/VolumeControlVolume.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/discovery/LGWebOSDiscovery.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/discovery/LGWebOSDiscovery.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/discovery/LGWebOSDiscovery.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/discovery/LGWebOSDiscovery.java diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSHandler.java b/bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSHandler.java rename to bundles/org.openhab.binding.lgwebos/src/main/java/org/openhab/binding/lgwebos/internal/handler/LGWebOSHandler.java diff --git a/addons/binding/org.openhab.binding.lgwebos/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.lgwebos/ESH-INF/config/config.xml b/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/config/config.xml similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/ESH-INF/config/config.xml rename to bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/config/config.xml diff --git a/addons/binding/org.openhab.binding.lgwebos/ESH-INF/i18n/lgwebos.properties b/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/i18n/lgwebos.properties similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/ESH-INF/i18n/lgwebos.properties rename to bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/i18n/lgwebos.properties diff --git a/addons/binding/org.openhab.binding.lgwebos/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/thing/thing-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/ESH-INF/thing/thing-types.xml rename to bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/addons/binding/org.openhab.binding.lgwebos/src/main/resources/openhab-logo-square.png b/bundles/org.openhab.binding.lgwebos/src/main/resources/openhab-logo-square.png similarity index 100% rename from addons/binding/org.openhab.binding.lgwebos/src/main/resources/openhab-logo-square.png rename to bundles/org.openhab.binding.lgwebos/src/main/resources/openhab-logo-square.png diff --git a/bundles/org.openhab.binding.logreader/.classpath b/bundles/org.openhab.binding.logreader/.classpath index a5d95095ccaaf..90f9930589be9 100755 --- a/bundles/org.openhab.binding.logreader/.classpath +++ b/bundles/org.openhab.binding.logreader/.classpath @@ -6,6 +6,12 @@ + + + + + + @@ -13,9 +19,9 @@ + - diff --git a/bundles/org.openhab.binding.logreader/NOTICE b/bundles/org.openhab.binding.logreader/NOTICE index 2dfdc56a4217f..3aa8e63be0dee 100644 --- a/bundles/org.openhab.binding.logreader/NOTICE +++ b/bundles/org.openhab.binding.logreader/NOTICE @@ -14,7 +14,7 @@ https://github.com/openhab/openhab2-addons == Third-party Content -commons-io +commons-io (files under src/3rdparty) * License: Apache 2.0 License * Project: https://commons.apache.org/proper/commons-io/ * Source: https://github.com/paulianttila/commons-io/tree/IO-279-fix diff --git a/bundles/org.openhab.binding.logreader/README.md b/bundles/org.openhab.binding.logreader/README.md index 94aacab2e90b1..386248535ad46 100755 --- a/bundles/org.openhab.binding.logreader/README.md +++ b/bundles/org.openhab.binding.logreader/README.md @@ -98,6 +98,18 @@ rule "LogReader" end ``` +Use the rules with your Telegram Bot (need openHAB Telegram Action installed and configured) + +```xtend +rule "LogReader" + when + Channel 'logreader:reader:openhablog:newErrorEvent' triggered + then + // do something + sendTelegram("bot3", "*ERROR* LogReader Event!\n%s Errors are in the log! Here is the last row of it:\n`%s`", logreaderErrors.state.toString, logreaderLastError.state.toString) + end +``` + Be careful when sending e.g. email notifications. You could easily send thousand of *spam* emails in short period if e.g. one binding is in error loop. diff --git a/bundles/org.openhab.binding.logreader/lib/commons-io-2.7-20180506.jar b/bundles/org.openhab.binding.logreader/lib/commons-io-2.7-20180506.jar deleted file mode 100644 index 396b94731e782..0000000000000 Binary files a/bundles/org.openhab.binding.logreader/lib/commons-io-2.7-20180506.jar and /dev/null differ diff --git a/bundles/org.openhab.binding.logreader/pom.xml b/bundles/org.openhab.binding.logreader/pom.xml index d2dfe215079b5..dd900c4eb61a0 100755 --- a/bundles/org.openhab.binding.logreader/pom.xml +++ b/bundles/org.openhab.binding.logreader/pom.xml @@ -13,4 +13,26 @@ openHAB Add-ons :: Bundles :: Log Reader Binding + + + + org.codehaus.mojo + build-helper-maven-plugin + + + generate-sources + + add-source + + + + src/3rdparty + + + + + + + + diff --git a/bundles/org.openhab.binding.logreader/src/3rdparty/java/org/apache/commons/io/input/Tailer.java b/bundles/org.openhab.binding.logreader/src/3rdparty/java/org/apache/commons/io/input/Tailer.java new file mode 100644 index 0000000000000..b4e9d2b4cb87c --- /dev/null +++ b/bundles/org.openhab.binding.logreader/src/3rdparty/java/org/apache/commons/io/input/Tailer.java @@ -0,0 +1,653 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.io.input; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.charset.Charset; + +import org.apache.commons.io.FileUtils; + +/** + * Simple implementation of the unix "tail -f" functionality. + * + *

1. Create a TailerListener implementation

+ *

+ * First you need to create a {@link TailerListener} implementation + * ({@link TailerListenerAdapter} is provided for convenience so that you don't have to + * implement every method). + *

+ * + *

+ * For example: + *

+ * + *
+ * public class MyTailerListener extends TailerListenerAdapter {
+ *     public void handle(String line) {
+ *         System.out.println(line);
+ *     }
+ * }
+ * 
+ * + *

2. Using a Tailer

+ * + *

+ * You can create and use a Tailer in one of three ways: + *

+ *
    + *
  • Using one of the static helper methods: + *
      + *
    • {@link Tailer#create(File, TailerListener)}
    • + *
    • {@link Tailer#create(File, TailerListener, long)}
    • + *
    • {@link Tailer#create(File, TailerListener, long, boolean)}
    • + *
    + *
  • + *
  • Using an {@link java.util.concurrent.Executor}
  • + *
  • Using an {@link Thread}
  • + *
+ * + *

+ * An example of each of these is shown below. + *

+ * + *

2.1 Using the static helper method

+ * + *
+ * TailerListener listener = new MyTailerListener();
+ * Tailer tailer = Tailer.create(file, listener, delay);
+ * 
+ * + *

2.2 Using an Executor

+ * + *
+ * TailerListener listener = new MyTailerListener();
+ * Tailer tailer = new Tailer(file, listener, delay);
+ *
+ * // stupid executor impl. for demo purposes
+ * Executor executor = new Executor() {
+ *     public void execute(Runnable command) {
+ *         command.run();
+ *     }
+ * };
+ *
+ * executor.execute(tailer);
+ * 
+ * + * + *

2.3 Using a Thread

+ * + *
+ * TailerListener listener = new MyTailerListener();
+ * Tailer tailer = new Tailer(file, listener, delay);
+ * Thread thread = new Thread(tailer);
+ * thread.setDaemon(true); // optional
+ * thread.start();
+ * 
+ * + *

3. Stopping a Tailer

+ *

+ * Remember to stop the tailer when you have done with it: + *

+ * + *
+ * tailer.stop();
+ * 
+ * + *

4. Interrupting a Tailer

+ *

+ * You can interrupt the thread a tailer is running on by calling {@link Thread#interrupt()}. + *

+ * + *
+ * thread.interrupt();
+ * 
+ *

+ * If you interrupt a tailer, the tailer listener is called with the {@link InterruptedException}. + *

+ * + *

+ * The file is read using the default charset; this can be overridden if necessary + *

+ * + * @see TailerListener + * @see TailerListenerAdapter + * @version $Id$ + * @since 2.0 + * @since 2.5 Updated behavior and documentation for {@link Thread#interrupt()} + */ +public class Tailer implements Runnable { + + private static final int EOF = -1; + + private static final int DEFAULT_DELAY_MILLIS = 1000; + + private static final String RAF_MODE = "r"; + + private static final int DEFAULT_BUFSIZE = 4096; + + // The default charset used for reading files + private static final Charset DEFAULT_CHARSET = Charset.defaultCharset(); + + /** + * Buffer on top of RandomAccessFile. + */ + private final byte inbuf[]; + + /** + * The file which will be tailed. + */ + private final File file; + + /** + * The character set that will be used to read the file. + */ + private final Charset cset; + + /** + * The amount of time to wait for the file to be updated. + */ + private final long delayMillis; + + /** + * Whether to tail from the end or start of file + */ + private final boolean end; + + /** + * The listener to notify of events when tailing. + */ + private final TailerListener listener; + + /** + * Whether to close and reopen the file whilst waiting for more input. + */ + private final boolean reOpen; + + /** + * Whether to ignore reading modified file with the same length. + */ + private final boolean ignoreNew; + + /** + * The tailer will run as long as this value is true. + */ + private volatile boolean run = true; + + /** + * Creates a Tailer for the given file, starting from the beginning, with the default delay of 1.0s. + * + * @param file The file to follow. + * @param listener the TailerListener to use. + */ + public Tailer(final File file, final TailerListener listener) { + this(file, listener, DEFAULT_DELAY_MILLIS); + } + + /** + * Creates a Tailer for the given file, starting from the beginning. + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + */ + public Tailer(final File file, final TailerListener listener, final long delayMillis) { + this(file, listener, delayMillis, false); + } + + /** + * Creates a Tailer for the given file, with a delay other than the default 1.0s. + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + */ + public Tailer(final File file, final TailerListener listener, final long delayMillis, final boolean end) { + this(file, listener, delayMillis, end, DEFAULT_BUFSIZE); + } + + /** + * Creates a Tailer for the given file, with a delay other than the default 1.0s. + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + * @param reOpen if true, close and reopen the file between reading chunks + */ + public Tailer(final File file, final TailerListener listener, final long delayMillis, final boolean end, + final boolean reOpen) { + this(file, listener, delayMillis, end, reOpen, DEFAULT_BUFSIZE); + } + + /** + * Creates a Tailer for the given file, with a delay other than the default 1.0s. + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + * @param reOpen if true, close and reopen the file between reading chunks + * @param ignoreNew if true, will ignore reading modified file with the same length. + */ + public Tailer(final File file, final TailerListener listener, final long delayMillis, final boolean end, + final boolean reOpen, final boolean ignoreNew) { + this(file, listener, delayMillis, end, reOpen, ignoreNew, DEFAULT_BUFSIZE); + } + + /** + * Creates a Tailer for the given file, with a specified buffer size. + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + * @param bufSize Buffer size + */ + public Tailer(final File file, final TailerListener listener, final long delayMillis, final boolean end, + final int bufSize) { + this(file, listener, delayMillis, end, false, bufSize); + } + + /** + * Creates a Tailer for the given file, with a specified buffer size. + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + * @param reOpen if true, close and reopen the file between reading chunks + * @param bufSize Buffer size + */ + public Tailer(final File file, final TailerListener listener, final long delayMillis, final boolean end, + final boolean reOpen, final int bufSize) { + this(file, DEFAULT_CHARSET, listener, delayMillis, end, reOpen, false, bufSize); + } + + /** + * Creates a Tailer for the given file, with a specified buffer size. + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + * @param reOpen if true, close and reopen the file between reading chunks + * @param ignoreNew if true, will ignore reading modified file with the same length. + * @param bufSize Buffer size + */ + public Tailer(final File file, final TailerListener listener, final long delayMillis, final boolean end, + final boolean reOpen, final boolean ignoreNew, final int bufSize) { + this(file, DEFAULT_CHARSET, listener, delayMillis, end, reOpen, ignoreNew, bufSize); + } + + /** + * Creates a Tailer for the given file, with a specified buffer size. + * + * @param file the file to follow. + * @param cset the Charset to be used for reading the file + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + * @param reOpen if true, close and reopen the file between reading chunks + * @param ignoreNew if true, will ignore reading modified file with the same length. + * @param bufSize Buffer size + */ + public Tailer(final File file, final Charset cset, final TailerListener listener, final long delayMillis, + final boolean end, final boolean reOpen, final boolean ignoreNew, final int bufSize) { + this.file = file; + this.delayMillis = delayMillis; + this.end = end; + + this.inbuf = new byte[bufSize]; + + // Save and prepare the listener + this.listener = listener; + listener.init(this); + this.reOpen = reOpen; + this.ignoreNew = ignoreNew; + this.cset = cset; + } + + /** + * Creates and starts a Tailer for the given file. + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + * @param bufSize buffer size. + * @return The new tailer + */ + public static Tailer create(final File file, final TailerListener listener, final long delayMillis, + final boolean end, final int bufSize) { + return create(file, listener, delayMillis, end, false, bufSize); + } + + /** + * Creates and starts a Tailer for the given file. + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + * @param reOpen whether to close/reopen the file between chunks + * @param bufSize buffer size. + * @return The new tailer + */ + public static Tailer create(final File file, final TailerListener listener, final long delayMillis, + final boolean end, final boolean reOpen, final int bufSize) { + return create(file, DEFAULT_CHARSET, listener, delayMillis, end, reOpen, false, bufSize); + } + + /** + * Creates and starts a Tailer for the given file. + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + * @param reOpen whether to close/reopen the file between chunks + * @param ignoreNew if true, will ignore reading modified file with the same length. + * @param bufSize buffer size. + * @return The new tailer + */ + public static Tailer create(final File file, final TailerListener listener, final long delayMillis, + final boolean end, final boolean reOpen, final boolean ignoreNew, final int bufSize) { + return create(file, DEFAULT_CHARSET, listener, delayMillis, end, reOpen, ignoreNew, bufSize); + } + + /** + * Creates and starts a Tailer for the given file. + * + * @param file the file to follow. + * @param charset the character set to use for reading the file + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + * @param reOpen whether to close/reopen the file between chunks + * @param ignoreNew if true, will ignore reading modified file with the same length. + * @param bufSize buffer size. + * @return The new tailer + */ + public static Tailer create(final File file, final Charset charset, final TailerListener listener, + final long delayMillis, final boolean end, final boolean reOpen, final boolean ignoreNew, + final int bufSize) { + final Tailer tailer = new Tailer(file, charset, listener, delayMillis, end, reOpen, ignoreNew, bufSize); + final Thread thread = new Thread(tailer); + thread.setDaemon(true); + thread.start(); + return tailer; + } + + /** + * Creates and starts a Tailer for the given file with default buffer size. + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + * @return The new tailer + */ + public static Tailer create(final File file, final TailerListener listener, final long delayMillis, + final boolean end) { + return create(file, listener, delayMillis, end, DEFAULT_BUFSIZE); + } + + /** + * Creates and starts a Tailer for the given file with default buffer size. + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + * @param reOpen whether to close/reopen the file between chunks + * @return The new tailer + */ + public static Tailer create(final File file, final TailerListener listener, final long delayMillis, + final boolean end, final boolean reOpen) { + return create(file, listener, delayMillis, end, reOpen, DEFAULT_BUFSIZE); + } + + /** + * Creates and starts a Tailer for the given file with default buffer size. + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file. + * @param reOpen whether to close/reopen the file between chunks + * @param ignoreNew if true, will ignore reading modified file with the same length. + * @return The new tailer + */ + public static Tailer create(final File file, final TailerListener listener, final long delayMillis, + final boolean end, final boolean reOpen, final boolean ignoreNew) { + return create(file, listener, delayMillis, end, reOpen, ignoreNew, DEFAULT_BUFSIZE); + } + + /** + * Creates and starts a Tailer for the given file, starting at the beginning of the file + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @param delayMillis the delay between checks of the file for new content in milliseconds. + * @return The new tailer + */ + public static Tailer create(final File file, final TailerListener listener, final long delayMillis) { + return create(file, listener, delayMillis, false); + } + + /** + * Creates and starts a Tailer for the given file, starting at the beginning of the file + * with the default delay of 1.0s + * + * @param file the file to follow. + * @param listener the TailerListener to use. + * @return The new tailer + */ + public static Tailer create(final File file, final TailerListener listener) { + return create(file, listener, DEFAULT_DELAY_MILLIS, false); + } + + /** + * Return the file. + * + * @return the file + */ + public File getFile() { + return file; + } + + /** + * Gets whether to keep on running. + * + * @return whether to keep on running. + * @since 2.5 + */ + protected boolean getRun() { + return run; + } + + /** + * Return the delay in milliseconds. + * + * @return the delay in milliseconds. + */ + public long getDelay() { + return delayMillis; + } + + /** + * Follows changes in the file, calling the TailerListener's handle method for each new line. + */ + @Override + public void run() { + RandomAccessFile reader = null; + try { + long last = 0; // The last time the file was checked for changes + long position = 0; // position within the file + // Open the file + while (getRun() && reader == null) { + try { + reader = new RandomAccessFile(file, RAF_MODE); + } catch (final FileNotFoundException e) { + listener.fileNotFound(); + } + if (reader == null) { + Thread.sleep(delayMillis); + } else { + // The current position in the file + position = end ? file.length() : 0; + last = file.lastModified(); + reader.seek(position); + } + } + while (getRun()) { + final boolean newer = FileUtils.isFileNewer(file, last); // IO-279, must be done first + // Check the file length to see if it was rotated + final long length = file.length(); + if (length < position) { + // File was rotated + listener.fileRotated(); + // Reopen the reader after rotation ensuring that the old file is closed iff we re-open it + // successfully + try (RandomAccessFile save = reader) { + reader = new RandomAccessFile(file, RAF_MODE); + // At this point, we're sure that the old file is rotated + // Finish scanning the old file and then we'll start with the new one + try { + readLines(save); + } catch (IOException ioe) { + listener.handle(ioe); + } + position = 0; + } catch (final FileNotFoundException e) { + // in this case we continue to use the previous reader and position values + listener.fileNotFound(); + Thread.sleep(delayMillis); + } + continue; + } else { + // File was not rotated + // See if the file needs to be read again + if (length > position) { + // The file has more content than it did last time + position = readLines(reader); + last = file.lastModified(); + } else if (newer) { + if (!ignoreNew) { + /* + * This can happen if the file is truncated or overwritten with the exact same length of + * information. In cases like this, the file position needs to be reset + */ + position = 0; + reader.seek(position); // cannot be null here + + // Now we can read new lines + position = readLines(reader); + } + last = file.lastModified(); + } + } + if (reOpen && reader != null) { + reader.close(); + } + Thread.sleep(delayMillis); + if (getRun() && reOpen) { + reader = new RandomAccessFile(file, RAF_MODE); + reader.seek(position); + } + } + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + listener.handle(e); + } catch (final Exception e) { + listener.handle(e); + } finally { + try { + if (reader != null) { + reader.close(); + } + } catch (final IOException e) { + listener.handle(e); + } + stop(); + } + } + + /** + * Allows the tailer to complete its current loop and return. + */ + public void stop() { + this.run = false; + } + + /** + * Read new lines. + * + * @param reader The file to read + * @return The new position after the lines have been read + * @throws java.io.IOException if an I/O error occurs. + */ + private long readLines(final RandomAccessFile reader) throws IOException { + try (ByteArrayOutputStream lineBuf = new ByteArrayOutputStream(64)) { + long pos = reader.getFilePointer(); + long rePos = pos; // position to re-read + int num; + boolean seenCR = false; + while (getRun() && ((num = reader.read(inbuf)) != EOF)) { + for (int i = 0; i < num; i++) { + final byte ch = inbuf[i]; + switch (ch) { + case '\n': + seenCR = false; // swallow CR before LF + listener.handle(new String(lineBuf.toByteArray(), cset)); + lineBuf.reset(); + rePos = pos + i + 1; + break; + case '\r': + if (seenCR) { + lineBuf.write('\r'); + } + seenCR = true; + break; + default: + if (seenCR) { + seenCR = false; // swallow final CR + listener.handle(new String(lineBuf.toByteArray(), cset)); + lineBuf.reset(); + rePos = pos + i + 1; + } + lineBuf.write(ch); + } + } + pos = reader.getFilePointer(); + } + + reader.seek(rePos); // Ensure we can re-read if necessary + + if (listener instanceof TailerListenerAdapter) { + ((TailerListenerAdapter) listener).endOfFileReached(); + } + + return rePos; + } + } +} diff --git a/bundles/org.openhab.binding.logreader/src/3rdparty/java/org/apache/commons/io/input/TailerListener.java b/bundles/org.openhab.binding.logreader/src/3rdparty/java/org/apache/commons/io/input/TailerListener.java new file mode 100644 index 0000000000000..712a1ebb5d0a1 --- /dev/null +++ b/bundles/org.openhab.binding.logreader/src/3rdparty/java/org/apache/commons/io/input/TailerListener.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.io.input; + +/** + * Listener for events from a {@link Tailer}. + * + * @since 2.0 + */ +public interface TailerListener { + + /** + * The tailer will call this method during construction, + * giving the listener a method of stopping the tailer. + * + * @param tailer the tailer. + */ + void init(Tailer tailer); + + /** + * This method is called if the tailed file is not found. + *

+ * Note: this is called from the tailer thread. + */ + void fileNotFound(); + + /** + * Called if a file rotation is detected. + * + * This method is called before the file is reopened, and fileNotFound may + * be called if the new file has not yet been created. + *

+ * Note: this is called from the tailer thread. + */ + void fileRotated(); + + /** + * Handles a line from a Tailer. + *

+ * Note: this is called from the tailer thread. + * + * @param line the line. + */ + void handle(String line); + + /** + * Handles an Exception . + *

+ * Note: this is called from the tailer thread. + * + * @param ex the exception. + */ + void handle(Exception ex); + +} diff --git a/bundles/org.openhab.binding.logreader/src/3rdparty/java/org/apache/commons/io/input/TailerListenerAdapter.java b/bundles/org.openhab.binding.logreader/src/3rdparty/java/org/apache/commons/io/input/TailerListenerAdapter.java new file mode 100644 index 0000000000000..8cae1592ceeae --- /dev/null +++ b/bundles/org.openhab.binding.logreader/src/3rdparty/java/org/apache/commons/io/input/TailerListenerAdapter.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.io.input; + +/** + * {@link TailerListener} Adapter. + * + * @since 2.0 + */ +public class TailerListenerAdapter implements TailerListener { + + /** + * The tailer will call this method during construction, + * giving the listener a method of stopping the tailer. + * + * @param tailer the tailer. + */ + @Override + public void init(final Tailer tailer) { + } + + /** + * This method is called if the tailed file is not found. + */ + @Override + public void fileNotFound() { + } + + /** + * Called if a file rotation is detected. + * + * This method is called before the file is reopened, and fileNotFound may + * be called if the new file has not yet been created. + */ + @Override + public void fileRotated() { + } + + /** + * Handles a line from a Tailer. + * + * @param line the line. + */ + @Override + public void handle(final String line) { + } + + /** + * Handles an Exception . + * + * @param ex the exception. + */ + @Override + public void handle(final Exception ex) { + } + + /** + * Called each time the Tailer reaches the end of the file. + * + * Note: this is called from the tailer thread. + * + * Note: a future version of commons-io will pull this method up to the TailerListener interface, + * for now clients must subclass this class to use this feature. + * + * @since 2.5 + */ + public void endOfFileReached() { + } +} diff --git a/bundles/org.openhab.binding.loxone/.classpath b/bundles/org.openhab.binding.loxone/.classpath new file mode 100644 index 0000000000000..7737f39c03922 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/.classpath @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.loxone/.project b/bundles/org.openhab.binding.loxone/.project new file mode 100644 index 0000000000000..8490bac942f16 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.loxone + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/bundles/org.openhab.binding.loxone/NOTICE b/bundles/org.openhab.binding.loxone/NOTICE new file mode 100644 index 0000000000000..e374be948c848 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/NOTICE @@ -0,0 +1,20 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab2-addons + +== Third-party Content + +harmony-client +* License: EPL 2.0 License +* Project: https://github.com/digitaldan/harmony-client +* Source: https://github.com/digitaldan/harmony-client diff --git a/bundles/org.openhab.binding.loxone/README.md b/bundles/org.openhab.binding.loxone/README.md new file mode 100644 index 0000000000000..005b8b9cac3c8 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/README.md @@ -0,0 +1,295 @@ +# Loxone Binding + +This binding integrates [Loxone Miniserver](https://www.loxone.com/enen/products/miniserver-extensions/) with [openHAB](http://www.openhab.org/). +Miniserver is represented as a [Thing](https://www.openhab.org/docs/configuration/things.html). Miniserver controls, that are visible in the Loxone [UI](https://www.loxone.com/enen/kb/user-interface-configuration/), are exposed as openHAB channels. + +## Features + +The following features are currently supported: + +* [Discovery](https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol) of Miniservers available on the local network +* Creation of channels for Loxone controls that are exposed in the Loxone [UI](https://www.loxone.com/enen/kb/user-interface-configuration/) +* Tagging of channels and [items](https://www.openhab.org/docs/configuration/items.html) with tags that can be recognized by [Alexa](https://en.wikipedia.org/wiki/Amazon_Alexa) openHAB [skill](https://www.amazon.com/openHAB-Foundation/dp/B01MTY7Z5L), so voice can be used to command Loxone controls +* Management of a Websocket connection to the Miniserver and updating Thing status accordingly +* Updates of openHAB channel's state in runtime according to control's state changes on the Miniserver +* Passing channel commands to the Miniserver's controls +* Hash-based and token-based authentication methods +* Command encryption and response decryption + +## Things + +This binding supports [Loxone Miniservers](https://www.loxone.com/enen/products/miniserver-extensions/) for accessing controls that are configured in their UI. + +The Thing UID of automatically discovered Miniservers is: `loxone:miniserver:`, where `` is a serial number of the Miniserver (effectively this is the MAC address of its network interface). + +### Discovery + +[Loxone Miniservers](https://www.loxone.com/enen/products/miniserver-extensions/) are automatically discovered by the binding and put in the Inbox. [Discovery](https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol) is performed using [UPnP](https://en.wikipedia.org/wiki/Universal_Plug_and_Play) protocol. + +Before a Miniserver Thing can go online, it must be configured with a user name and a password of an account available on the Miniserver. +Please set them manually in Thing configuration after you add a new Miniserver Thing from your Inbox. + +### Manual configuration + +As an alternative to the automatic discovery process, Miniservers can be configured manually, through an entry in [.things file](https://www.openhab.org/docs/configuration/things.html#defining-things-using-files). +The entry should have the following syntax: + +`loxone:miniserver: [ user="", password="", host="", port=, ... ]` + +Where: + +* `` is a unique ID for your Miniserver (you can but do not have to use Miniserver's MAC address here) +* `` and `` are the credentials used to log into the Miniserver +* `` is a host name or IP of the Miniserver +* `` is a port of web services on the Miniserver (please notice that port, as a number, is not surrounded by quotation marks, while the other values described above are) +* `...` are optional advanced parameters - please refer to _Advanced parameters_ section at the end of this instruction for a list of available options + +Example 1 - minimal required configuration: + + `loxone:miniserver:504F2414780F [ user="kryten", password="jmc2017", host="loxone.local", port=80 ]` + +Example 2 - additionally keep alive period is set to 2 minutes and Websocket maximum binary message size to 8MB: + + `loxone:miniserver:504F2414780F [ user="kryten", password="jmc2017", host="192.168.0.210", port=80, keepAlivePeriod=120, maxBinMsgSize=8192 ]` + +### Thing Offline Reasons + +There can be following reasons why Miniserver status is `OFFLINE`: + +* __Configuration Error__ + * _Unknown host_ + * Miniserver host/ip address can't be resolved. No connection attempt will be made. + * _User authentication error_ + * Invalid user name or password or user not authorized to connect to the Miniserver. Binding will make another attempt to connect after some time. + * _Too many failed login attempts - stopped trying_ + * Miniserver locked out user for too many failed login attempts. In this case binding will stop trying to connect to the Miniserver. A new connection will be attempted only when user corrects user name or password in the configuration parameters. + * _Enter password to generate a new token_ + * Authentication using stored token failed - either token is wrong or it. A password must be reentered in the binding settings to acquire a new token. + * _Internal error_ + * Probably a code defect, collect debug data and submit an issue. Binding will try to reconnect, but with unknown chance for success. + * _Other_ + * An exception occured and its details will be displayed +* __Communication Error__ + * _Error communicating with Miniserver_ + * I/O error occurred during established communication with the Miniserver, most likely due to network connectivity issues, Miniserver going offline or Loxone Config is uploading a new configuration. A reconnect attempt will be made soon. Please consult detailed message against one of the following: + * _"Text message size &lsqbXX&rsqb exceeds maximum size &lsqbYY&rsqb"_ - adjust text message size in advanced parameters to be above XX value + * _"Binary message size &lsqbXX&rsqb exceeds maximum size &lsqbYY&rsqb"_ - adjust binary message size in advanced parameters to be above XX value + * _User authentication timeout_ + * Authentication procedure took too long time and Miniserver closed connection. It should not occur under normal conditions and may indicate performance issue on binding's OS side. + * _Timeout due to no activity_ + * Miniserver closed connection because there was no activity from binding. It should not occur under normal conditions, as it is prevented by sending keep-alive messages from the binding to the Miniserver. By default Miniserver's timeout is 5 minutes and period between binding's keep-alive messages is 4 minutes. If you see this error, try changing the keep-alive period in binding's configuration to a smaller value. + * _Other_ + * An exception occured and its details will be displayed + +### Security + +The binding supports the following authentication methods, which are selected automatically based on the firmware version. They can be also chosen manually in the advanced settings. + +| Method | Miniserver Firmware | Authentication | Encryption | Requirements | +|-------------|---------------------|--------------------------------------------------------------------------------|------------|-------------------------------------------------------| +| Hash-based | 8.x | HMAC-SHA1 hash on user and password | None | None | +| Token-based | 9.x | Token acquired on the first connection and used later instead of the password. | AES-256 | JRE must have unrestricted security policy configured | + +For the token-based authentication, the password is required only for the first login and acquiring the token. After the token is acquired, the password is cleared in the binding configuration. + +The acquired token will remain active for several weeks following the last succesful authentication with this token. If the connection is not established used during that period and the token expires, a user password has to be re-entered in the binding settings to acquire a new token. + +In case a websocket connection to the Miniserver remains active for the whole duration of the token's life span, the binding will refresh the token one day before token expiration, without the need of providing the password. + + +A method to enable unrestricted security policy depends on the JRE version and vendor, some examples can be found [here](https://www.petefreitag.com/item/844.cfm) and [here](https://stackoverflow.com/questions/41580489/how-to-install-unlimited-strength-jurisdiction-policy-files). + +## Channels + +This binding creates channels for controls that are [used in Loxone's user interface](https://www.loxone.com/enen/kb/user-interface-configuration/). +Currently supported controls are presented in the table below. + +| [Loxone API Control](https://www.loxone.com/enen/kb/api/) | Loxone Block-Functions | [Item Types](https://www.openhab.org/docs/concepts/items.html) | Supported Commands | +|-----------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Alarm | [Burglar Alarm](https://www.loxone.com/enen/kb/burglar-alarm/) | `Switch` - arm the alarm | `OnOffType.*` | +| | | `Switch` - arm with delay - pushbuton | `OnOffType.ON` - armes the alarm with delay | +| | | `Number` - ID of the next alarm level | Read-only channel | +| | | `Number` - delay of the next alarm level | Read-only channel | +| | | `Number` - total delay of the next alarm level | Read-only channel | +| | | `Number` - current alarm level | Read-only channel | +| | | `DateTime` - time when alarm started | Read-only channel | +| | | `Number` - delay of the alarm being armed | Read-only channel | +| | | `Number` - total delay of the alarm being armed | Read-only channel | +| | | `String` - list of alarm sensors separated with `|` | Read-only channel | +| | | `Switch` - acknowledge the alarm - pushbutton | `OnOffType.ON` - acknowledge alarm | +| ColorPickerV2 | [RGBW 24v Dimmer Tree](https://www.loxone.com/enen/kb/rgbw-24v-dimmer-tree/) | `Color` | `HSBType` - sets the color of the light, `DecimalType` and `PercentType` - sets the brightness, `IncreaseDecreaseType.*` - increases/decreases the brightness, `OnOffType.*` - switches light on/off | +| Dimmer | [Dimmer](https://www.loxone.com/enen/kb/dimmer/) | `Dimmer` | `OnOffType.*`, `PercentType` | +| InfoOnlyAnalog | Analog [virtual inputs](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) (virtual state) | `Number` | Read-only channel | +| InfoOnlyDigital | Digital [virtual inputs](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) (virtual state) | `String` | Read-only channel | +| Jalousie | Blinds, [Automatic Blinds](https://www.loxone.com/enen/kb/automatic-blinds/), Automatic Blinds Integrated | `Rollershutter` - main control element | `UpDownType.*`, `StopMoveType.*`, `PercentType` | +| | | `Switch` - shading | `OnOffType.ON` - shade | +| | | `Switch` - automatic shading | `OnOffType.*` - automatic shading enabled/disabled | +| LeftRightAnalog | Analog [Virtual input](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) of left-right buttons type | `Number` | `DecimalType` | +| LeftRightDigital | Digital [Virtual input](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) of left-right buttons type | `Switch` (left button) | `OnOffType.*` - left on/off, sets right to off | +| | | `Switch` (right button) | `OnOffType.*` - right on/off, sets left to off | +| LightController | [Lighting controller V1 (obsolete)](https://www.loxone.com/enen/kb/lighting-controller/), [Hotel lighting controller](https://www.loxone.com/enen/kb/hotel-lighting-controller/)
Additionally, for each configured output of a lighting controller, a new independent control (with own channel/item) will be created. | `Number` | `DecimalType` - select lighting scene, `UpDownType.*` - swipe through scenes, `OnOffType.*` - select all off or all on scene | +| LightControllerV2 | [Lighting controller](https://www.loxone.com/enen/kb/lighting-controller-v2/)
Additionally, for each configured output and for each mood of a lighting controller, a new independent control (with own channel/item) will be created. | `Number` | `DecimalType` - select mood, `UpDownType.*` - swipe through moods | +| LightControllerV2 Mood | A mood defined for a [Lighting controller](https://www.loxone.com/enen/kb/lighting-controller-v2/). Each mood will have own channel and can be operated independently in order to allow mixing of moods. | `Switch` | `OnOffType.*` - mixes mood in or out of the controller | +| Pushbutton | [Virtual inputs](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) of pushbutton type | `Switch` | `OnOffType.ON` - generates Pulse command | +| Radio | [Radio button 8x and 16x](https://www.loxone.com/enen/kb/radio-buttons/) | `Number` | `DecimalType` - select output number 1-8/16 or 0 for all outputs off, `OnOffType.OFF` - all outputs off | +| Slider | [Virtual inputs](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) of slider type | `Number` | `DecimalType` | +| Switch | [Virtual inputs](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) of switch type
[Push-button](https://www.loxone.com/enen/kb/push-button/) | `Switch` | `OnOffType.*` | +| TextState | [State](https://www.loxone.com/enen/kb/state/) | `String` | Read-only channel | +| TimedSwitch | [Stairwell light switch](https://www.loxone.com/enen/kb/stairwell-light-switch/) or [Multifunction switch](https://www.loxone.com/enen/kb/multifunction-switch/) | `Switch` | `OnOffType.*` - ON sends pulse to Loxone | +| | | `Number` | Read-only countdown value to off | +| Tracker | [Tracker](https://www.loxone.com/enen/kb/tracker/) | `String` | Read-only channel | +| UpDownAnalog | Analog [Virtual input](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) of up-down buttons type | `Number` | `DecimalType` | +| UpDownDigital | Digital [Virtual input](https://www.loxone.com/enen/kb/virtual-inputs-outputs/) of up-down buttons type | `Switch` - up button | `OnOffType.*` - up on/off, sets down to off | +| | | `Switch` - down button | `OnOffType.*` - down on/off, sets up to off | +| ValueSelector | [Selection Switch+](https://www.loxone.com/enen/kb/selection-switch-plus/), [Selection Switch+/-](https://www.loxone.com/enen/kb/selection-switch-plus-minus/) | `Dimmer` - selected value will be scaled to min/max | `OnOffType.*` - sets selector to max or min value, `PercentType` - sets selector to % of min-max range, `IncreaseDecreaseType` - adds or subtracts step value from the selector | +| | | `Number` - direct input of selected value | `DecimalType` - must be between min-max range | +| Webpage | [Web Page](https://www.loxone.com/enen/kb/webpage/) | `String` - low-res URL | Read-only channel | +| | | `String` - high-res URL | Read-only channel | + +If your control is supported, but binding does not recognize it, please check if it is exposed in Loxone UI using [Loxone Config](https://www.loxone.com/enen/kb-cat/loxone-config/) application. + +Most controls have a single channel. Such channel ID is defined in the following way: + +* `loxone:miniserver::` + +Controls, which have more than one channel, define the channel ID of the extra channels in the following way: + +* `loxone:miniserver::-`, where `` is equal to 1, 2, ... + +Channel label is defined in the following way: + +* For controls that belong to a room: ` / ` +* For controls without a room: `` + + +## Advanced Parameters + +This section describes the optional advanced parameters that can be configured for a Miniserver. They can be set using UI (e.g. PaperUI) or in a .things file. +If a parameter is not explicitly defined, binding will use its default value. + +To define a parameter value in a .things file, please refer to it by parameter's ID, for example: + + keepAlivePeriod=120 + +### Security + +| ID | Name | Values | Default | Description | +|--------------|-----------------------|-------------------------------------------------|--------------|-------------------------------------------------------| +| `authMethod` | Authentication method | 0: Automatic
1: Hash-based
2: Token-based | 0: Automatic | A method used to authenticate user in the Miniserver. | + +### Timeouts + +Timeout values control various parts of Websocket connection management. +They can be tuned, when abnormal behavior of the binding is observed, which can be attributed to timing. +
+ +| ID | Name | Range | Default | Description | +|-------------------|-----------------------------------------------|----------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `firstConDelay` | First connection delay | 0-120 s | 1 s | Time in seconds between binding initialization with all necessary parameters and first connection attempt. | +| `keepAlivePeriod` | Period between connection keep-alive messages | 1-600 s | 240 s | Time in seconds between sending two consecutive keep-alive messages, in order to inform Miniserver about active connection and prevent it from disconnecting. Miniserver default connection timeout is 5 minutes, so default is set to 4 minutes. | +| `connectErrDelay` | Connect error delay | 0-600 s | 10 s | Time in seconds between failed Websocket connect attempt and another attempt to connect. Websocket connection is established before authentication and data transfer. It can usually fail due to unreachable Miniserver. | +| `responseTimeout` | Response timeout | 0-60 s | 4 s | Time to wait for a response from Miniserver to a request sent from the binding. A request can be any of: websocket connect request, credentials hashing key request, configuration request, enabling of state updates (until initial states are received). If this time passed without the expected reaction from the Miniserver, the connection will be closed. A new connection attempt may be made, depending on the situation. | +| `userErrorDelay` | Authentication error delay | 0-3600 s | 60 s | Time in seconds between user authentication error and another connection attempt. User authentication error can be a result of a wrong name or password, or no authority granted to the user on the Miniserver. If this time is too short, Miniserver will eventually lock out the user for a longer period of time due to too many failed login attempts. This time should allow the administrator to fix the authentication issue without being locked out. Connection retry is required, because very rarely Miniserver seems to reject correct credentials, which are successful on a subsequent identical attempt. | +| `comErrorDelay` | Communication error delay | 0-3600 s | 30 s | Time in seconds between an active connection closes, as a result of a communication error, and next connection attempt. This relates to all types of network communication issues, which can occur and cease to exist randomly to the binding. It is desired that the binding monitors the situation and brings things back to online as soon as Miniserver is accessible. | + +### Sizes + +| ID | Name | Range | Default | Description | +|------------------|----------------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `maxBinMsgSize` | Maximum binary message size (kB) | 0-100 MB | 3072 (3 MB) | For Websocket client, a maximum size of a binary message that can be received from the Miniserver. If you get communication errors with a message indicating there are too long binary messages received, you may need to adjust this parameter. | +| `maxTextMsgSize` | Maximum text message size (kB) | 0-100 MB | 512 KB | For Websocket client, a maximum size of a text message that can be received from the Miniserver. If you get communication errors with a message indicating there are too long text messages received, you may need to adjust this parameter. | + +## Limitations + +* As there is no push button item type in openHAB, Loxone's push button is an openHAB's switch, which always generates a short pulse on changing its state to on. If you use simple UI mode and framework generates items for you, switches for push buttons will still be toggle switches. To change it to the push button style, you have to create item manually with `autoupdate=false` parameter. An example of such item definition is given in the _Items_ section above. + +## Automatic Configuration Example + +The simplest and quickest way of configuring a Loxone Miniserver with openHAB is to use automatic configuration features: + +* Make sure your Miniserver is up and running and on the same network segment as openHAB server. +* Add Loxone binding from the available `Add-ons`. +* In `Configuration/System` page, set `Item Linking` to `Simple Mode` (don't forget to save your choice). +* Add your Miniserver Thing from the `Inbox`, after automatic discovery is performed by the framework during binding initialization. +* Configure your Miniserver by editing Miniserver Thing in `Configuration/Things` page and providing user name and password. +* Miniserver Thing should go online. Channels and Items will be automatically created and configured. +* On the `Control` page, you can test Miniserver Items and interact with them. +* As the user interface, you may use [HABPanel](https://www.openhab.org/docs/configuration/habpanel.html), where all Miniserver's items are ready for picking up, using entirely the graphical user interface. + +## Manual Configuration Example + +A more advanced setup requires manual creation and editing of openHAB configuration files, according to the instructions provided in [configuration user guide](https://www.openhab.org/docs/configuration/). +In this example we will manually configure: + +* A Miniserver with serial number 504F2414780F, available at IP 192.168.0.220 and with web services port 80 +* A Miniserver's user named "kryten" and password "jmc2017" +* Items for: + * Temperature of the Miniserver - a Virtual Analog State functional block + * State of a garage door - a Virtual Digital State funtional block (ON=door open, OFF=door closed) + * Kitchen lights switch - a Switch Subcontrol at the AI1 output of a Lighting Controller functional block (with a tag recognizable by Alexa service) + * Pushbutton to switch all lights off - a Virtual Input of Pushbutton type functional block (pushbutton realized by adding `autoupdate="false"` parameter) + * Kitchen blinds - a Jalousie functional block + * Lighting scene - a Lighting Controller functional block + * Output valve selection for garden watering - 8x Radio Button functional block, where only one valve can be open at a time + * A text displaying current alarm's state - a State functional block + +### things/loxone.things: + +``` +loxone:miniserver:504F2414780F [ user="kryten", password="jmc2017", host="192.168.0.220", port=80 +``` + +### items/loxone.items: + +``` +// Type ID Label Icon Tags Settings + +Number Miniserver_Temp "Miniserver temperature: [%.1f °C]" {channel="loxone:miniserver:504F2414780F:0F2F2133-017D-3C82-FFFF203EB0C34B9E"} +Switch Garage_Door "Garage door [MAP(garagedoor.map):%s]" {channel="loxone:miniserver:504F2414780F:0F2F2133-017D-3C82-FFFF203EB0C34B9E"} +Switch Kitchen_Lights "Kitchen Lights" ["lighting"] {channel="loxone:miniserver:504F2414780F:0EC5E0CF-0255-6ABD-FFFF402FB0C24B9E_AI1"} +Switch Stair_Lights "Stair Lights" ["lighting"] {channel="loxone:miniserver:504F2414780F:0EC5E0CF-0255-31BD-FFFF402FB0C24B9E"} +Number Stair_Lights-1 "Stair Lights Deactivation Delay" ["lighting"] {channel="loxone:miniserver:504F2414780F:0EC5E0CF-0255-31BD-FFFF402FB0C24B9E-1"} +Switch Reset_Lights "Switch all lights off" ["lighting"] {channel="loxone:miniserver:504F2414780F:0F2F2133-01AD-3282-FFFF201EB0C24B9E",autoupdate="false"} +Rollershutter Kitchen_Blinds "Kitchen blinds" {channel="loxone:miniserver:504F2414780F:0F2E2123-014D-3232-FFEF204EB3C24B9E"} +Dimmer Kitchen_Dimmer "Kitchen dimmer" ["lighting"] {channel="loxone:miniserver:504F2414780F:0F2E2123-014D-3232-FFEF207EB3C24B9E"} +Number Light_Scene "Lighting scene" {channel="loxone:miniserver:504F2414780F:0FC4E0DF-0255-6ABD-FFFE403FB0C34B9E"} +Number Mood_Selector "Lighting mood" {channel="loxone:miniserver:504F2414780F:0FC4E0DF-0255-6ABD-FFFE203EA0C34B9E"} +Switch Mood_Enter_Home "Entering home" {channel="loxone:miniserver:504F2414780F:0FC4E0DF-0255-6ABD-FFFE203EA0C34B9E-M1"} +Switch Mood_Read_Book "Reading book" {channel="loxone:miniserver:504F2414780F:0FC4E0DF-0255-6ABD-FFFE203EA0C34B9E-M2"} +Switch Mood_Evening "Evening setup" {channel="loxone:miniserver:504F2414780F:0FC4E0DF-0255-6ABD-FFFE203EA0C34B9E-M3"} +Number Garden_Valve "Garden watering section" {channel="loxone:miniserver:504F2414780F:0FC5E0DF-0355-6AAD-FFFE403FB0C34B9E"} +String Alarm_State "Alarm state [%s]" {channel="loxone:miniserver:504F2414780F:0F2E2134-017D-3E82-FFFF433FB4A34B9E"} +``` + +### sitemaps/loxone.sitemap: + +``` +sitemap loxone label="Loxone Example Menu" +{ + Frame label="Demo Controls" { + Text item=Miniserver_Temp + Text item=Garage_Door + Switch item=Kitchen_Lights + Switch item=Reset_Lights + Switch item=Kitchen_Blinds + Slider item=Kitchen_Dimmer switchSupport + Switch item=Stairs_Light + Text item=Stairs_Light-1 + Selection item=Light_Scene mappings=[0="All off", 1="My scene 1", 2="My scene 2", 9="All on"] + Selection item=Mood_Selector + Switch item=Mood_Enter_Home + Switch item=Mood_Read_Book + Switch item=Mood_Evening + Setpoint item=Garden_Valve minValue=0 maxValue=8 step=1 + Text item=Alarm_State + } +} +``` + +### transform/garagedoor.map: + +```java +OFF=Closed +ON=Open +-=Unknown +``` diff --git a/bundles/org.openhab.binding.loxone/pom.xml b/bundles/org.openhab.binding.loxone/pom.xml new file mode 100644 index 0000000000000..c027d5eaf2e97 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.loxone + + openHAB Add-ons :: Bundles :: Loxone Binding + + diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxBindingConfiguration.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxBindingConfiguration.java new file mode 100644 index 0000000000000..9de618932a76e --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxBindingConfiguration.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal; + +/** + * Configuration of a Loxone Miniserver ({@link LxServerHandler}) + * + * @author Pawel Pieczul - Initial contribution + * + */ +public class LxBindingConfiguration { + /** + * Host address or IP of the Miniserver + */ + public String host; + /** + * Port of web service of the Miniserver + */ + public int port; + /** + * User name used to log into the Miniserver + */ + public String user; + /** + * Password used to log into the Miniserver + */ + public String password; + /** + * Authentication token acquired from the Miniserver + */ + public String authToken; + /** + * Time in seconds between binding initialization and first connection attempt + */ + public int firstConDelay; + /** + * Time in seconds between sending two consecutive keep-alive messages + */ + public int keepAlivePeriod; + /** + * Time in seconds between failed websocket connect attempts + */ + public int connectErrDelay; + /** + * Time to wait for Miniserver response to a request sent from the binding + */ + public int responseTimeout; + /** + * Time in seconds between user login error as a result of wrong name/password or no authority and next connection + * attempt + */ + public int userErrorDelay; + /** + * Time in seconds between connection close (as a result of some communication error) and next connection attempt + */ + public int comErrorDelay; + /** + * Websocket client's max binary message size in kB + */ + public int maxBinMsgSize; + /** + * Websocket client's max text message size in kB + */ + public int maxTextMsgSize; + /** + * Authentication method (0-auto, 1-hash, 2-token) + */ + public int authMethod; +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxBindingConstants.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxBindingConstants.java new file mode 100644 index 0000000000000..bac39273146e3 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxBindingConstants.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * Common constants used across the whole binding. + * + * @author Pawel Pieczul - Initial contribution + */ +@NonNullByDefault +public class LxBindingConstants { + + public static final String BINDING_ID = "loxone"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_MINISERVER = new ThingTypeUID(BINDING_ID, "miniserver"); + + // Channel Type IDs - read/write + public static final String MINISERVER_CHANNEL_TYPE_SWITCH = "switchTypeId"; + public static final String MINISERVER_CHANNEL_TYPE_LIGHT_CTRL = "lightCtrlTypeId"; + public static final String MINISERVER_CHANNEL_TYPE_RADIO_BUTTON = "radioButtonTypeId"; + public static final String MINISERVER_CHANNEL_TYPE_ROLLERSHUTTER = "rollerShutterTypeId"; + public static final String MINISERVER_CHANNEL_TYPE_DIMMER = "dimmerTypeId"; + public static final String MINISERVER_CHANNEL_TYPE_NUMBER = "numberTypeId"; + public static final String MINISERVER_CHANNEL_TYPE_COLORPICKER = "colorPickerTypeId"; + + // Channel Type IDs - read only + public static final String MINISERVER_CHANNEL_TYPE_RO_TEXT = "roTextTypeId"; + public static final String MINISERVER_CHANNEL_TYPE_RO_SWITCH = "roSwitchTypeId"; + public static final String MINISERVER_CHANNEL_TYPE_RO_ANALOG = "roAnalogTypeId"; + public static final String MINISERVER_CHANNEL_TYPE_RO_NUMBER = "roNumberTypeId"; + public static final String MINISERVER_CHANNEL_TYPE_RO_DATETIME = "roDateTimeTypeId"; + + // Miniserver properties and parameters + public static final String MINISERVER_PARAM_HOST = "host"; + public static final String MINISERVER_PARAM_PORT = "port"; + public static final String MINISERVER_PROPERTY_MINISERVER_NAME = "name"; + public static final String MINISERVER_PROPERTY_PROJECT_NAME = "project"; + public static final String MINISERVER_PROPERTY_CLOUD_ADDRESS = "cloudAddress"; + + // Location as configured on the Miniserver - it may be different to the Thing location property, which is user + // defined and influences the grouping of items in the UI + public static final String MINISERVER_PROPERTY_PHYSICAL_LOCATION = "physicalLocation"; +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxDiscoveryParticipant.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxDiscoveryParticipant.java new file mode 100644 index 0000000000000..8897b73e013eb --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxDiscoveryParticipant.java @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.upnp.UpnpDiscoveryParticipant; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.jupnp.model.meta.DeviceDetails; +import org.jupnp.model.meta.RemoteDevice; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link LxDiscoveryParticipant} class creates Miniserver things. + * It analyzes UPNP devices discovered by the framework and if Loxone Miniserver is found, + * a new thing discovery is reported, which in turn will result in creating a {@link Thing} + * and subsequently a new {@link LxServerHandler} object. + * + * @author Pawel Pieczul - Initial contribution + * + */ +@Component(immediate = true) +public class LxDiscoveryParticipant implements UpnpDiscoveryParticipant { + + private final Logger logger = LoggerFactory.getLogger(LxDiscoveryParticipant.class); + + @Override + public Set getSupportedThingTypeUIDs() { + return LxServerHandler.SUPPORTED_THING_TYPES_UIDS; + } + + @Override + public DiscoveryResult createResult(RemoteDevice device) { + ThingUID uid = getThingUID(device); + if (uid != null) { + Map properties = new HashMap<>(2); + + // After correct Thing UID is created, we have confidence that all following parameters exist and we don't + // need to check for null objects here in the device details + DeviceDetails details = device.getDetails(); + String serial = details.getSerialNumber(); + String host = details.getPresentationURI().getHost(); + String label = details.getFriendlyName() + " @ " + host; + int port = details.getPresentationURI().getPort(); + String vendor = details.getManufacturerDetails().getManufacturer(); + String model = details.getModelDetails().getModelName(); + + logger.debug("Creating discovery result for serial {} label {} port {}", serial, label, port); + properties.put(LxBindingConstants.MINISERVER_PARAM_HOST, host); + properties.put(LxBindingConstants.MINISERVER_PARAM_PORT, port); + properties.put(Thing.PROPERTY_VENDOR, vendor); + properties.put(Thing.PROPERTY_MODEL_ID, model); + properties.put(Thing.PROPERTY_SERIAL_NUMBER, serial); + + return DiscoveryResultBuilder.create(uid).withProperties(properties).withLabel(label) + .withRepresentationProperty(serial).build(); + } + return null; + } + + @Override + public ThingUID getThingUID(RemoteDevice device) { + String manufacturer = device.getDetails().getManufacturerDetails().getManufacturer(); + if (manufacturer != null && manufacturer.toLowerCase().contains("loxone")) { + String model = device.getDetails().getModelDetails().getModelName(); + if (model != null && model.toLowerCase().contentEquals("loxone miniserver")) { + String serial = device.getDetails().getSerialNumber(); + if (serial == null) { + serial = device.getIdentity().getUdn().getIdentifierString(); + } + if (serial != null) { + return new ThingUID(LxBindingConstants.THING_TYPE_MINISERVER, serial); + } + } + } + return null; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxDynamicStateDescriptionProvider.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxDynamicStateDescriptionProvider.java new file mode 100644 index 0000000000000..ec7a4c5ea593b --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxDynamicStateDescriptionProvider.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal; + +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.DynamicStateDescriptionProvider; +import org.eclipse.smarthome.core.types.StateDescription; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Dynamic channel state description provider. + * Overrides the state description for the controls, which receive its configuration in the runtime. + * + * @author Pawel Pieczul - Initial contribution + */ +@Component(service = { DynamicStateDescriptionProvider.class, LxDynamicStateDescriptionProvider.class }) +@NonNullByDefault +public class LxDynamicStateDescriptionProvider implements DynamicStateDescriptionProvider { + + private Map descriptions = new ConcurrentHashMap<>(); + private Logger logger = LoggerFactory.getLogger(LxDynamicStateDescriptionProvider.class); + + /** + * Set a state description for a channel. This description will be used when preparing the channel state by + * the framework for presentation. A previous description, if existed, will be replaced. + * + * @param channelUID channel UID + * @param description state description for the channel + */ + void setDescription(ChannelUID channelUID, StateDescription description) { + logger.debug("Adding state description for channel {}", channelUID); + descriptions.put(channelUID, description); + } + + /** + * Clear all registered state descriptions + */ + void removeAllDescriptions() { + logger.debug("Removing all state descriptions"); + descriptions.clear(); + } + + /** + * Removes a state description for a given channel ID + * + * @param channelUID channel ID to remove description for + */ + void removeDescription(ChannelUID channelUID) { + logger.debug("Removing state description for channel {}", channelUID); + descriptions.remove(channelUID); + } + + @Override + public @Nullable StateDescription getStateDescription(Channel channel, + @Nullable StateDescription originalStateDescription, @Nullable Locale locale) { + StateDescription description = descriptions.get(channel.getUID()); + logger.trace("Providing state description for channel {}", channel.getUID()); + return description; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxHandlerFactory.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxHandlerFactory.java new file mode 100644 index 0000000000000..700c023dae5a6 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxHandlerFactory.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal; + +import java.util.Collections; +import java.util.Set; + +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +/** + * Factory responsible for creating Loxone things (Miniservers) and their handlers ({@link LxServerHandler} + * + * @author Pawel Pieczul - Initial contribution + */ +@Component(service = ThingHandlerFactory.class, immediate = true, configurationPid = "binding.loxone") +public class LxHandlerFactory extends BaseThingHandlerFactory { + + public static final Set SUPPORTED_THING_TYPES_UIDS = Collections + .singleton(LxBindingConstants.THING_TYPE_MINISERVER); + + private LxDynamicStateDescriptionProvider dynamicStateDescriptionProvider; + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected ThingHandler createHandler(Thing thing) { + ThingTypeUID uid = thing.getThingTypeUID(); + if (uid.equals(LxBindingConstants.THING_TYPE_MINISERVER)) { + LxServerHandler handler = new LxServerHandler(thing, dynamicStateDescriptionProvider); + return handler; + } + return null; + } + + @Reference + protected void setDynamicStateDescriptionProvider(LxDynamicStateDescriptionProvider provider) { + this.dynamicStateDescriptionProvider = provider; + } + + protected void unsetDynamicStateDescriptionProvider(LxDynamicStateDescriptionProvider provider) { + this.dynamicStateDescriptionProvider = null; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxServerHandler.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxServerHandler.java new file mode 100644 index 0000000000000..acfdc9e3c873d --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxServerHandler.java @@ -0,0 +1,735 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; +import java.nio.channels.Channels; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.StateDescription; +import org.openhab.binding.loxone.internal.controls.LxControl; +import org.openhab.binding.loxone.internal.types.LxConfig; +import org.openhab.binding.loxone.internal.types.LxConfig.LxServerInfo; +import org.openhab.binding.loxone.internal.types.LxErrorCode; +import org.openhab.binding.loxone.internal.types.LxResponse; +import org.openhab.binding.loxone.internal.types.LxState; +import org.openhab.binding.loxone.internal.types.LxUuid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * Representation of a Loxone Miniserver. It is an openHAB {@link Thing}, which is used to communicate with + * objects (controls) configured in the Miniserver over {@link Channels}. + * + * @author Pawel Pieczul - Initial contribution + */ +public class LxServerHandler extends BaseThingHandler implements LxServerHandlerApi { + + private static final String SOCKET_URL = "/ws/rfc6455"; + private static final String CMD_CFG_API = "jdev/cfg/api"; + + private static final Gson GSON; + + private InetAddress host; + private int port; + + private int firstConDelay = 1; + private int connectErrDelay = 10; + private int userErrorDelay = 60; + private int comErrorDelay = 30; + private long keepAlivePeriod = 240; // 4 minutes, server timeout is 5 minutes + + // initial delay to initiate connection + private int reconnectDelay = firstConDelay; + + // Map of state UUID to a map of control UUID and state objects + // State with a unique UUID can be configured in many controls and each control can even have a different name of + // the state. It must be ensured that updates received for this state UUID are passed to all controls that have this + // state UUID configured. + private Map> states = new HashMap<>(); + + private LxWebSocket socket; + private WebSocketClient wsClient; + + private int debugId = 0; + private Thread monitorThread; + private final Lock threadLock = new ReentrantLock(); + private final Condition connectDelay = threadLock.newCondition(); + private final Condition sessionActive = threadLock.newCondition(); + + // Data structures + private final Map controls = new HashMap<>(); + private final Map channels = new HashMap<>(); + + private LxDynamicStateDescriptionProvider dynamicStateDescriptionProvider; + private final Logger logger = LoggerFactory.getLogger(LxServerHandler.class); + private static AtomicInteger staticDebugId = new AtomicInteger(1); + + static final Set SUPPORTED_THING_TYPES_UIDS = Collections + .singleton(LxBindingConstants.THING_TYPE_MINISERVER); + + private QueuedThreadPool jettyThreadPool; + + static { + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(LxUuid.class, LxUuid.DESERIALIZER); + builder.registerTypeAdapter(LxControl.class, LxControl.DESERIALIZER); + GSON = builder.create(); + } + + /** + * Create {@link LxServerHandler} object + * + * @param thing Thing object that creates the handler + * @param provider state description provider service + */ + public LxServerHandler(Thing thing, LxDynamicStateDescriptionProvider provider) { + super(thing); + if (provider != null) { + dynamicStateDescriptionProvider = provider; + } else { + logger.warn("Dynamic state description provider is null"); + } + } + + /* + * Methods from BaseThingHandler + */ + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + updateChannelState(channelUID); + return; + } + try { + LxControl control = channels.get(channelUID); + if (control != null) { + control.handleCommand(channelUID, command); + } else { + logger.error("[{}] Received command {} for unknown control.", debugId, command); + } + } catch (IOException e) { + setOffline(LxErrorCode.COMMUNICATION_ERROR, e.getMessage()); + } + } + + @Override + public void channelLinked(ChannelUID channelUID) { + logger.debug("[{}] Channel linked: {}", debugId, channelUID.getAsString()); + updateChannelState(channelUID); + } + + @Override + public void initialize() { + debugId = staticDebugId.getAndIncrement(); + + logger.trace("[{}] Initializing thing instance", debugId); + LxBindingConfiguration cfg = getConfig().as(LxBindingConfiguration.class); + try { + this.host = InetAddress.getByName(cfg.host); + } catch (UnknownHostException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Unknown host"); + return; + } + this.port = cfg.port; + if (cfg.keepAlivePeriod > 0 && cfg.keepAlivePeriod != keepAlivePeriod) { + logger.debug("[{}] Changing keepAlivePeriod to {}", debugId, cfg.keepAlivePeriod); + keepAlivePeriod = cfg.keepAlivePeriod; + } + if (cfg.firstConDelay >= 0 && firstConDelay != cfg.firstConDelay) { + logger.debug("[{}] Changing firstConDelay to {}", debugId, cfg.firstConDelay); + firstConDelay = cfg.firstConDelay; + } + if (cfg.connectErrDelay >= 0 && connectErrDelay != cfg.connectErrDelay) { + logger.debug("[{}] Changing connectErrDelay to {}", debugId, cfg.connectErrDelay); + connectErrDelay = cfg.connectErrDelay; + } + if (cfg.userErrorDelay >= 0 && userErrorDelay != cfg.userErrorDelay) { + logger.debug("[{}] Changing userErrorDelay to {}", debugId, cfg.userErrorDelay); + userErrorDelay = cfg.userErrorDelay; + } + if (cfg.comErrorDelay >= 0 && comErrorDelay != cfg.comErrorDelay) { + logger.debug("[{}] Changing comErrorDelay to {}", debugId, cfg.comErrorDelay); + comErrorDelay = cfg.comErrorDelay; + } + + jettyThreadPool = new QueuedThreadPool(); + jettyThreadPool.setName(LxServerHandler.class.getSimpleName() + "-" + debugId); + jettyThreadPool.setDaemon(true); + + socket = new LxWebSocket(debugId, this, cfg, host); + wsClient = new WebSocketClient(); + wsClient.setExecutor(jettyThreadPool); + + threadLock.lock(); + if (debugId > 1) { + reconnectDelay = 0; + } + try { + if (monitorThread == null) { + monitorThread = new LxServerThread(debugId); + monitorThread.start(); + } + } finally { + threadLock.unlock(); + } + } + + @Override + public void dispose() { + logger.debug("[{}] Disposing of thing", debugId); + Thread thread; + threadLock.lock(); + try { + thread = monitorThread; + if (monitorThread != null) { + monitorThread.interrupt(); + monitorThread = null; + } + clearConfiguration(); + } finally { + threadLock.unlock(); + } + if (thread != null) { + try { + thread.join(5000); + } catch (InterruptedException e) { + logger.warn("[{}] Waiting for thread termination interrupted.", debugId); + } + } + } + + /* + * Public methods that are called by {@link LxControl} child classes + */ + + /* + * (non-Javadoc) + * + * @see org.openhab.binding.loxone.internal.LxServerHandlerApi#sendAction(org.openhab.binding.loxone.internal.types. + * LxUuid, java.lang.String) + */ + @Override + public void sendAction(LxUuid id, String operation) throws IOException { + socket.sendAction(id, operation); + } + + /* + * (non-Javadoc) + * + * @see + * org.openhab.binding.loxone.internal.LxServerHandlerApi#addControl(org.openhab.binding.loxone.internal.controls. + * LxControl) + */ + @Override + public void addControl(LxControl control) { + addControlStructures(control); + addThingChannels(control.getChannelsWithSubcontrols(), false); + } + + /* + * (non-Javadoc) + * + * @see + * org.openhab.binding.loxone.internal.LxServerHandlerApi#removeControl(org.openhab.binding.loxone.internal.controls + * .LxControl) + */ + @Override + public void removeControl(LxControl control) { + logger.debug("[{}] Removing control: {}", debugId, control.getName()); + control.getSubControls().values().forEach(subControl -> removeControl(subControl)); + LxUuid controlUuid = control.getUuid(); + control.getStates().values().forEach(state -> { + LxUuid stateUuid = state.getUuid(); + Map perUuid = states.get(stateUuid); + if (perUuid != null) { + perUuid.remove(controlUuid); + if (perUuid.isEmpty()) { + states.remove(stateUuid); + } + } + }); + + ThingBuilder builder = editThing(); + control.getChannels().forEach(channel -> { + ChannelUID id = channel.getUID(); + builder.withoutChannel(id); + dynamicStateDescriptionProvider.removeDescription(id); + channels.remove(id); + }); + updateThing(builder.build()); + controls.remove(controlUuid); + } + + /* + * (non-Javadoc) + * + * @see org.openhab.binding.loxone.internal.LxServerHandlerApi#setChannelState(org.eclipse.smarthome.core.thing. + * ChannelUID, org.eclipse.smarthome.core.types.State) + */ + @Override + public void setChannelState(ChannelUID channelId, State state) { + updateState(channelId, state); + } + + /* + * (non-Javadoc) + * + * @see + * org.openhab.binding.loxone.internal.LxServerHandlerApi#setChannelStateDescription(org.eclipse.smarthome.core. + * thing.ChannelUID, org.eclipse.smarthome.core.types.StateDescription) + */ + @Override + public void setChannelStateDescription(ChannelUID channelId, StateDescription description) { + logger.debug("[{}] State description update for channel {}", debugId, channelId); + dynamicStateDescriptionProvider.setDescription(channelId, description); + } + + /* + * Public methods called by {@link LxWsSecurity} child classes. + */ + + /* + * (non-Javadoc) + * + * @see org.openhab.binding.loxone.internal.LxServerHandlerApi#getSetting(java.lang.String) + */ + @Override + public String getSetting(String name) { + Object value = getConfig().get(name); + return (value instanceof String) ? (String) value : null; + } + + /* + * (non-Javadoc) + * + * @see org.openhab.binding.loxone.internal.LxServerHandlerApi#setSettings(java.util.Map) + */ + @Override + public void setSettings(Map properties) { + Configuration config = getConfig(); + properties.forEach((name, value) -> config.put(name, value)); + updateConfiguration(config); + } + + /* + * (non-Javadoc) + * + * @see org.openhab.binding.loxone.internal.LxServerHandlerApi#getGson() + */ + @Override + public Gson getGson() { + return GSON; + } + + /* + * (non-Javadoc) + * + * @see org.openhab.binding.loxone.internal.LxServerHandlerApi#getThingId() + */ + @Override + public ThingUID getThingId() { + return getThing().getUID(); + } + + /* + * Methods called by {@link LxWebSocket} class. + */ + + /** + * Dispose of all objects created from the Miniserver configuration. + */ + void clearConfiguration() { + controls.clear(); + channels.clear(); + states.clear(); + dynamicStateDescriptionProvider.removeAllDescriptions(); + } + + /** + * Sets a new configuration received from the Miniserver and creates all required channels. + * + * @param config Miniserver's configuration + */ + void setMiniserverConfig(LxConfig config) { + logger.debug("[{}] Setting configuration from Miniserver", debugId); + + if (config.msInfo == null) { + logger.warn("[{}] missing global configuration msInfo on Loxone", debugId); + config.msInfo = config.new LxServerInfo(); + } + Thing thing = getThing(); + LxServerInfo info = config.msInfo; + thing.setProperty(LxBindingConstants.MINISERVER_PROPERTY_MINISERVER_NAME, buildName(info.msName)); + thing.setProperty(LxBindingConstants.MINISERVER_PROPERTY_PROJECT_NAME, buildName(info.projectName)); + thing.setProperty(LxBindingConstants.MINISERVER_PROPERTY_CLOUD_ADDRESS, buildName(info.remoteUrl)); + thing.setProperty(LxBindingConstants.MINISERVER_PROPERTY_PHYSICAL_LOCATION, buildName(info.location)); + thing.setProperty(Thing.PROPERTY_FIRMWARE_VERSION, buildName(info.swVersion)); + thing.setProperty(Thing.PROPERTY_SERIAL_NUMBER, buildName(info.serialNr)); + thing.setProperty(Thing.PROPERTY_MAC_ADDRESS, buildName(info.macAddress)); + + List list = new ArrayList<>(); + if (config.controls != null) { + logger.trace("[{}] creating control structures.", debugId); + config.controls.values().forEach(ctrl -> { + addControlStructures(ctrl); + list.addAll(ctrl.getChannelsWithSubcontrols()); + }); + } else { + logger.warn("[{}] no controls received in Miniserver configuration.", debugId); + } + addThingChannels(list, true); + updateStatus(ThingStatus.ONLINE); + } + + /** + * Update to the new value of a state received from Miniserver. This method will go through all instances of this + * state UUID and update their value, which will trigger corresponding control state update method in each control + * that has this state. + * + * @param uuid Miniserver's state UUID + * @param value a new value for this state + */ + void updateStateValue(LxUuid uuid, Object value) { + Map perStateUuid = states.get(uuid); + if (perStateUuid != null) { + perStateUuid.forEach((controlUuid, state) -> { + state.setStateValue(value); + }); + } + } + + /** + * Set thing status to offline and start attempts to establish a new connection to the Miniserver after a delay + * depending of the reason for going offline. + * + * @param code error code + * @param reason reason for going offline + */ + void setOffline(LxErrorCode code, String reason) { + switch (code) { + case TOO_MANY_FAILED_LOGIN_ATTEMPTS: + // assume credentials are wrong, do not re-attempt connections any time soon + // expect a new instance will have to be initialized with corrected configuration + setReconnectDelay(60 * 60 * 24 * 7); + updateStatusToOffline(ThingStatusDetail.CONFIGURATION_ERROR, + "Too many failed login attempts - stopped trying"); + break; + case USER_UNAUTHORIZED: + setReconnectDelay(userErrorDelay); + updateStatusToOffline(ThingStatusDetail.CONFIGURATION_ERROR, + reason != null ? reason : "User authentication error (invalid user name or password)"); + break; + case USER_AUTHENTICATION_TIMEOUT: + updateStatusToOffline(ThingStatusDetail.COMMUNICATION_ERROR, "User authentication timeout"); + break; + case COMMUNICATION_ERROR: + setReconnectDelay(comErrorDelay); + String text = "Error communicating with Miniserver"; + if (reason != null) { + text += " (" + reason + ")"; + } + updateStatusToOffline(ThingStatusDetail.COMMUNICATION_ERROR, text); + break; + case INTERNAL_ERROR: + updateStatusToOffline(ThingStatusDetail.CONFIGURATION_ERROR, + reason != null ? "Internal error (" + reason + ")" : "Internal error"); + break; + case WEBSOCKET_IDLE_TIMEOUT: + logger.warn("Idle timeout from Loxone Miniserver - adjust keepalive settings"); + updateStatusToOffline(ThingStatusDetail.COMMUNICATION_ERROR, "Timeout due to no activity"); + break; + case ERROR_CODE_MISSING: + logger.warn("No error code available from the Miniserver"); + updateStatusToOffline(ThingStatusDetail.COMMUNICATION_ERROR, "Unknown reason - error code missing"); + break; + default: + updateStatusToOffline(ThingStatusDetail.CONFIGURATION_ERROR, "Unknown reason"); + break; + } + threadLock.lock(); + try { + sessionActive.signalAll(); + } finally { + threadLock.unlock(); + } + + } + + /** + * Add a new control, its states, subcontrols and channels to the handler structures. + * Handler maintains maps of all controls (main controls + subcontrols), all channels for all controls and all + * states to match received openHAB commands and state updates from the Miniserver. States also contain links to + * possibly multiple control objects, as many controls can share the same state with the same state uuid. + * To create channels, {@link LxServerHandler#addThingChannels} method should be called separately. This allows + * creation of all channels for all controls with a single thing update. + * + * @param control a created control object to be added + */ + private void addControlStructures(LxControl control) { + LxUuid uuid = control.getUuid(); + logger.debug("[{}] Adding control to handler: {}, {}", debugId, uuid, control.getName()); + control.getStates().values().forEach(state -> { + Map perUuid = states.get(state.getUuid()); + if (perUuid == null) { + perUuid = new HashMap<>(); + states.put(state.getUuid(), perUuid); + } + perUuid.put(uuid, state); + }); + controls.put(control.getUuid(), control); + control.getChannels().forEach(channel -> channels.put(channel.getUID(), control)); + control.getSubControls().values().forEach(subControl -> addControlStructures(subControl)); + } + + /** + * Adds channels to the thing, to make them available to the framework and user. + * This method will sort the channels according to their label. + * It is expected that input list contains no duplicate channel IDs. + * + * @param newChannels a list of channels to add to the thing + * @param purge if true, old channels will be removed, otherwise merged + */ + private void addThingChannels(List newChannels, boolean purge) { + List channels = newChannels; + if (!purge) { + channels.addAll(getThing().getChannels()); + } + channels.sort((c1, c2) -> { + String label = c1.getLabel(); + return label == null ? 1 : label.compareTo(c2.getLabel()); + }); + ThingBuilder builder = editThing(); + builder.withChannels(channels); + updateThing(builder.build()); + } + + /** + * Connect the websocket. + * Attempts to connect to the websocket on a remote Miniserver. If a connection is established, a + * {@link LxWebSocket#onConnect} method will be called from a parallel websocket thread. + * + * @return true if connection request initiated correctly, false if not + */ + private boolean connect() { + logger.debug("[{}] connect() websocket", debugId); + /* + * Try to read CfgApi structure from the miniserver. It contains serial number and firmware version. If it can't + * be read this is not a fatal issue, we will assume most recent version running. + */ + String message = socket.httpGet(CMD_CFG_API); + if (message != null) { + LxResponse resp = socket.getResponse(message); + if (resp != null) { + socket.setFwVersion(GSON.fromJson(resp.getValueAsString(), LxResponse.LxResponseCfgApi.class).version); + } + } else { + logger.debug("[{}] Http get failed for API config request.", debugId); + } + + try { + wsClient.start(); + // Following the PR github.com/eclipse/smarthome/pull/6636 + // without this zero timeout, jetty will wait 30 seconds for stopping the client to eventually fail + // with the timeout it is immediate and all threads end correctly + jettyThreadPool.setStopTimeout(0); + URI target = new URI("ws://" + host.getHostAddress() + ":" + port + SOCKET_URL); + ClientUpgradeRequest request = new ClientUpgradeRequest(); + request.setSubProtocols("remotecontrol"); + socket.startResponseTimeout(); + logger.debug("[{}] Connecting to server : {} ", debugId, target); + wsClient.connect(socket, target, request); + return true; + } catch (Exception e) { + logger.debug("[{}] Error starting websocket client: {}", debugId, e.getMessage()); + try { + wsClient.stop(); + } catch (Exception e2) { + logger.debug("[{}] Error stopping websocket client: {}", debugId, e2.getMessage()); + } + return false; + } + } + + /* + * Private methods + */ + + /** + * Sets value for the delay before websocket connect attempt + * + * @param delay number of seconds to wait + */ + private void setReconnectDelay(int delay) { + threadLock.lock(); + try { + reconnectDelay = delay; + } finally { + threadLock.unlock(); + } + } + + /** + * Disconnect websocket session - initiated from this end. + * + * @param code error code for disconnecting the websocket + * @param reason reason for disconnecting the websocket + */ + private void disconnect(LxErrorCode code, String reason) { + logger.debug("[{}] disconnect the websocket: {}, {}", debugId, code, reason); + socket.disconnect(code, reason); + try { + logger.debug("[{}] client stop", debugId); + wsClient.stop(); + logger.debug("[{}] client stopped", debugId); + } catch (Exception e) { + logger.debug("[{}] Exception disconnecting the websocket: ", e.getMessage()); + } + } + + /** + * Thread that maintains connection to the Miniserver. + * It will periodically attempt to connect and if failed, wait a configured amount of time. + * If connection succeeds, it will sleep until the session is terminated. Then it will wait and try to reconnect + * again. + * + * @author Pawel Pieczul - initial contribution + * + */ + private class LxServerThread extends Thread { + private int debugId = 0; + + LxServerThread(int id) { + debugId = id; + } + + @Override + public void run() { + logger.debug("[{}] Thread starting", debugId); + threadLock.lock(); + try { + while (!isInterrupted()) { + try { + if (reconnectDelay > 0) { + logger.debug("[{}] Delaying connect request by {} seconds.", debugId, reconnectDelay); + connectDelay.await(reconnectDelay, TimeUnit.SECONDS); + } + } catch (InterruptedException e) { + break; + } + logger.debug("[{}] Server connecting to websocket", debugId); + if (!connect()) { + updateStatusToOffline(ThingStatusDetail.COMMUNICATION_ERROR, + "Failed to connect to Miniserver's WebSocket"); + reconnectDelay = connectErrDelay; + } else { + try { + logger.debug("[{}] Sleeping for {} seconds.", debugId, keepAlivePeriod); + while (!sessionActive.await(keepAlivePeriod, TimeUnit.SECONDS)) { + socket.sendKeepAlive(); + } + } catch (InterruptedException e) { + break; + } + } + } + logger.debug("[{}] Stopping reconnect attempts permanently", debugId); + disconnect(LxErrorCode.OK, "Thing is going down."); + } finally { + threadLock.unlock(); + } + logger.debug("[{}] Thread ending", debugId); + } + } + + /** + * Updates the thing status to offline, if it is not already offline. This will preserve he first reason of going + * offline in case there were multiple reasons. + * + * @param code error code + * @param reason reason for going offline + */ + private void updateStatusToOffline(ThingStatusDetail code, String reason) { + ThingStatus status = getThing().getStatus(); + if (status == ThingStatus.OFFLINE) { + logger.debug("[{}] received offline request with code {}, but thing already offline.", debugId, code); + } else { + updateStatus(ThingStatus.OFFLINE, code, reason); + } + } + + /** + * Updates an actual state of a channel. + * Determines control for the channel and retrieves the state from the control. + * + * @param channelId channel ID to update its state + */ + private void updateChannelState(ChannelUID channelId) { + LxControl control = channels.get(channelId); + if (control != null) { + State state = control.getChannelState(channelId); + if (state != null) { + updateState(channelId, state); + } + } else { + logger.error("[{}] Received state update request for unknown control (channelId={}).", debugId, channelId); + } + } + + /** + * Check and convert null string to empty string. + * + * @param name string to check + * @return string guaranteed to be not null + */ + private String buildName(String name) { + if (name == null) { + return ""; + } + return name; + } + +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxServerHandlerApi.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxServerHandlerApi.java new file mode 100644 index 0000000000000..9730b25edaf69 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxServerHandlerApi.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal; + +import java.io.IOException; +import java.nio.channels.Channels; +import java.util.Map; + +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.StateDescription; +import org.openhab.binding.loxone.internal.controls.LxControl; +import org.openhab.binding.loxone.internal.security.LxWsSecurity; +import org.openhab.binding.loxone.internal.types.LxUuid; + +import com.google.gson.Gson; + +/** + * Representation of a Loxone Miniserver. It is an openHAB {@link Thing}, which is used to communicate with + * objects (controls) configured in the Miniserver over {@link Channels}. + * + * @author Pawel Pieczul - Initial contribution + */ +public interface LxServerHandlerApi { + + /** + * Sends an action to a Loxone Miniserver's control. + * + * @param id identifier of the control + * @param operation identifier of the operation + * @throws IOException when communication error with Miniserver occurs + */ + void sendAction(LxUuid id, String operation) throws IOException; + + /** + * Add a control - creates internal data structures and channels in the framework. + * This method should be used for all dynamically created controls, usually as a result of Miniserver's state update + * messages, after the static configuration is setup. + * + * @param control control to be added + */ + void addControl(LxControl control); + + /** + * Remove a control - removes internal data structures and channels from the framework + * This method should be used for all dynamically created controls, usually as a result of Miniserver's state update + * messages, after the static configuration is setup. + * + * @param control control to remove + */ + void removeControl(LxControl control); + + /** + * Sets channel's state to a new value + * + * @param channelId channel ID + * @param state new state value + */ + void setChannelState(ChannelUID channelId, State state); + + /** + * Sets a new channel state description. This method is called to dynamically change the way the channel state is + * interpreted and displayed. It is called when a dynamic state update is received from the Miniserver with a new + * way of displaying control's state. + * + * @param channelId channel ID + * @param description a new state description + */ + void setChannelStateDescription(ChannelUID channelId, StateDescription description); + + /** + * Get configuration parameter from the thing configuration. This method is called by the {@link LxWsSecurity} class + * to dynamically retrieve previously stored login token and its parameters. + * + * @param name parameter name + * @return parameter value + */ + String getSetting(String name); + + /** + * Set configuration parameters in the thing configuration. This method is called by the {@link LxWsSecurity} class + * to dynamically stored login token and its parameters received from the Miniserver. + * + * @param properties pairs of parameter names and values + */ + void setSettings(Map properties); + + /** + * Get GSON object for reuse + * + * @return GSON object + */ + Gson getGson(); + + /** + * Get ID of the Miniserver's Thing + * + * @return ID of the Thing + */ + ThingUID getThingId(); +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxWebSocket.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxWebSocket.java new file mode 100644 index 0000000000000..07102460f780a --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/LxWebSocket.java @@ -0,0 +1,641 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.StatusCode; +import org.eclipse.jetty.websocket.api.WebSocketPolicy; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; +import org.eclipse.smarthome.core.common.ThreadPoolManager; +import org.eclipse.smarthome.core.util.HexUtils; +import org.openhab.binding.loxone.internal.security.LxWsSecurity; +import org.openhab.binding.loxone.internal.types.LxConfig; +import org.openhab.binding.loxone.internal.types.LxErrorCode; +import org.openhab.binding.loxone.internal.types.LxResponse; +import org.openhab.binding.loxone.internal.types.LxUuid; +import org.openhab.binding.loxone.internal.types.LxWsBinaryHeader; +import org.openhab.binding.loxone.internal.types.LxWsSecurityType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonParseException; + +/** + * Implementation of jetty websocket client + * + * @author Pawel Pieczul - initial contribution + * + */ +@WebSocket +public class LxWebSocket { + private static final String CMD_ACTION = "jdev/sps/io/"; + private static final String CMD_KEEPALIVE = "keepalive"; + private static final String CMD_ENABLE_UPDATES = "jdev/sps/enablebinstatusupdate"; + private static final String CMD_GET_APP_CONFIG = "data/LoxAPP3.json"; + + private final int debugId; + private final Gson gson; + private final LxServerHandler thingHandler; + + private long responseTimeout = 4; // 4 seconds to wait for Miniserver response + private int maxBinMsgSize = 3 * 1024; // 3 MB + private int maxTextMsgSize = 512; // 512 KB + private final LxWsSecurityType securityType; + private final InetAddress host; + private final int port; + private final String user; + private final String password; + + private Session session; + private String fwVersion; + private ScheduledFuture timeout; + private LxWsBinaryHeader header; + private LxWsSecurity security; + private boolean awaitingConfiguration = false; + private final Lock webSocketLock = new ReentrantLock(); + private final Lock responseLock = new ReentrantLock(); + private final Condition responseAvailable = responseLock.newCondition(); + private String awaitingCommand; + private LxResponse awaitedResponse; + private boolean syncRequest; + + private LxErrorCode offlineCode; + private String offlineReason; + + private static final ScheduledExecutorService SCHEDULER = ThreadPoolManager + .getScheduledPool(LxWebSocket.class.getSimpleName()); + private final Logger logger = LoggerFactory.getLogger(LxWebSocket.class); + + /** + * Create websocket object. + * + * @param debugId instance of the client used for debugging purposes only + * @param thingHandler API to the thing handler + * @param cfg binding configuration + * @param host IP address of the Miniserver + */ + LxWebSocket(int debugId, LxServerHandler thingHandler, LxBindingConfiguration cfg, InetAddress host) { + this.debugId = debugId; + this.thingHandler = thingHandler; + this.host = host; + this.port = cfg.port; + this.user = cfg.user; + this.password = cfg.password; + this.gson = thingHandler.getGson(); + + securityType = LxWsSecurityType.getType(cfg.authMethod); + if (cfg.responseTimeout > 0 && cfg.responseTimeout != responseTimeout) { + logger.debug("[{}] Changing responseTimeout to {}", debugId, cfg.responseTimeout); + responseTimeout = cfg.responseTimeout; + } + if (cfg.maxBinMsgSize > 0 && cfg.maxBinMsgSize != maxBinMsgSize) { + logger.debug("[{}] Changing maxBinMsgSize to {}", debugId, cfg.maxBinMsgSize); + maxBinMsgSize = cfg.maxBinMsgSize; + } + if (cfg.maxTextMsgSize > 0 && cfg.maxTextMsgSize != maxTextMsgSize) { + logger.debug("[{}] Changing maxTextMsgSize to {}", debugId, cfg.maxTextMsgSize); + maxTextMsgSize = cfg.maxTextMsgSize; + } + } + + /* + * Jetty websocket methods + */ + + @OnWebSocketConnect + public void onConnect(Session session) { + webSocketLock.lock(); + try { + offlineCode = null; + offlineReason = null; + WebSocketPolicy policy = session.getPolicy(); + policy.setMaxBinaryMessageSize(maxBinMsgSize * 1024); + policy.setMaxTextMessageSize(maxTextMsgSize * 1024); + + logger.debug("[{}] Websocket connected (maxBinMsgSize={}, maxTextMsgSize={})", debugId, + policy.getMaxBinaryMessageSize(), policy.getMaxTextMessageSize()); + this.session = session; + + security = LxWsSecurity.create(securityType, fwVersion, debugId, thingHandler, this, user, password); + security.authenticate((result, details) -> { + if (result == LxErrorCode.OK) { + authenticated(); + } else { + disconnect(result, details); + } + }); + } finally { + webSocketLock.unlock(); + } + } + + @OnWebSocketClose + public void onClose(int statusCode, String reason) { + String reasonToPass; + LxErrorCode codeToPass; + webSocketLock.lock(); + try { + logger.debug("[{}] Websocket connection closed with code {} reason : {}", debugId, statusCode, reason); + if (security != null) { + security.cancel(); + } + session = null; + // This callback is called when connection is terminated by either end. + // If there is already a reason for disconnection, pass it unchanged. + // Otherwise try to interpret the remote end reason. + if (offlineCode != null) { + codeToPass = offlineCode; + reasonToPass = offlineReason; + } else { + codeToPass = LxErrorCode.getErrorCode(statusCode); + reasonToPass = reason; + } + } finally { + webSocketLock.unlock(); + } + + // Release any requester waiting for message response + responseLock.lock(); + try { + if (awaitedResponse != null) { + awaitedResponse.subResponse = null; + } + responseAvailable.signalAll(); + } finally { + responseLock.unlock(); + } + thingHandler.setOffline(codeToPass, reasonToPass); + } + + @OnWebSocketError + public void onError(Throwable error) { + logger.debug("[{}] Websocket error : {}", debugId, error.getMessage()); + // We do nothing. This callback may be called at various connection stages and indicates something wrong + // with the connection mostly on the protocol level. It will be caught by other activities - connection will + // be closed of timeouts will detect its inactivity. + } + + @OnWebSocketMessage + public void onBinaryMessage(byte data[], int msgOffset, int msgLength) { + int offset = msgOffset; + int length = msgLength; + if (logger.isTraceEnabled()) { + String s = HexUtils.bytesToHex(data); + logger.trace("[{}] Binary message: length {}: {}", debugId, length, s); + } + webSocketLock.lock(); + try { + // websocket will receive header and data in turns as two separate binary messages + if (header == null) { + // header expected now + header = new LxWsBinaryHeader(data, offset); + switch (header.getType()) { + // following header types precede data in next message + case BINARY_FILE: + case EVENT_TABLE_OF_VALUE_STATES: + case EVENT_TABLE_OF_TEXT_STATES: + case EVENT_TABLE_OF_DAYTIMER_STATES: + case EVENT_TABLE_OF_WEATHER_STATES: + break; + // other header types have no data and next message will be header again + default: + header = null; + break; + } + } else { + // data expected now + switch (header.getType()) { + case EVENT_TABLE_OF_VALUE_STATES: + stopResponseTimeout(); + while (length > 0) { + Double value = ByteBuffer.wrap(data, offset + 16, 8).order(ByteOrder.LITTLE_ENDIAN) + .getDouble(); + thingHandler.updateStateValue(new LxUuid(data, offset), value); + offset += 24; + length -= 24; + } + break; + case EVENT_TABLE_OF_TEXT_STATES: + while (length > 0) { + // unused today at (offset + 16): iconUuid + int textLen = ByteBuffer.wrap(data, offset + 32, 4).order(ByteOrder.LITTLE_ENDIAN).getInt(); + String value = new String(data, offset + 36, textLen); + int size = 36 + (textLen % 4 > 0 ? textLen + 4 - (textLen % 4) : textLen); + thingHandler.updateStateValue(new LxUuid(data, offset), value); + offset += size; + length -= size; + } + break; + case KEEPALIVE_RESPONSE: + case TEXT_MESSAGE: + default: + break; + } + // header will be next + header = null; + } + } catch (IndexOutOfBoundsException e) { + logger.debug("[{}] malformed binary message received, discarded", debugId); + } finally { + webSocketLock.unlock(); + } + } + + @OnWebSocketMessage + public void onMessage(String msg) { + webSocketLock.lock(); + try { + if (logger.isTraceEnabled()) { + String trace = msg; + if (trace.length() > 100) { + trace = msg.substring(0, 100); + } + logger.trace("[{}] received message: {}", debugId, trace); + } + if (!awaitingConfiguration) { + processResponse(msg); + return; + } + awaitingConfiguration = false; + stopResponseTimeout(); + thingHandler.clearConfiguration(); + + LxConfig config = gson.fromJson(msg, LxConfig.class); + config.finalize(thingHandler); + + thingHandler.setMiniserverConfig(config); + + if (sendCmdWithResp(CMD_ENABLE_UPDATES, false, false) == null) { + disconnect(LxErrorCode.COMMUNICATION_ERROR, "Failed to enable state updates."); + } + } finally { + webSocketLock.unlock(); + } + } + + /* + * Public methods, called by {@link LxControl} and {@link LxWsSecurity} child classes + */ + + /** + * Parse received message into a response structure. Check basic correctness of the response. + * + * @param msg received response message + * @return parsed response message + */ + public LxResponse getResponse(String msg) { + try { + LxResponse resp = gson.fromJson(msg, LxResponse.class); + if (!resp.isResponseOk()) { + logger.debug("[{}] Miniserver response is not ok: {}", debugId, msg); + return null; + } + return resp; + } catch (JsonParseException e) { + logger.debug("[{}] Miniserver response JSON parsing error: {}, {}", debugId, msg, e.getMessage()); + return null; + } + } + + /** + * Sends a command to the Miniserver and encrypts it if command can be encrypted and encryption is available. + * Request can be synchronous or asynchronous. There is always a response expected to the command, and it is a + * standard command response as defined in {@link LxResponse}. Such commands are the majority of commands + * used for performing actions on the controls and for executing authentication procedure. + * A synchronous command must not be sent from the websocket thread (from websocket callback methods) or it will + * cause a deadlock. + * An asynchronous command request returns immediately, but the returned value will not contain valid data in + * the subResponse structure until a response is received. Asynchronous request can be sent from the websocket + * thread. There can be only one command sent which awaits response per websocket connection, + * whether this is synchronous or asynchronous command (this seems how Loxone Miniserver behaves, as it does not + * have any unique identifier to match commands to responses). + * For synchronous commands this is ensured naturally, for asynchronous the caller must manage it. + * If this method is called before a response to the previous command is received, it will return error and not + * send the command. + * + * @param command command to send to the Miniserver + * @param sync true is synchronous request, false if ansynchronous + * @param encrypt true if command can be encrypted (does not mean it will) + * @return response received (for sync command) or to be received (for async), null if error occurred + */ + public LxResponse sendCmdWithResp(String command, boolean sync, boolean encrypt) { + responseLock.lock(); + try { + if (awaitedResponse != null || awaitingCommand != null) { + logger.warn("[{}] Command not sent, previous command not finished: {}", debugId, command); + return null; + } + if (!sendCmdNoResp(command, encrypt)) { + return null; + } + LxResponse resp = new LxResponse(); + awaitingCommand = command; + awaitedResponse = resp; + syncRequest = sync; + if (sync) { + if (!responseAvailable.await(responseTimeout, TimeUnit.SECONDS)) { + awaitedResponse = null; + awaitingCommand = null; + responseTimeout(); + return null; + } + awaitedResponse = null; + awaitingCommand = null; + } + return resp; + } catch (InterruptedException e) { + logger.debug("[{}] Interrupted waiting for response: {}", debugId, command); + awaitedResponse = null; + awaitingCommand = null; + return null; + } finally { + responseLock.unlock(); + } + } + + /** + * Send a HTTP GET request and return server's response. + * + * @param request request content + * @return response received + */ + public String httpGet(String request) { + HttpURLConnection con = null; + try { + URL url = new URL("http", host.getHostAddress(), port, request.startsWith("/") ? request : "/" + request); + con = (HttpURLConnection) url.openConnection(); + con.setRequestMethod("GET"); + StringBuilder result = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()))) { + String l; + while ((l = reader.readLine()) != null) { + result.append(l); + } + return result.toString(); + } + } catch (IOException e) { + return null; + } finally { + if (con != null) { + con.disconnect(); + } + } + } + + /* + * Methods used by {@link LxServerHandler} + */ + + /** + * Sends an action to a Loxone Miniserver's control. + * + * @param id identifier of the control + * @param operation identifier of the operation + * @throws IOException when communication error with Miniserver occurs + */ + void sendAction(LxUuid id, String operation) throws IOException { + String command = CMD_ACTION + id.getOriginalString() + "/" + operation; + logger.debug("[{}] Sending command {}", debugId, command); + LxResponse response = sendCmdWithResp(command, true, true); + if (response == null) { + throw new IOException("Error sending command " + command); + } + if (!response.isResponseOk()) { + if (response.getResponseCode() == LxErrorCode.USER_UNAUTHORIZED) { + // we don't support per-control passwords, because the controls should have been filtered to remove + // secured ones, it is an unexpected situation to receive this error code, but generally we can continue + // operation + logger.warn("[{}] User not authorised to operate on control {}", debugId, id); + } else { + throw new IOException("Received response is not ok to command " + command); + } + } + } + + /** + * Send keep-alive message to the Miniserver + */ + void sendKeepAlive() { + sendCmdNoResp(CMD_KEEPALIVE, false); + } + + /** + * Sets Miniserver firmware version, if known. + * + * @param fwVersion Miniserver firmware version + */ + void setFwVersion(String fwVersion) { + this.fwVersion = fwVersion; + } + + /** + * Start a timer to wait for a Miniserver response to an action sent from the binding. + * When timer expires, connection is removed and server error is reported. Further connection attempt can be made + * later by the upper layer. + * If a previous timer is running, it will be stopped before a new timer is started. + * The caller must take care of thread synchronization. + */ + void startResponseTimeout() { + webSocketLock.lock(); + try { + stopResponseTimeout(); + timeout = SCHEDULER.schedule(this::responseTimeout, responseTimeout, TimeUnit.SECONDS); + } finally { + webSocketLock.unlock(); + } + } + + /** + * Disconnect websocket session - initiated from this end. + * + * @param code error code for disconnecting the websocket + * @param reason reason for disconnecting the websocket + */ + void disconnect(LxErrorCode code, String reason) { + logger.trace("[{}] disconnect the websocket: {}, {}", debugId, code, reason); + // in case the disconnection happens from both connection ends, store and pass only the first reason + if (offlineCode == null) { + offlineCode = code; + offlineReason = reason; + } + stopResponseTimeout(); + if (session != null) { + logger.debug("[{}] Closing session", debugId); + session.close(StatusCode.NORMAL, reason); + logger.debug("[{}] Session closed", debugId); + } else { + logger.debug("[{}] Disconnecting websocket, but no session, reason : {}", debugId, reason); + thingHandler.setOffline(LxErrorCode.COMMUNICATION_ERROR, reason); + } + } + + /* + * Private methods + */ + + /** + * Stops scheduled timeout waiting for a Miniserver response + * The caller must take care of thread synchronization. + */ + private void stopResponseTimeout() { + webSocketLock.lock(); + try { + logger.trace("[{}] stopping response timeout", debugId); + if (timeout != null) { + timeout.cancel(true); + timeout = null; + } + } finally { + webSocketLock.unlock(); + } + } + + /** + * Sends a command to the Miniserver and encrypts it if command can be encrypted and encryption is available. + * The request is asynchronous and no response is expected (but it can arrive). It can be used to send commands + * from the websocket thread or commands for which the responses are not following the standard format defined + * in {@link LxResponse}. + * If the caller expects the non-standard response it should manage its reception and the response timeout. + * + * @param command command to send to the Miniserver + * @param encrypt true if command can be encrypted (does not mean it will) + * @return true if command was sent (no information if it was received by the remote end) + */ + private boolean sendCmdNoResp(String command, boolean encrypt) { + webSocketLock.lock(); + try { + if (session != null) { + String encrypted; + if (encrypt) { + encrypted = security.encrypt(command); + logger.debug("[{}] Sending encrypted string: {}", debugId, command); + logger.debug("[{}] Encrypted: {}", debugId, encrypted); + } else { + logger.debug("[{}] Sending unencrypted string: {}", debugId, command); + encrypted = command; + } + try { + session.getRemote().sendString(encrypted); + return true; + } catch (IOException e) { + logger.debug("[{}] Error sending command: {}, {}", debugId, command, e.getMessage()); + return false; + } + } else { + logger.debug("[{}] NOT sending command: {}", debugId, command); + return false; + } + } finally { + webSocketLock.unlock(); + } + } + + /** + * Process a Miniserver's response to a command. The response is in plain text format as received from the + * websocket, but is expected to follow the standard format defined in {@link LxResponse}. + * If there is a thread waiting for the response (on a synchronous command request), the thread will be + * released. Otherwise the response will be copied into the response object provided to the asynchronous + * requester when the command was sent. + * Only one requester is expected to wait for the response at a time - commands must be sent sequentially - a + * command can be sent only after a response to the previous command was received, whether it was sent + * synchronously or asynchronously. + * If the received message is encrypted, it will be decrypted before processing. + * + * @param message websocket message with the response + */ + private void processResponse(String message) { + LxResponse resp = getResponse(message); + if (resp == null) { + return; + } + logger.debug("[{}] Response: {}", debugId, message.trim()); + String control = resp.getCommand().trim(); + control = security.decryptControl(control); + // for some reason the responses to some commands starting with jdev begin with dev, not jdev + // this seems to be a bug in the Miniserver + if (control.startsWith("dev/")) { + control = "j" + control; + } + responseLock.lock(); + try { + if (awaitedResponse == null || awaitingCommand == null) { + logger.warn("[{}] Received response, but awaiting none.", debugId); + return; + } + if (!awaitingCommand.equals(control)) { + logger.warn("[{}] Waiting for another response: {}", debugId, awaitingCommand); + } + awaitedResponse.subResponse = resp.subResponse; + if (syncRequest) { + logger.debug("[{}] Releasing command sender with response: {}, {}", debugId, control, + resp.getResponseCodeNumber()); + responseAvailable.signal(); + } else { + logger.debug("[{}] Reponse to asynchronous request: {}, {}", debugId, control, + resp.getResponseCodeNumber()); + awaitedResponse = null; + awaitingCommand = null; + } + } finally { + responseLock.unlock(); + } + } + + /** + * Perform actions after user authentication is successfully completed. + * This method sends a request to receive Miniserver configuration. + */ + private void authenticated() { + logger.debug("[{}] Websocket authentication successfull.", debugId); + webSocketLock.lock(); + try { + awaitingConfiguration = true; + if (sendCmdNoResp(CMD_GET_APP_CONFIG, false)) { + startResponseTimeout(); + // startKeepAlive(); + } else { + disconnect(LxErrorCode.INTERNAL_ERROR, "Error sending get config command."); + } + } finally { + webSocketLock.unlock(); + } + } + + /** + * Called when response timeout occurred. + */ + private void responseTimeout() { + logger.debug("[{}] Miniserver response timeout", debugId); + disconnect(LxErrorCode.COMMUNICATION_ERROR, "Miniserver response timeout occured"); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControl.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControl.java new file mode 100644 index 0000000000000..6a3cdb1a21270 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControl.java @@ -0,0 +1,733 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.StateDescription; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.loxone.internal.LxServerHandlerApi; +import org.openhab.binding.loxone.internal.types.LxCategory; +import org.openhab.binding.loxone.internal.types.LxConfig; +import org.openhab.binding.loxone.internal.types.LxContainer; +import org.openhab.binding.loxone.internal.types.LxState; +import org.openhab.binding.loxone.internal.types.LxUuid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; + +/** + * A control of Loxone Miniserver. + *

+ * It represents a control object on the Miniserver. Controls can represent an input, functional block or an output of + * the Miniserver, that is marked as visible in the Loxone UI. Controls can belong to a {@link LxContainer} room and a + * {@link LxCategory} category. + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControl { + + /** + * This class contains static configuration of the control and is used to make the fields transparent to the child + * classes that implement specific controls. + * + * @author Pawel Pieczul - initial contribution + * + */ + public static class LxControlConfig { + private final LxServerHandlerApi thingHandler; + private final LxContainer room; + private final LxCategory category; + + LxControlConfig(LxControlConfig config) { + this(config.thingHandler, config.room, config.category); + } + + public LxControlConfig(LxServerHandlerApi thingHandler, LxContainer room, LxCategory category) { + this.room = room; + this.category = category; + this.thingHandler = thingHandler; + } + } + + /** + * This class is used to instantiate a particular control object by the {@link LxControlFactory} + * + * @author Pawel Pieczul - initial contribution + * + */ + abstract static class LxControlInstance { + /** + * Creates an instance of a particular control class. + * + * @param uuid UUID of the control object to be created + * @return a newly created control object + */ + abstract LxControl create(LxUuid uuid); + + /** + * Return a type name for this control. + * + * @return type name (as used on the Miniserver) + */ + abstract String getType(); + } + + /** + * This class describes additional parameters of a control received from the Miniserver and is used during JSON + * deserialization. + * + * @author Pawel Pieczul - initial contribution + * + */ + class LxControlDetails { + Double min; + Double max; + Double step; + String format; + Boolean increaseOnly; + String allOff; + String url; + String urlHd; + Map outputs; + Boolean presenceConnected; + } + + /** + * A callback that should be implemented by child classes to process received commands. This callback can be + * provided for each channel created by the controls. + * + * @author Pawel Pieczul - initial contribution + * + */ + @FunctionalInterface + interface CommandCallback { + abstract void handleCommand(Command cmd) throws IOException; + } + + /** + * A callback that should be implemented by child classes to return current channel state. This callback can be + * provided for each channel created by the controls. + * + * @author Pawel Pieczul - initial contribution + * + */ + @FunctionalInterface + interface StateCallback { + abstract State getChannelState(); + } + + /** + * A set of callbacks registered per each channel by the child classes. + * + * @author Pawel Pieczul - initial contribution + * + */ + private class Callbacks { + private CommandCallback commandCallback; + private StateCallback stateCallback; + + private Callbacks(CommandCallback cC, StateCallback sC) { + commandCallback = cC; + stateCallback = sC; + } + } + + /* + * Parameters parsed from the JSON configuration file during deserialization + */ + LxUuid uuid; + LxControlDetails details; + private String name; + private LxUuid roomUuid; + private Boolean isSecured; + private LxUuid categoryUuid; + private Map subControls; + private final Map states; + + /* + * Parameters set when finalizing {@link LxConfig} object setup. They will be null right after constructing object. + */ + transient String defaultChannelLabel; + private transient LxControlConfig config; + + /* + * Parameters set when object is connected to the openHAB by the binding handler + */ + final transient Set tags = new HashSet<>(); + private final transient List channels = new ArrayList<>(); + private final transient Map callbacks = new HashMap<>(); + + private final transient Logger logger; + private int numberOfChannels = 0; + + /* + * JSON deserialization routine, called during parsing configuration by the GSON library + */ + public static final JsonDeserializer DESERIALIZER = new JsonDeserializer() { + @Override + public LxControl deserialize(JsonElement json, Type type, JsonDeserializationContext context) + throws JsonParseException { + JsonObject parent = json.getAsJsonObject(); + String controlName = LxConfig.deserializeString(parent, "name"); + String controlType = LxConfig.deserializeString(parent, "type"); + LxUuid uuid = LxConfig.deserializeObject(parent, "uuidAction", LxUuid.class, context); + if (controlName == null || controlType == null || uuid == null) { + throw new JsonParseException("Control name/type/uuid is null."); + } + LxControl control = LxControlFactory.createControl(uuid, controlType); + if (control == null) { + return null; + } + control.name = controlName; + control.isSecured = LxConfig.deserializeObject(parent, "isSecured", Boolean.class, context); + control.roomUuid = LxConfig.deserializeObject(parent, "room", LxUuid.class, context); + control.categoryUuid = LxConfig.deserializeObject(parent, "cat", LxUuid.class, context); + control.details = LxConfig.deserializeObject(parent, "details", LxControlDetails.class, context); + control.subControls = LxConfig.deserializeObject(parent, "subControls", + new TypeToken>() { + }.getType(), context); + + JsonObject states = parent.getAsJsonObject("states"); + if (states != null) { + states.entrySet().forEach(entry -> { + // temperature state of intelligent home controller object is the only + // one that has state represented as an array, as this is not implemented + // yet, we will skip this state + JsonElement element = entry.getValue(); + if (element != null && !(element instanceof JsonArray)) { + String value = element.getAsString(); + if (value != null) { + String name = entry.getKey().toLowerCase(); + control.states.put(name, new LxState(new LxUuid(value), name, control)); + } + } + }); + } + return control; + } + }; + + LxControl(LxUuid uuid) { + logger = LoggerFactory.getLogger(LxControl.class); + this.uuid = uuid; + states = new HashMap<>(); + } + + /** + * A method that executes commands by the control. It delegates command execution to a registered callback method. + * + * @param channelId channel Id for the command + * @param command value of the command to perform + * @throws IOException in case of communication error with the Miniserver + */ + public final void handleCommand(ChannelUID channelId, Command command) throws IOException { + Callbacks c = callbacks.get(channelId); + if (c != null && c.commandCallback != null) { + c.commandCallback.handleCommand(command); + } + } + + /** + * Provides actual state value for the specified channel. It delegates execution to a registered callback method. + * + * @param channelId channel ID to get state for + * @return state if the channel value or null if no value available + */ + public final State getChannelState(ChannelUID channelId) { + Callbacks c = callbacks.get(channelId); + if (c != null && c.stateCallback != null) { + try { + return c.stateCallback.getChannelState(); + } catch (NumberFormatException e) { + return UnDefType.UNDEF; + } + } + return null; + } + + /** + * Obtain control's name + * + * @return Human readable name of control + */ + public String getName() { + return name; + } + + /** + * Get control's UUID as defined on the Miniserver + * + * @return UUID of the control + */ + public LxUuid getUuid() { + return uuid; + } + + /** + * Get subcontrols of this control + * + * @return subcontrols of the control + */ + public Map getSubControls() { + return subControls; + } + + /** + * Get control's channels + * + * @return channels + */ + public List getChannels() { + return channels; + } + + /** + * Get control's and its subcontrols' channels + * + * @return channels + */ + public List getChannelsWithSubcontrols() { + final List list = new ArrayList<>(channels); + subControls.values().forEach(c -> list.addAll(c.getChannelsWithSubcontrols())); + return list; + } + + /** + * Get control's Miniserver states + * + * @return control's Miniserver states + */ + public Map getStates() { + return states; + } + + /** + * Gets information is password is required to operate on this control object + * + * @return true is control is secured + */ + public Boolean isSecured() { + return isSecured != null && isSecured; + } + + /** + * Compare UUID's of two controls - + * + * @param object Object to compare with + * @return true if UUID of two objects are equal + */ + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null) { + return false; + } + if (object.getClass() != getClass()) { + return false; + } + LxControl c = (LxControl) object; + return Objects.equals(c.uuid, uuid); + } + + /** + * Hash code of the control is equal to its UUID's hash code + */ + @Override + public int hashCode() { + return uuid.hashCode(); + } + + /** + * Initialize Miniserver's control in runtime. Each class that implements {@link LxControl} should override this + * method and call it as a first step in the overridden implementation. Then it should add all runtime data, like + * channels and any fields that derive their value from the parsed JSON configuration. + * Before this method is called during configuration parsing, the control object must not be used. + * + * @param configToSet control's configuration + */ + public void initialize(LxControlConfig configToSet) { + logger.debug("Initializing LxControl: {}", uuid); + + if (config != null) { + logger.error("Error, attempt to initialize control that is already initialized: {}", uuid); + return; + } + config = configToSet; + + if (subControls == null) { + subControls = new HashMap<>(); + } + + if (config.room != null) { + config.room.addControl(this); + } + + if (config.category != null) { + config.category.addControl(this); + } + + String label = getLabel(); + if (label == null) { + // Each control on a Miniserver must have a name defined, but in case this is a subject + // of some malicious data attack, we'll prevent null pointer exception + label = "Undefined name"; + } + String roomName = config.room != null ? config.room.getName() : null; + if (roomName != null) { + label = roomName + " / " + label; + } + defaultChannelLabel = label; + + // Propagate to all subcontrols of this control object + subControls.values().forEach(c -> c.initialize(config)); + } + + /** + * This method will be called from {@link LxState}, when Miniserver state value is updated. + * By default it will query all channels of the control and update their state accordingly. + * This method will not handle channel state descriptions, as they must be prepared individually. + * It can be overridden in child class to handle particular states differently. + * + * @param state changed Miniserver state or null if not specified (any/all) + */ + public void onStateChange(LxState state) { + if (config == null) { + logger.error("Attempt to change state with not finalized configuration!: {}", state.getUuid()); + } else { + channels.forEach(channel -> { + ChannelUID channelId = channel.getUID(); + State channelState = getChannelState(channelId); + if (channelState != null) { + config.thingHandler.setChannelState(channelId, channelState); + } + }); + } + } + + /** + * Gets room UUID after it was deserialized by GSON + * + * @return room UUID + */ + public LxUuid getRoomUuid() { + return roomUuid; + } + + /** + * Gets category UUID after it was deserialized by GSON + * + * @return category UUID + */ + public LxUuid getCategoryUuid() { + return categoryUuid; + } + + /** + * Gets a GSON object for reuse + * + * @return GSON object + */ + Gson getGson() { + if (config == null) { + logger.error("Attempt to get GSON from not finalized configuration!"); + return null; + } + return config.thingHandler.getGson(); + } + + /** + * Adds a new control in the framework. Called when a control is dynamically created based on some control's state + * changes from the Miniserver. + * + * @param control a new control to be created + */ + static void addControl(LxControl control) { + control.config.thingHandler.addControl(control); + } + + /** + * Removes a control from the framework. Called when a control is dynamically deleted based on some control's state + * changes from the Miniserver. + * + * @param control a control to be removed + */ + static void removeControl(LxControl control) { + control.config.thingHandler.removeControl(control); + control.dispose(); + } + + /** + * Gets control's configuration + * + * @return configuration + */ + LxControlConfig getConfig() { + return config; + } + + /** + * Get control's room. + * + * @return control's room object + */ + LxContainer getRoom() { + return config.room; + } + + /** + * Get control's category. + * + * @return control's category object + */ + LxCategory getCategory() { + return config.category; + } + + /** + * Changes the channel state in the framework. + * + * @param id channel ID + * @param state new state value + */ + void setChannelState(ChannelUID id, State state) { + if (config == null) { + logger.error("Attempt to set channel state with not finalized configuration!: {}", id); + } else { + if (state != null) { + config.thingHandler.setChannelState(id, state); + } + } + } + + /** + * Returns control label that will be used for building channel name. This allows for customizing the label per + * control by overriding this method, but keeping {@link LxControl#getName()} intact. + * + * @return control channel label + */ + String getLabel() { + return name; + } + + /** + * Gets value of a state object of given name, if exists + * + * @param name name of state object + * @return state object's value + */ + Double getStateDoubleValue(String name) { + LxState state = states.get(name); + if (state != null) { + Object value = state.getStateValue(); + if (value instanceof Double) { + return (Double) value; + } + } + return null; + } + + /** + * Gets value of a state object of given name, if exists, and converts it to decimal type value. + * + * @param name state name + * @return state value + */ + State getStateDecimalValue(String name) { + Double value = getStateDoubleValue(name); + if (value != null) { + return new DecimalType(value); + } + return null; + } + + /** + * Gets text value of a state object of given name, if exists + * + * @param name name of state object + * @return state object's text value + */ + String getStateTextValue(String name) { + LxState state = states.get(name); + if (state != null) { + Object value = state.getStateValue(); + if (value instanceof String) { + return (String) value; + } + } + return null; + } + + /** + * Gets text value of a state object of given name, if exists and converts it to string type + * + * @param name name of state object + * @return state object's text value + */ + State getStateStringValue(String name) { + String value = getStateTextValue(name); + if (value != null) { + return new StringType(value); + } + return null; + } + + /** + * Gets double value of a state object of given name, if exists and converts it to switch type + * + * @param name name of state object + * @return state object's text value + */ + State getStateOnOffValue(String name) { + Double value = getStateDoubleValue(name); + if (value != null) { + if (value == 1.0) { + return OnOffType.ON; + } + return OnOffType.OFF; + } + return null; + } + + /** + * Create a new channel and add it to the control. Channel ID is assigned automatically in the order of calls to + * this method, see (@link LxControl#getChannelId}. + * + * @param itemType item type for the channel + * @param typeId channel type ID for the channel + * @param channelLabel channel label + * @param channelDescription channel description + * @param tags tags for the channel or null if no tags needed + * @param commandCallback {@link LxControl} child class method that will be called when command is received + * @param stateCallback {@link LxControl} child class method that will be called to get state value + * @return channel ID of the added channel (can be used to later set state description to it) + */ + ChannelUID addChannel(String itemType, ChannelTypeUID typeId, String channelLabel, String channelDescription, + Set tags, CommandCallback commandCallback, StateCallback stateCallback) { + if (channelLabel == null || channelDescription == null) { + logger.error("Attempt to add channel with not finalized configuration!: {}", channelLabel); + return null; + } + ChannelUID channelId = getChannelId(numberOfChannels++); + ChannelBuilder builder = ChannelBuilder.create(channelId, itemType).withType(typeId).withLabel(channelLabel) + .withDescription(channelDescription + " : " + channelLabel); + if (tags != null) { + builder.withDefaultTags(tags); + } + channels.add(builder.build()); + if (commandCallback != null || stateCallback != null) { + callbacks.put(channelId, new Callbacks(commandCallback, stateCallback)); + } + return channelId; + } + + /** + * Adds a new {@link StateDescription} for a channel that has multiple options to select from or a custom format + * string. + * + * @param channelId channel ID to add the description for + * @param description channel state description + */ + void addChannelStateDescription(ChannelUID channelId, StateDescription description) { + if (config == null) { + logger.error("Attempt to set channel state description with not finalized configuration!: {}", channelId); + } else { + config.thingHandler.setChannelStateDescription(channelId, description); + } + } + + /** + * Sends an action command to the Miniserver using active socket connection + * + * @param action string with action command + * @throws IOException when communication error with Miniserver occurs + */ + void sendAction(String action) throws IOException { + if (config == null) { + logger.error("Attempt to send command with not finalized configuration!: {}", action); + } else { + config.thingHandler.sendAction(uuid, action); + } + } + + /** + * Remove all channels from the control. This method is used by child classes that may decide to stop exposing any + * channels, for example by {@link LxControlMood}, which is based on {@link LxControlSwitch}, but sometime does not + * expose anything to the user. + */ + void removeAllChannels() { + channels.clear(); + callbacks.clear(); + } + + /** + * Call when control is no more needed - unlink it from containers + */ + private void dispose() { + if (config.room != null) { + config.room.removeControl(this); + } + if (config.category != null) { + config.category.removeControl(this); + } + subControls.values().forEach(control -> control.dispose()); + } + + /** + * Build channel ID for the control, based on control's UUID, thing's UUID and index of the channel for the control + * + * @param index index of a channel within control (0 for primary channel) all indexes greater than 0 will have + * -index added to the channel ID + * @return channel ID for the control and index + */ + private ChannelUID getChannelId(int index) { + if (config == null) { + logger.error("Attempt to get control's channel ID with not finalized configuration!: {}", index); + return null; + } + String controlId = uuid.toString(); + if (index > 0) { + controlId += "-" + index; + } + return new ChannelUID(config.thingHandler.getThingId(), controlId); + } + +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlAlarm.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlAlarm.java new file mode 100644 index 0000000000000..669cafdbfe655 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlAlarm.java @@ -0,0 +1,264 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +import org.eclipse.smarthome.core.library.types.DateTimeType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.StateDescription; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.loxone.internal.types.LxState; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * Loxone Control that controls the Burglar Alarm + * + * @author Michael Mattan - Initial contribution + * + */ +public class LxControlAlarm extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlAlarm(uuid); + } + + @Override + String getType() { + return "alarm"; + } + } + + private static final String CMD_ON = "on"; + private static final String CMD_ON_WITH_MOVEMENT = "on/1"; + private static final String CMD_ON_WITHOUT_MOVEMENT = "on/0"; + private static final String CMD_DELAYED_ON = "delayedon"; + private static final String CMD_DELAYED_ON_WITH_MOVEMENT = "delayedon/1"; + private static final String CMD_DELAYED_ON_WITHOUT_MOVEMENT = "delayedon/0"; + private static final String CMD_OFF = "off"; + private static final String CMD_QUIT = "quit"; + private static final String CMD_DISABLE_MOVEMENT = "dismv/1"; + private static final String CMD_ENABLE_MOVEMENT = "dismv/0"; + + /** + * If the alarm control is armed + */ + private static final String STATE_ARMED = "armed"; + + /** + * The id of the next alarm level + */ + private static final String STATE_NEXT_LEVEL = "nextlevel"; + + /** + * The delay of the next level in seconds + */ + private static final String STATE_NEXT_LEVEL_DELAY = "nextleveldelay"; + + /** + * The total delay of the next level in seconds + */ + private static final String STATE_NEXT_LEVEL_DELAY_TOTAL = "nextleveldelaytotal"; + + /** + * The id of the current alarm level + */ + private static final String STATE_LEVEL = "level"; + + /** + * Timestamp when alarm started + */ + private static final String STATE_START_TIME = "starttime"; + + /** + * The delay of the alarm control being armed + */ + private static final String STATE_ARMED_DELAY = "armeddelay"; + + /** + * The total delay of the alarm control being armed + */ + private static final String STATE_ARMED_DELAY_TOTAL = "armeddelaytotal"; + + /** + * A string of sensors separated by a pipe + */ + private static final String STATE_SENSOR = "sensors"; + + /** + * If the movement is disabled or not + */ + private static final String STATE_DISABLED_MOVE = "disabledmove"; + + private ChannelUID startTimeId; + private ChannelUID ackChannelId; + private State startTime = UnDefType.UNDEF; + private boolean presenceConnected = false; + private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + private LxControlAlarm(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + if (details.presenceConnected != null) { + presenceConnected = details.presenceConnected; + } + + addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH), defaultChannelLabel, + "Alarm armed", tags, this::handleArmAlarm, () -> getStateOnOffValue(STATE_ARMED)); + + addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH), + defaultChannelLabel + " / Arm Delayed", "Arm with delay", tags, this::handleArmDelayedAlarm, + () -> OnOffType.OFF); + + addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER), + defaultChannelLabel + " / Next Level", "ID of the next alarm level", tags, null, + () -> getStateDecimalValue(STATE_NEXT_LEVEL)); + + addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER), + defaultChannelLabel + " / Next Level Delay", "Delay of the next level", tags, null, + () -> getStateDecimalValue(STATE_NEXT_LEVEL_DELAY)); + + addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER), + defaultChannelLabel + " / Next Level Delay Total", "Total delay of the next level", tags, null, + () -> getStateDecimalValue(STATE_NEXT_LEVEL_DELAY_TOTAL)); + + addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER), + defaultChannelLabel + " / Level", "Current alarm level", tags, null, + () -> getStateDecimalValue(STATE_LEVEL)); + + startTimeId = addChannel("DateTime", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_DATETIME), + defaultChannelLabel + " / Start Time", "Time when alarm started", tags, null, () -> startTime); + + addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER), + defaultChannelLabel + " / Armed Delay", "Delay of the alarm being armed", tags, null, + () -> getStateDecimalValue(STATE_ARMED_DELAY)); + + addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER), + defaultChannelLabel + " / Armed Total Delay", "Total delay of the alarm being armed", tags, null, + () -> getStateDecimalValue(STATE_ARMED_DELAY_TOTAL)); + + addChannel("String", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_TEXT), + defaultChannelLabel + " / Sensors", "Alarm sensors", tags, null, + () -> getStateStringValue(STATE_SENSOR)); + + ackChannelId = addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH), + defaultChannelLabel + " / Acknowledge", "Acknowledge alarm", tags, this::handleQuitAlarm, + () -> OnOffType.OFF); + addChannelStateDescription(ackChannelId, new StateDescription(null, null, null, null, true, null)); + + if (presenceConnected) { + // this channel has reversed logic - we show state of enabled option, but receive state updates if disabled + addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH), + defaultChannelLabel + " / Motion Sensors", "Motion sensors enabled", tags, + this::handleMotionSensors, this::getStateMotionSensors); + } + } + + @Override + public void onStateChange(LxState state) { + String stateName = state.getName(); + if (STATE_START_TIME.equals(stateName)) { + startTime = UnDefType.UNDEF; + Object obj = state.getStateValue(); + if (obj instanceof String && !((String) obj).isEmpty()) { + try { + LocalDateTime ldt = LocalDateTime.parse((String) obj, dateTimeFormatter); + ZonedDateTime dt = ldt.atZone(ZoneId.systemDefault()); + startTime = new DateTimeType(dt); + } catch (DateTimeParseException e) { + startTime = null; + } + } + setChannelState(startTimeId, startTime); + } else if (STATE_LEVEL.equals(stateName)) { + Object obj = state.getStateValue(); + addChannelStateDescription(ackChannelId, new StateDescription(null, null, null, null, + (obj instanceof Double && ((Double) obj) == 0.0), null)); + super.onStateChange(state); + } else { + super.onStateChange(state); + } + } + + private void handleArming(Command command, String onAction, String onWithMovementAction, + String onWithoutMovementAction) throws IOException { + if (command instanceof OnOffType) { + if (command == OnOffType.ON) { + if (presenceConnected) { + Double value = getStateDoubleValue(STATE_DISABLED_MOVE); + if (value == null || value == 1.0) { + sendAction(onWithoutMovementAction); + } else { + sendAction(onWithMovementAction); + } + } else { + sendAction(onAction); + } + } else { + sendAction(CMD_OFF); + } + } + } + + private void handleArmAlarm(Command command) throws IOException { + handleArming(command, CMD_ON, CMD_ON_WITH_MOVEMENT, CMD_ON_WITHOUT_MOVEMENT); + } + + private void handleArmDelayedAlarm(Command command) throws IOException { + handleArming(command, CMD_DELAYED_ON, CMD_DELAYED_ON_WITH_MOVEMENT, CMD_DELAYED_ON_WITHOUT_MOVEMENT); + } + + private void handleQuitAlarm(Command command) throws IOException { + if (command instanceof OnOffType && command == OnOffType.ON) { + sendAction(CMD_QUIT); + } + } + + private void handleMotionSensors(Command command) throws IOException { + if (command instanceof OnOffType) { + if (command == OnOffType.ON) { + sendAction(CMD_ENABLE_MOVEMENT); + } else { + sendAction(CMD_DISABLE_MOVEMENT); + } + } + } + + private State getStateMotionSensors() { + Double value = getStateDoubleValue(STATE_DISABLED_MOVE); + if (value != null) { + if (value == 1.0) { + return OnOffType.OFF; + } + return OnOffType.ON; + } + return UnDefType.UNDEF; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlColorPickerV2.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlColorPickerV2.java new file mode 100644 index 0000000000000..abbe8fdc70d79 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlColorPickerV2.java @@ -0,0 +1,210 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.loxone.internal.types.LxTemperatureHSBType; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * A Color Picker V2 type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, a color picker control covers: + *

    + *
  • Color (Hue/Saturation/Brightness) + *
+ * + * @author Michael Mattan - initial contribution + * + */ +class LxControlColorPickerV2 extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlColorPickerV2(uuid); + } + + @Override + String getType() { + return "colorpickerv2"; + } + } + + /** + * Color state + */ + private static final String STATE_COLOR = "color"; + + private LxControlColorPickerV2(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + addChannel("Color", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_COLORPICKER), defaultChannelLabel, + "Color Picker", tags, this::handleCommands, this::getColor); + } + + private void handleCommands(Command command) throws IOException { + if (command instanceof HSBType) { + setColor((HSBType) command); + } else if (command instanceof OnOffType) { + if (command == OnOffType.ON) { + on(); + } else { + off(); + } + } else if (command instanceof DecimalType) { + setBrightness((DecimalType) command); + } else if (command instanceof PercentType) { + setBrightness((PercentType) command); + } else if (command instanceof IncreaseDecreaseType) { + if (((IncreaseDecreaseType) command).equals(IncreaseDecreaseType.INCREASE)) { + increaseDecreaseBrightness(1); + } else { + increaseDecreaseBrightness(-1); + } + } + } + + /** + * Gets the current Loxone color in HSBType format + * + * @return the HSBType color + */ + private HSBType getColor() { + HSBType hsbColor = null; + String color = getStateTextValue(STATE_COLOR); + if (color != null) { + hsbColor = this.mapLoxoneToOH(color); + } + return hsbColor; + } + + /** + * Sets the color of the color picker + * + * @param hsb the color to set + * @throws IOException error communicating with the Miniserver + */ + private void setColor(HSBType hsb) throws IOException { + HSBType currentColor = getColor(); + if (currentColor == null || !currentColor.toString().equals(hsb.toString())) { + // only update the color when it changed + // this prevents a mood switch in the Light Controller when the color did not change anyway + sendAction("hsv(" + hsb.toString() + ")"); + } + } + + /** + * Sets the color picker to on + * + * @throws IOException error communicating with the Miniserver + */ + private void on() throws IOException { + HSBType currentColor = getColor(); + if (currentColor != null) { + setColor(new HSBType(currentColor.getHue(), currentColor.getSaturation(), PercentType.HUNDRED)); + } + } + + /** + * Sets the color picker to off + * + * @throws IOException error communicating with the Miniserver + */ + private void off() throws IOException { + HSBType currentColor = getColor(); + if (currentColor != null) { + setColor(new HSBType(currentColor.getHue(), currentColor.getSaturation(), PercentType.ZERO)); + } + } + + /** + * set the brightness level + * + * @param p the brightness percentage + * @throws IOException error communicating with the Miniserver + */ + private void setBrightness(PercentType p) throws IOException { + HSBType currentColor = this.getColor(); + if (currentColor != null) { + setColor(new HSBType(currentColor.getHue(), currentColor.getSaturation(), p)); + } + } + + /** + * set the brightness level from a decimal type + * + * @param d the brightness in decimal + * @throws IOException error communicating with the Miniserver + */ + private void setBrightness(DecimalType d) throws IOException { + setBrightness(new PercentType(d.toBigDecimal())); + } + + /** + * Increases/decreases the brightness with a given step + * + * @param step the amount to increase/decrease + * @throws IOException error communicating with the Miniserver + */ + private void increaseDecreaseBrightness(int step) throws IOException { + HSBType currentColor = this.getColor(); + if (currentColor != null) { + setBrightness(new PercentType(currentColor.getBrightness().intValue() + step)); + } + } + + /** + * Map Loxone color to OpenHab HSBType + * + * @param color color in format hsb(h,s,v) or temp(brightness,kelvin) + * @return HSBType + */ + private HSBType mapLoxoneToOH(String color) { + HSBType hsbColor = null; + + try { + Pattern hsvPattern = Pattern.compile("^hsv\\([0-9]\\d{0,2},[0-9]\\d{0,2},[0-9]\\d{0,2}\\)"); + Pattern tempPattern = Pattern.compile("^temp\\([0-9]\\d{0,2},[0-9]\\d{0,4}\\)"); + Matcher valueMatcher = Pattern.compile("\\((.*?)\\)").matcher(color); + + if (hsvPattern.matcher(color).matches() && valueMatcher.find()) { + // we have a hsv(hue,saturation,value) pattern + hsbColor = new HSBType(valueMatcher.group(1)); + } else if (tempPattern.matcher(color).matches() && valueMatcher.find()) { + // we have a temp(brightness,kelvin) pattern + hsbColor = LxTemperatureHSBType.fromBrightnessTemperature(valueMatcher.group(1)); + } + } catch (IllegalArgumentException e) { + // an error happened during HSBType creation, we return null + hsbColor = null; + } + return hsbColor; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlDimmer.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlDimmer.java new file mode 100644 index 0000000000000..d50cdfae1f964 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlDimmer.java @@ -0,0 +1,162 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import java.io.IOException; + +import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * A dimmer type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, a dimmer control is: + *

    + *
  • a virtual input of dimmer type + *
+ * + * @author Stephan Brunner - initial contribution + * + */ +class LxControlDimmer extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlDimmer(uuid); + } + + @Override + String getType() { + return "dimmer"; + } + } + + /** + * States + */ + private static final String STATE_POSITION = "position"; + private static final String STATE_MIN = "min"; + private static final String STATE_MAX = "max"; + private static final String STATE_STEP = "step"; + + /** + * Command string used to set the dimmer ON + */ + private static final String CMD_ON = "On"; + /** + * Command string used to set the dimmer to OFF + */ + private static final String CMD_OFF = "Off"; + + private LxControlDimmer(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + addChannel("Dimmer", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_DIMMER), defaultChannelLabel, + "Dimmer", tags, this::handleCommands, this::getChannelState); + } + + private void handleCommands(Command command) throws IOException { + if (command instanceof OnOffType) { + if (command == OnOffType.ON) { + sendAction(CMD_ON); + } else { + sendAction(CMD_OFF); + } + } else if (command instanceof PercentType) { + PercentType percentCmd = (PercentType) command; + setPosition(percentCmd.doubleValue()); + } else if (command instanceof IncreaseDecreaseType) { + Double value = getStateDoubleValue(STATE_POSITION); + Double min = getStateDoubleValue(STATE_MIN); + Double max = getStateDoubleValue(STATE_MAX); + Double step = getStateDoubleValue(STATE_STEP); + if (value != null && max != null && min != null && step != null && min >= 0 && max >= 0 && max > min) { + if ((IncreaseDecreaseType) command == IncreaseDecreaseType.INCREASE) { + value += step; + if (value > max) { + value = max; + } + } else { + value -= step; + if (value < min) { + value = min; + } + } + sendAction(value.toString()); + } + } + } + + private PercentType getChannelState() { + Double value = mapLoxoneToOH(getStateDoubleValue(STATE_POSITION)); + if (value != null && value >= 0 && value <= 100) { + return new PercentType(value.intValue()); + } + return null; + } + + /** + * Sets the current position of the dimmer + * + * @param position position to move to (0-100, 0 - full off, 100 - full on) + * @throws IOException error communicating with the Miniserver + */ + private void setPosition(Double position) throws IOException { + Double loxonePosition = mapOHToLoxone(position); + if (loxonePosition != null) { + sendAction(loxonePosition.toString()); + } + } + + private Double mapLoxoneToOH(Double loxoneValue) { + if (loxoneValue != null) { + // 0 means turn dimmer off, any value above zero should be mapped from min-max range + if (Double.compare(loxoneValue, 0.0) == 0) { + return 0.0; + } + Double max = getStateDoubleValue(STATE_MAX); + Double min = getStateDoubleValue(STATE_MIN); + if (max != null && min != null && max > min && min >= 0 && max >= 0) { + return 100 * (loxoneValue - min) / (max - min); + } + } + return null; + } + + private Double mapOHToLoxone(Double ohValue) { + if (ohValue != null) { + // 0 means turn dimmer off, any value above zero should be mapped to min-max range + if (Double.compare(ohValue, 0.0) == 0) { + return 0.0; + } + Double max = getStateDoubleValue(STATE_MAX); + Double min = getStateDoubleValue(STATE_MIN); + if (max != null && min != null) { + double value = min + ohValue * (max - min) / 100; + return value; // no rounding to integer value is needed as loxone is accepting floating point values + } + } + return null; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlFactory.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlFactory.java new file mode 100644 index 0000000000000..df0b6269c80b4 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlFactory.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import java.util.HashMap; +import java.util.Map; + +import org.openhab.binding.loxone.internal.controls.LxControl.LxControlInstance; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * A factory of controls of Loxone Miniserver. + * It creates various types of control objects based on control type received from Miniserver. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlFactory { + static { + CONTROLS = new HashMap<>(); + add(new LxControlAlarm.Factory()); + add(new LxControlColorPickerV2.Factory()); + add(new LxControlDimmer.Factory()); + add(new LxControlInfoOnlyAnalog.Factory()); + add(new LxControlInfoOnlyDigital.Factory()); + add(new LxControlJalousie.Factory()); + add(new LxControlLeftRightAnalog.Factory()); + add(new LxControlLeftRightDigital.Factory()); + add(new LxControlLightController.Factory()); + add(new LxControlLightControllerV2.Factory()); + add(new LxControlPushbutton.Factory()); + add(new LxControlRadio.Factory()); + add(new LxControlSlider.Factory()); + add(new LxControlSwitch.Factory()); + add(new LxControlTextState.Factory()); + add(new LxControlTimedSwitch.Factory()); + add(new LxControlTracker.Factory()); + add(new LxControlUpDownAnalog.Factory()); + add(new LxControlUpDownDigital.Factory()); + add(new LxControlValueSelector.Factory()); + add(new LxControlWebPage.Factory()); + } + + private static final Map CONTROLS; + + /** + * Create a {@link LxControl} object for a control received from the Miniserver + * + * @param uuid UUID of the control to create + * @param type control type + * @return created control object or null if error + */ + static LxControl createControl(LxUuid uuid, String type) { + LxControlInstance control = CONTROLS.get(type.toLowerCase()); + if (control != null) { + return control.create(uuid); + } + return null; + } + + private static void add(LxControlInstance control) { + CONTROLS.put(control.getType(), control); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlInfoOnlyAnalog.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlInfoOnlyAnalog.java new file mode 100644 index 0000000000000..4f1c1b89a6d7b --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlInfoOnlyAnalog.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.StateDescription; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * An InfoOnlyAnalog type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, this control covers analog virtual states only. This control does not send any + * commands to the Miniserver. It can be used to read a formatted representation of an analog virtual state. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlInfoOnlyAnalog extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlInfoOnlyAnalog(uuid); + } + + @Override + String getType() { + return "infoonlyanalog"; + } + } + + /** + * InfoOnlyAnalog state with current value + */ + private static final String STATE_VALUE = "value"; + + private LxControlInfoOnlyAnalog(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + ChannelUID cid = addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_ANALOG), + defaultChannelLabel, "Analog virtual state", tags, null, () -> getStateDecimalValue(STATE_VALUE)); + String format; + if (details != null && details.format != null) { + format = details.format; + } else { + format = "%.1f"; + } + addChannelStateDescription(cid, new StateDescription(null, null, null, format, true, null)); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlInfoOnlyDigital.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlInfoOnlyDigital.java new file mode 100644 index 0000000000000..bfe6c8152196c --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlInfoOnlyDigital.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * An InfoOnlyDigital type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, this control covers digital virtual states only. This control does not send + * any commands to the Miniserver. It can be used to read a formatted representation of a digital virtual state. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlInfoOnlyDigital extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlInfoOnlyDigital(uuid); + } + + @Override + String getType() { + return "infoonlydigital"; + } + } + + /** + * InfoOnlyDigital has one state that can be on/off + */ + private static final String STATE_ACTIVE = "active"; + + private LxControlInfoOnlyDigital(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_SWITCH), defaultChannelLabel, + "Digital virtual state", tags, null, this::getChannelState); + } + + private State getChannelState() { + Double value = getStateDoubleValue(STATE_ACTIVE); + if (value != null) { + if (value == 0) { + return OnOffType.OFF; + } else if (value == 1.0) { + return OnOffType.ON; + } else { + return UnDefType.UNDEF; + } + } + return null; + }; +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlJalousie.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlJalousie.java new file mode 100644 index 0000000000000..79b95b20c4515 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlJalousie.java @@ -0,0 +1,240 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import java.io.IOException; + +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.StopMoveType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.loxone.internal.types.LxState; +import org.openhab.binding.loxone.internal.types.LxUuid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A jalousie type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, a jalousie control covers: + *

    + *
  • Blinds
  • + *
  • Automatic blinds
  • + *
  • Automatic blinds integrated
  • + *
+ *

+ * Jalousie control has three channels: + *

    + *
  • 0 (default) - rollershutter position
  • + *
  • 1 - shading command (always off switch, sending on triggers shading)
  • + *
  • 2 - automatic shading (on/off switch)
  • + *
+ * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlJalousie extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlJalousie(uuid); + } + + @Override + String getType() { + return "jalousie"; + } + } + + /** + * Jalousie is moving up + */ + private static final String STATE_UP = "up"; + /** + * Jalousie is moving down + */ + private static final String STATE_DOWN = "down"; + /** + * The position of the Jalousie, a number from 0 to 1 + * Jalousie upper position = 0 + * Jalousie lower position = 1 + */ + private static final String STATE_POSITION = "position"; + /** + * Only used by ones with Autopilot + */ + private static final String STATE_AUTO_ACTIVE = "autoactive"; + /** + * Command string used to set control's state to Full Down + */ + private static final String CMD_FULL_DOWN = "FullDown"; + /** + * Command string used to set control's state to Full Up + */ + private static final String CMD_FULL_UP = "FullUp"; + /** + * Command string used to stop rollershutter + */ + private static final String CMD_STOP = "Stop"; + /** + * Command to shade the jalousie + */ + private static final String CMD_SHADE = "shade"; + /** + * Command to enable automatic shading + */ + private static final String CMD_AUTO = "auto"; + /** + * Command to disable automatic shading + */ + private static final String CMD_NO_AUTO = "NoAuto"; + + private final Logger logger = LoggerFactory.getLogger(LxControlJalousie.class); + private Double targetPosition; + + private LxControlJalousie(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + addChannel("Rollershutter", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_ROLLERSHUTTER), + defaultChannelLabel, "Rollershutter", tags, this::handleOperateCommands, this::getOperateState); + addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH), + defaultChannelLabel + " / Shade", "Rollershutter shading", null, this::handleShadeCommands, + () -> OnOffType.OFF); + addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH), + defaultChannelLabel + " / Auto Shade", "Rollershutter automatic shading", null, + this::handleAutoShadeCommands, this::getAutoShadeState); + } + + private void handleOperateCommands(Command command) throws IOException { + if (command instanceof PercentType) { + moveToPosition(((PercentType) command).doubleValue() / 100); + } else if (command instanceof UpDownType) { + if ((UpDownType) command == UpDownType.UP) { + sendAction(CMD_FULL_UP); + } else { + sendAction(CMD_FULL_DOWN); + } + } else if (command instanceof StopMoveType) { + if ((StopMoveType) command == StopMoveType.STOP) { + sendAction(CMD_STOP); + } + } + }; + + private PercentType getOperateState() { + Double value = getStateDoubleValue(STATE_POSITION); + if (value != null && value >= 0 && value <= 1) { + // state UP or DOWN from Loxone indicates blinds are moving up or down + // state UP in openHAB means blinds are fully up (0%) and DOWN means fully down (100%) + // so we will update only position and not up or down states + // a basic calculation like (value * 100.0) will give significant errors for some fractions, e.g. + // 0.29 * 100 = 28.xxxxx which in turn if converted to integer will cause the position to take same value + // for two different positions and later jump skipping one value (29 in this example) + // for that reason we need to round the results of multiplication to avoid this skipping of percentages + return new PercentType((int) Math.round(value * 100.0)); + } + return null; + }; + + private void handleShadeCommands(Command command) throws IOException { + if (command instanceof OnOffType) { + if ((OnOffType) command == OnOffType.ON) { + sendAction(CMD_SHADE); + } + } + }; + + private void handleAutoShadeCommands(Command command) throws IOException { + if (command instanceof OnOffType) { + if ((OnOffType) command == OnOffType.ON) { + sendAction(CMD_AUTO); + } else { + sendAction(CMD_NO_AUTO); + } + } + }; + + private OnOffType getAutoShadeState() { + Double value = getStateDoubleValue(STATE_AUTO_ACTIVE); + if (value != null) { + return value == 1.0 ? OnOffType.ON : OnOffType.OFF; + } + return null; + }; + + /** + * Monitor jalousie position against desired target position and stop it if target position is reached. + */ + @Override + public void onStateChange(LxState state) { + // check position changes + if (STATE_POSITION.equals(state.getName()) && targetPosition != null && targetPosition >= 0 + && targetPosition <= 1) { + // see in which direction jalousie is moving + Object value = state.getStateValue(); + if (value instanceof Double) { + Double currentPosition = (Double) value; + Double upValue = getStateDoubleValue(STATE_UP); + Double downValue = getStateDoubleValue(STATE_DOWN); + if (upValue != null && downValue != null) { + if (((upValue == 1) && (currentPosition <= targetPosition)) + || ((downValue == 1) && (currentPosition >= targetPosition))) { + targetPosition = null; + try { + sendAction(CMD_STOP); + } catch (IOException e) { + logger.debug("Error stopping jalousie when meeting target position."); + } + } + } + } + } else { + super.onStateChange(state); + } + } + + /** + * Move the rollershutter (jalousie) to a desired position. + *

+ * The jalousie will start moving in the desired direction based on the current position. It will stop moving once + * there is a state update event received with value above/below (depending on direction) or equal to the set + * position. + * + * @param position end position to move jalousie to, floating point number from 0..1 (0-fully closed to 1-fully + * open) + * @throws IOException when something went wrong with communication + */ + private void moveToPosition(Double position) throws IOException { + Double currentPosition = getStateDoubleValue(STATE_POSITION); + if (currentPosition != null && currentPosition >= 0 && currentPosition <= 1) { + if (currentPosition > position) { + logger.debug("Moving jalousie up from {} to {}", currentPosition, position); + targetPosition = position; + sendAction(CMD_FULL_UP); + } else if (currentPosition < position) { + logger.debug("Moving jalousie down from {} to {}", currentPosition, position); + targetPosition = position; + sendAction(CMD_FULL_DOWN); + } + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlLeftRightAnalog.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlLeftRightAnalog.java new file mode 100644 index 0000000000000..4926a2f4c4f6a --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlLeftRightAnalog.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * An LeftRightAnalog type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, LeftRightAnalog control is a virtual input that is analog and has an input + * type + * up-down buttons. The analog buttons are simulated as a single analog number value. This control behaves exactly the + * same as {@link LxControlUpDownAnalog} but has a different name. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlLeftRightAnalog extends LxControlUpDownAnalog { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlLeftRightAnalog(uuid); + } + + @Override + String getType() { + return "leftrightanalog"; + } + } + + private LxControlLeftRightAnalog(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config, "Left/Right Analog"); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlLeftRightDigital.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlLeftRightDigital.java new file mode 100644 index 0000000000000..3db34c58048c7 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlLeftRightDigital.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * An LeftRightDigital type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, LeftRightDigital control is a virtual input that is digital and has an input + * type left-right buttons. It has no states and can only accept commands. Only left/right (which are actually equal to + * up/down commands of {@link LxControlUpDownDigital}) on/off commands are generated. Pulse commands are not supported, + * because of lack of corresponding feature in openHAB. Pulse can be emulated by quickly alternating between ON and OFF + * commands. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlLeftRightDigital extends LxControlUpDownDigital { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlLeftRightDigital(uuid); + } + + @Override + String getType() { + return "leftrightdigital"; + } + } + + private LxControlLeftRightDigital(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config, " / Left", "Left/Right Digital: Left", " / Right", "Left/Right Digital: Right"); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlLightController.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlLightController.java new file mode 100644 index 0000000000000..b11949c96163e --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlLightController.java @@ -0,0 +1,160 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.StateDescription; +import org.eclipse.smarthome.core.types.StateOption; +import org.openhab.binding.loxone.internal.types.LxState; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * A Light Controller type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, a light controller is one of following functional blocks: + *

    + *
  • Lighting Controller + *
  • Hotel Lighting Controller + *
+ * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlLightController extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlLightController(uuid); + } + + @Override + String getType() { + return "lightcontroller"; + } + } + + /** + * Number of scenes supported by the Miniserver. Indexing starts with 0 to NUM_OF_SCENES-1. + */ + private static final int NUM_OF_SCENES = 10; + + /** + * Current active scene number (0-9) + */ + private static final String STATE_ACTIVE_SCENE = "activescene"; + /** + * List of available scenes (public state, so user can monitor scene list updates) + */ + private static final String STATE_SCENE_LIST = "scenelist"; + /** + * Command string used to set control's state to ON + */ + private static final String CMD_ON = "On"; + /** + * Command string used to set control's state to OFF + */ + private static final String CMD_OFF = "Off"; + /** + * Command string used to go to the next scene + */ + private static final String CMD_NEXT_SCENE = "plus"; + /** + * Command string used to go to the previous scene + */ + private static final String CMD_PREVIOUS_SCENE = "minus"; + private static final int SCENE_ALL_ON = 9; + + private List sceneNames = new ArrayList<>(); + private ChannelUID channelId; + + private LxControlLightController(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + // add only channel, state description will be added later when a control state update message is received + channelId = addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_LIGHT_CTRL), + defaultChannelLabel, "Light controller", tags, this::handleCommands, this::getChannelState); + } + + private void handleCommands(Command command) throws IOException { + if (command instanceof OnOffType) { + if ((OnOffType) command == OnOffType.ON) { + sendAction(CMD_ON); + } else { + sendAction(CMD_OFF); + } + } else if (command instanceof UpDownType) { + if ((UpDownType) command == UpDownType.UP) { + sendAction(CMD_NEXT_SCENE); + } else { + sendAction(CMD_PREVIOUS_SCENE); + } + } else if (command instanceof DecimalType) { + int scene = ((DecimalType) command).intValue(); + if (scene == SCENE_ALL_ON) { + sendAction(CMD_ON); + } else if (scene >= 0 && scene < NUM_OF_SCENES) { + sendAction(Long.toString(scene)); + } + } + } + + private DecimalType getChannelState() { + Double value = getStateDoubleValue(STATE_ACTIVE_SCENE); + if (value != null && value >= 0 && value < NUM_OF_SCENES) { + return new DecimalType(value); + } + return null; + }; + + /** + * Get scene names from new state value received from the Miniserver + */ + @Override + public void onStateChange(LxState state) { + if (STATE_SCENE_LIST.equals(state.getName()) && channelId != null) { + Object value = state.getStateValue(); + if (value instanceof String) { + sceneNames.clear(); + String[] scenes = ((String) value).split(","); + for (String line : scenes) { + line = line.replaceAll("\"", ""); + String[] params = line.split("="); + if (params.length == 2) { + sceneNames.add(new StateOption(params[0], params[1])); + } + } + addChannelStateDescription(channelId, new StateDescription(BigDecimal.ZERO, + new BigDecimal(NUM_OF_SCENES - 1), BigDecimal.ONE, null, false, sceneNames)); + } + } else { + super.onStateChange(state); + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlLightControllerV2.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlLightControllerV2.java new file mode 100644 index 0000000000000..a2c4efb6c1c50 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlLightControllerV2.java @@ -0,0 +1,262 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.StateDescription; +import org.eclipse.smarthome.core.types.StateOption; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.loxone.internal.types.LxState; +import org.openhab.binding.loxone.internal.types.LxUuid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonSyntaxException; + +/** + * A Light Controller V2 type of control on Loxone Miniserver. + *

+ * This control has been introduced in Loxone Config 9 in 2017 and it makes the {@link LxControlLightController} + * obsolete. Both controls will exist for some time together. + *

+ * Light controller V2 can have N outputs named AQ1...AQN that can function as Switch, Dimmer, RGB, Lumitech or Smart + * Actuator functional blocks. Individual controls will be created for these outputs so they can be operated directly + * and independently from the controller. + *

+ * Controller can also have M moods configured. Each mood defines own subset of outputs and their settings, which will + * be engaged when the mood is active. A dedicated switch control object will be created for each mood. + * This effectively will allow for mixing various moods by individually enabling/disabling them. + *

+ * It seems there is no imposed limitation for the number of outputs and moods. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlLightControllerV2 extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlLightControllerV2(uuid); + } + + @Override + String getType() { + return "lightcontrollerv2"; + } + } + + /** + * State with list of active moods + */ + private static final String STATE_ACTIVE_MOODS_LIST = "activemoods"; + /** + * State with list of available moods + */ + private static final String STATE_MOODS_LIST = "moodlist"; + + /** + * Command string used to set a given mood + */ + private static final String CMD_CHANGE_TO_MOOD = "changeTo"; + /** + * Command string used to change to the next mood + */ + private static final String CMD_NEXT_MOOD = "plus"; + /** + * Command string used to change to the previous mood + */ + private static final String CMD_PREVIOUS_MOOD = "minus"; + /** + * Command string used to add mood to the active moods (mix it in) + */ + private static final String CMD_ADD_MOOD = "addMood"; + /** + * Command string used to remove mood from the active moods (mix it out) + */ + private static final String CMD_REMOVE_MOOD = "removeMood"; + + private final transient Logger logger = LoggerFactory.getLogger(LxControlLightControllerV2.class); + + // Following commands are not supported: + // moveFavoriteMood, moveAdditionalMood, moveMood, addToFavoriteMood, removeFromFavoriteMood, learn, delete + + private Map moodList = new HashMap<>(); + private List activeMoods = new ArrayList<>(); + private ChannelUID channelId; + + private LxControlLightControllerV2(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + // add only channel, state description will be added later when a control state update message is received + channelId = addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_LIGHT_CTRL), + defaultChannelLabel, "Light controller V2", tags, this::handleCommands, this::getChannelState); + } + + private void handleCommands(Command command) throws IOException { + if (command instanceof UpDownType) { + if ((UpDownType) command == UpDownType.UP) { + sendAction(CMD_NEXT_MOOD); + } else { + sendAction(CMD_PREVIOUS_MOOD); + } + } else if (command instanceof DecimalType) { + int moodId = ((DecimalType) command).intValue(); + if (isMoodOk(moodId)) { + sendAction(CMD_CHANGE_TO_MOOD + "/" + moodId); + } + } + } + + private State getChannelState() { + // update the single mood channel state + if (activeMoods.size() == 1) { + Integer id = activeMoods.get(0); + if (isMoodOk(id)) { + return new DecimalType(id); + } + } + return UnDefType.UNDEF; + }; + + /** + * Get configured and active moods from a new state value received from the Miniserver + * + * @param state state update from the Miniserver + */ + @Override + public void onStateChange(LxState state) { + String stateName = state.getName(); + Object value = state.getStateValue(); + try { + if (STATE_MOODS_LIST.equals(stateName) && value instanceof String) { + onMoodsListChange((String) value); + } else if (STATE_ACTIVE_MOODS_LIST.equals(stateName) && value instanceof String) { + // this state can be received before list of moods, but it contains a valid list of IDs + Integer[] array = getGson().fromJson((String) value, Integer[].class); + activeMoods = Arrays.asList(array).stream().filter(id -> isMoodOk(id)).collect(Collectors.toList()); + // update all moods states - this will force update of channels too + moodList.values().forEach(mood -> mood.onStateChange(null)); + // finally we update controller's state based on the active moods list + super.onStateChange(state); + } + } catch (JsonSyntaxException e) { + logger.debug("Error parsing state {}: {}", stateName, e.getMessage()); + } + } + + /** + * Mix a mood into currently active moods. + * + * @param moodId ID of the mood to add + * @throws IOException when something went wrong with communication + */ + void addMood(Integer moodId) throws IOException { + if (isMoodOk(moodId)) { + sendAction(CMD_ADD_MOOD + "/" + moodId); + } + } + + /** + * Check if mood is currently active. + * + * @param moodId mood ID to check + * @return true if mood is currently active + */ + boolean isMoodActive(Integer moodId) { + return activeMoods.contains(moodId); + } + + /** + * Check if mood ID is within allowed range + * + * @param moodId mood ID to check + * @return true if mood ID is within allowed range or range is not configured + */ + boolean isMoodOk(Integer moodId) { + return moodId != null && moodList.containsKey(moodId); + } + + /** + * Mix a mood out of currently active moods. + * + * @param moodId ID of the mood to remove + * @throws IOException when something went wrong with communication + */ + void removeMood(Integer moodId) throws IOException { + if (isMoodOk(moodId)) { + sendAction(CMD_REMOVE_MOOD + "/" + moodId); + } + } + + /** + * Handles a change in the list of configured moods + * + * @param text json structure with new moods + * @throws JsonSyntaxException error parsing json structure + */ + private void onMoodsListChange(String text) throws JsonSyntaxException { + LxControlMood[] array = getGson().fromJson(text, LxControlMood[].class); + Map newMoodList = new HashMap<>(); + Integer minMoodId = null; + Integer maxMoodId = null; + for (LxControlMood mood : array) { + Integer id = mood.getId(); + if (id != null && mood.getName() != null) { + logger.debug("Adding mood {} (id={}, name={})", id, mood.getName()); + // mood-UUID = -M + LxUuid moodUuid = new LxUuid(getUuid().toString() + "-M" + id); + mood.initialize(getConfig(), this, moodUuid); + newMoodList.put(id, mood); + if (minMoodId == null || minMoodId > id) { + minMoodId = id; + } + if (maxMoodId == null || maxMoodId < id) { + maxMoodId = id; + } + } + } + + if (channelId != null && minMoodId != null && maxMoodId != null) { + // convert all moods to options list for state description + List optionsList = newMoodList.values().stream() + .map(mood -> new StateOption(mood.getId().toString(), mood.getName())).collect(Collectors.toList()); + addChannelStateDescription(channelId, new StateDescription(new BigDecimal(minMoodId), + new BigDecimal(maxMoodId), BigDecimal.ONE, null, false, optionsList)); + } + + moodList.values().forEach(m -> removeControl(m)); + newMoodList.values().forEach(m -> addControl(m)); + moodList = newMoodList; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlMood.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlMood.java new file mode 100644 index 0000000000000..6ffbd6095a344 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlMood.java @@ -0,0 +1,145 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import java.io.IOException; + +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.openhab.binding.loxone.internal.types.LxUuid; + +import com.google.gson.annotations.SerializedName; + +/** + * This class represents a mood belonging to a {@link LxControlMood} object. + * A mood is effectively a switch. When the switch is set to ON, mood is active and mixed into a set of active + * moods. + * A mood is deserialized using a default gson, not like {@link LxControl} which has a proprietary deserialization + * method. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlMood extends LxControlSwitch { + + /** + * An ID that uniquely identifies this mood (e.g. inside activeMoods) + */ + @SerializedName("id") + private Integer moodId; + + /** + * Bitmask that tells if the mood is used for a specific purpose in the logic. + * If it’s not used, it can be removed without affecting the logic on the Miniserver. + * 0: not used + * 1: this mood is activated by a movement event + * 2: a T5 or other inputs activate/deactivate this mood + */ + @SerializedName("used") + private Integer isUsed; + + /** + * Whether or not this mood can be controlled with a t5 input + */ + @SerializedName("t5") + private Boolean isT5Controlled; + + /** + * If a mood is marked as static it cannot be deleted or modified in any way. + * But it can be moved within and between favorite and additional lists. + */ + @SerializedName("static") + private Boolean isStatic; + + private LxControlLightControllerV2 controller; + + /** + * This constructor will be called by the default JSON deserialization + */ + LxControlMood() { + super(null); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + } + + public void initialize(LxControlConfig config, LxControlLightControllerV2 controller, LxUuid uuid) { + this.uuid = uuid; + this.controller = controller; + super.initialize(config); + // the 'all off' mood can't be operated as a switch, but needs to be present on the moods list for the + // lighting controller + // currently the API does not give a hint how to figure out the 'all off' mood + // empirically this is the only mood that is not editable by the user and has a static flag set on + // we will assume that the only static mood is 'all off' mood + if (isStatic != null && isStatic) { + removeAllChannels(); + } + } + + @Override + String getLabel() { + return "Mood / " + super.getLabel(); + } + + /** + * Get an ID of this mood. ID identifies the mood within a light controller. + * It is equal to the mood ID received from the Miniserver. + * + * @return mood ID + */ + Integer getId() { + return moodId; + } + + /** + * Mix the mood into active moods. + * + * @throws IOException when something went wrong with communication + */ + @Override + void on() throws IOException { + if (controller != null) { + controller.addMood(moodId); + } + } + + /** + * Mix the mood out of active moods. + * + * @throws IOException when something went wrong with communication + */ + @Override + void off() throws IOException { + if (controller != null) { + controller.removeMood(moodId); + } + } + + /** + * Return whether the mood is active of not. + * + * @return 1 if mood is active and 0 otherwise + */ + @Override + OnOffType getSwitchState() { + if (controller != null && controller.isMoodOk(moodId)) { + if (controller.isMoodActive(moodId)) { + return OnOffType.ON; + } + return OnOffType.OFF; + } + return null; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlPushbutton.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlPushbutton.java new file mode 100644 index 0000000000000..c56f44c42a7a0 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlPushbutton.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import java.io.IOException; + +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * A pushbutton type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, a pushbutton control covers virtual input of type pushbutton + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlPushbutton extends LxControlSwitch { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlPushbutton(uuid); + } + + @Override + String getType() { + return "pushbutton"; + } + } + + /** + * Command string used to set control's state to ON and OFF (tap) + */ + private static final String CMD_PULSE = "Pulse"; + + LxControlPushbutton(LxUuid uuid) { + super(uuid); + } + + @Override + void handleSwitchCommands(Command command) throws IOException { + if (command instanceof OnOffType) { + if ((OnOffType) command == OnOffType.ON) { + sendAction(CMD_PULSE); + } else { + off(); + } + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlRadio.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlRadio.java new file mode 100644 index 0000000000000..80664f1adb101 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlRadio.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.StateDescription; +import org.eclipse.smarthome.core.types.StateOption; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * A radio-button type of control on Loxone Miniserver. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlRadio extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlRadio(uuid); + } + + @Override + String getType() { + return "radio"; + } + } + + /** + * Number of outputs a radio controller may have + */ + private static final int MAX_RADIO_OUTPUTS = 16; + + /** + * Radio-button has one state that is a number representing current active output + */ + private static final String STATE_ACTIVE_OUTPUT = "activeoutput"; + + /** + * Command string used to set radio button to all outputs off + */ + private static final String CMD_RESET = "reset"; + + private Map outputsMap; + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + // add both channel and state description (all needed configuration is available) + ChannelUID cid = addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RADIO_BUTTON), + defaultChannelLabel, "Radio button", tags, this::handleCommands, this::getChannelState); + + if (details != null) { + List outputs = new ArrayList<>(); + if (details.outputs != null) { + outputsMap = details.outputs; + outputs = details.outputs.entrySet().stream().map(e -> new StateOption(e.getKey(), e.getValue())) + .collect(Collectors.toList()); + } + if (details.allOff != null && !details.allOff.isEmpty()) { + outputs.add(new StateOption("0", details.allOff)); + outputsMap.put("0", details.allOff); + } + addChannelStateDescription(cid, new StateDescription(BigDecimal.ZERO, new BigDecimal(MAX_RADIO_OUTPUTS), + BigDecimal.ONE, null, false, outputs)); + } + } + + private LxControlRadio(LxUuid uuid) { + super(uuid); + } + + private void handleCommands(Command command) throws IOException { + if (((command instanceof OnOffType && (OnOffType) command == OnOffType.OFF) || DecimalType.ZERO.equals(command)) + && outputsMap.containsKey("0")) { + sendAction(CMD_RESET); + } else if (command instanceof DecimalType) { + DecimalType output = (DecimalType) command; + if (outputsMap.containsKey(output.toString())) { + sendAction(String.valueOf(output.intValue())); + } + } + } + + private DecimalType getChannelState() { + Double output = getStateDoubleValue(STATE_ACTIVE_OUTPUT); + if (output != null && output % 1 == 0 && outputsMap.containsKey(String.valueOf(output.intValue()))) { + return new DecimalType(output); + } + return null; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlSlider.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlSlider.java new file mode 100644 index 0000000000000..3ccc8e1ea9409 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlSlider.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * A slider type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, a slider control is a virtual input of slider type. + * It behaves exactly the same as {@link LxControlUpDownAnalog}. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlSlider extends LxControlUpDownAnalog { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlSlider(uuid); + } + + @Override + String getType() { + return "slider"; + } + } + + private LxControlSlider(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config, "Slider"); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlSwitch.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlSwitch.java new file mode 100644 index 0000000000000..250d9813e456b --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlSwitch.java @@ -0,0 +1,138 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import java.io.IOException; + +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.loxone.internal.types.LxCategory; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * A switch type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, a switch control is: + *

    + *
  • a virtual input of switch type + *
  • a push button function block + *
+ * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlSwitch extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlSwitch(uuid); + } + + @Override + String getType() { + return "switch"; + } + } + + /** + * Switch has one state that can be on/off + */ + private static final String STATE_ACTIVE = "active"; + + /** + * Command string used to set control's state to ON + */ + private static final String CMD_ON = "On"; + /** + * Command string used to set control's state to OFF + */ + private static final String CMD_OFF = "Off"; + + LxControlSwitch(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + LxCategory category = getCategory(); + if (category != null && category.getType() == LxCategory.CategoryType.LIGHTS) { + tags.add("Lighting"); + } + addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH), defaultChannelLabel, + "Switch", tags, this::handleSwitchCommands, this::getSwitchState); + } + + void handleSwitchCommands(Command command) throws IOException { + if (command instanceof OnOffType) { + if ((OnOffType) command == OnOffType.ON) { + on(); + } else { + off(); + } + } + } + + /** + * Set switch to ON. + *

+ * Sends a command to operate the switch. + * This method is separated, so {@link LxControlMood} can inherit from this class, but handle 'on' commands + * differently. + * + * @throws IOException when something went wrong with communication + */ + void on() throws IOException { + sendAction(CMD_ON); + } + + /** + * Set switch to OFF. + *

+ * Sends a command to operate the switch. + * This method is separated, so {@link LxControlMood} and {@link LxControlPushbutton} can inherit from this class, + * but handle 'off' commands differently. + * + * @throws IOException when something went wrong with communication + */ + void off() throws IOException { + sendAction(CMD_OFF); + } + + /** + * Get current value of the switch'es state. + * This method is separated, so it can be overridden by {@link LxControlTimedSwitch}, which inherits from the switch + * class, but has a different way of handling states. + * + * @return ON/OFF or null if undefined + */ + State getSwitchState() { + Double value = getStateDoubleValue(STATE_ACTIVE); + if (value != null) { + if (value == 1.0) { + return OnOffType.ON; + } else if (value == 0.0) { + return OnOffType.OFF; + } else { + return UnDefType.UNDEF; + } + } + return null; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlTextState.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlTextState.java new file mode 100644 index 0000000000000..9ab9a02dd4583 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlTextState.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.StateDescription; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * A Text State type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, a text state represents a State functional block on the Miniserver + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlTextState extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlTextState(uuid); + } + + @Override + String getType() { + return "textstate"; + } + } + + /** + * A state which will receive an update of possible Text State values) + */ + private static final String STATE_TEXT_AND_ICON = "textandicon"; + + private LxControlTextState(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + ChannelUID id = addChannel("String", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_TEXT), + defaultChannelLabel, "Text state", tags, null, () -> getStateStringValue(STATE_TEXT_AND_ICON)); + addChannelStateDescription(id, new StateDescription(null, null, null, null, true, null)); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlTimedSwitch.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlTimedSwitch.java new file mode 100644 index 0000000000000..aebb3bda7ac0d --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlTimedSwitch.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import java.math.BigDecimal; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.StateDescription; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * A timed switch type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, a switch control is: + *

    + *
  • a virtual input of switch type + *
  • a push button function block + *
+ * + * @author Stephan Brunner - initial contribution + * + */ +class LxControlTimedSwitch extends LxControlPushbutton { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlTimedSwitch(uuid); + } + + @Override + String getType() { + return "timedswitch"; + } + } + + /** + * deactivationDelay - countdown until the output is deactivated. + * 0 = the output is turned off + * -1 = the output is permanently on + * otherwise it will count down from deactivationDelayTotal + */ + private static final String STATE_DEACTIVATION_DELAY = "deactivationdelay"; + + private LxControlTimedSwitch(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + ChannelUID id = addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_NUMBER), + defaultChannelLabel + " / Deactivation Delay", "Deactivation Delay", null, null, + this::getDeactivationState); + addChannelStateDescription(id, new StateDescription(new BigDecimal(-1), null, null, null, true, null)); + } + + private State getDeactivationState() { + Double deactivationValue = getStateDoubleValue(STATE_DEACTIVATION_DELAY); + if (deactivationValue != null) { + if (deactivationValue.equals(-1.0)) { + // we don't show the special value of -1 to the user, this means switch is on and delay is zero + deactivationValue = 0.0; + } + return new DecimalType(deactivationValue); + } + return null; + } + + @Override + OnOffType getSwitchState() { + /** + * 0 = the output is turned off + * -1 = the output is permanently on + * otherwise it will count down from deactivationDelayTotal + **/ + Double value = getStateDoubleValue(STATE_DEACTIVATION_DELAY); + if (value != null) { + if (value == -1.0 || value > 0.0) { // mapping + return OnOffType.ON; + } else if (value == 0) { + return OnOffType.OFF; + } + } + return null; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlTracker.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlTracker.java new file mode 100644 index 0000000000000..c4aa52e0396d9 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlTracker.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.StateDescription; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * A Tracker type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, a Tracker control represents a Tracker functional block on the Miniserver + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlTracker extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlTracker(uuid); + } + + @Override + String getType() { + return "tracker"; + } + } + + /** + * A state which will receive an update of possible Text State values) + */ + private static final String STATE_ENTRIES = "entries"; + + private LxControlTracker(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + ChannelUID id = addChannel("String", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_TEXT), + defaultChannelLabel, "Tracker", tags, null, () -> getStateStringValue(STATE_ENTRIES)); + addChannelStateDescription(id, new StateDescription(null, null, null, null, true, null)); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlUpDownAnalog.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlUpDownAnalog.java new file mode 100644 index 0000000000000..91e5006b054e7 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlUpDownAnalog.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import java.io.IOException; +import java.math.BigDecimal; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.StateDescription; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.loxone.internal.types.LxUuid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An UpDownAnalog type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, UpDownAnalog control is a virtual input that is analog and has an input type + * up-down buttons. The analog buttons are simulated as a single analog number value. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlUpDownAnalog extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlUpDownAnalog(uuid); + } + + @Override + String getType() { + return "updownanalog"; + } + } + + private static final String STATE_VALUE = "value"; + private static final String STATE_ERROR = "error"; + + private final Logger logger = LoggerFactory.getLogger(LxControlUpDownAnalog.class); + + private Double minValue; + private Double maxValue; + private ChannelUID channelId; + + LxControlUpDownAnalog(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + initialize(config, "Up/Down Analog"); + } + + void initialize(LxControlConfig config, String channelDescription) { + super.initialize(config); + channelId = addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_NUMBER), + defaultChannelLabel, channelDescription, tags, this::handleCommands, this::getChannelState); + if (details != null && details.min != null && details.max != null) { + if (details.min <= details.max) { + minValue = details.min; + maxValue = details.max; + if (details.step != null) { + addChannelStateDescription(channelId, + new StateDescription(new BigDecimal(minValue), new BigDecimal(maxValue), + new BigDecimal(details.step), details.format != null ? details.format : "%.1f", + false, null)); + } + } else { + logger.warn("Received min value > max value: {}, {}", minValue, maxValue); + } + } + } + + private void handleCommands(Command command) throws IOException { + if (command instanceof DecimalType) { + Double value = ((DecimalType) command).doubleValue(); + if (minValue != null && maxValue != null && value >= minValue && value <= maxValue) { + sendAction(value.toString()); + } else { + // we'll update the state value to reflect current real value that has not been changed + setChannelState(channelId, getChannelState()); + } + } + } + + private State getChannelState() { + Double error = getStateDoubleValue(STATE_ERROR); + if (error == null || error == 0.0) { + Double value = getStateDoubleValue(STATE_VALUE); + if (value != null) { + if (minValue != null && maxValue != null && (minValue > value || maxValue < value)) { + return null; + } + return new DecimalType(value); + } + } else { + return UnDefType.UNDEF; + } + return null; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlUpDownDigital.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlUpDownDigital.java new file mode 100644 index 0000000000000..d922822da00ea --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlUpDownDigital.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import java.io.IOException; + +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * An UpDownDigital type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, UpDownDigital control is a virtual input that is digital and has an input type + * up-down buttons. Buttons act like on an integrated up/down arrows switch - only one direction can be active at a + * time. Pushing button in one direction will automatically set the other direction to off. + * This control has no states and can only accept commands. Only up/down on/off commands are generated. Pulse + * commands are not supported, because of lack of corresponding feature in openHAB. Pulse can be emulated by quickly + * alternating between ON and OFF commands. Because this control has no states, there will be no openHAB state changes + * triggered by the Miniserver and we need to take care of updating the states inside this class. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlUpDownDigital extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlUpDownDigital(uuid); + } + + @Override + String getType() { + return "updowndigital"; + } + } + + private static final String CMD_UP_ON = "UpOn"; + private static final String CMD_UP_OFF = "UpOff"; + private static final String CMD_DOWN_ON = "DownOn"; + private static final String CMD_DOWN_OFF = "DownOff"; + + private OnOffType upState = OnOffType.OFF; + private OnOffType downState = OnOffType.OFF; + private ChannelUID upChannelId; + private ChannelUID downChannelId; + + LxControlUpDownDigital(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + initialize(config, " / Up", "Up/Down Digital: Up", " / Down", "Up/Down Digital: Down"); + } + + void initialize(LxControlConfig config, String upChannelLabel, String upChannelDescription, String downChannelLabel, + String downChannelDescription) { + super.initialize(config); + upChannelId = addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH), + defaultChannelLabel + upChannelLabel, upChannelDescription, tags, this::handleUpCommands, + () -> upState); + downChannelId = addChannel("Switch", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_SWITCH), + defaultChannelLabel + downChannelLabel, downChannelDescription, tags, this::handleDownCommands, + () -> downState); + } + + private void handleUpCommands(Command command) throws IOException { + if (command instanceof OnOffType) { + if ((OnOffType) command == OnOffType.ON && upState == OnOffType.OFF) { + setStates(OnOffType.ON, OnOffType.OFF); + sendAction(CMD_UP_ON); + } else if (upState == OnOffType.ON) { + setStates(OnOffType.OFF, OnOffType.OFF); + sendAction(CMD_UP_OFF); + } + } + } + + private void handleDownCommands(Command command) throws IOException { + if (command instanceof OnOffType) { + if ((OnOffType) command == OnOffType.ON && downState == OnOffType.OFF) { + setStates(OnOffType.OFF, OnOffType.ON); + sendAction(CMD_DOWN_ON); + } else if (downState == OnOffType.ON) { + setStates(OnOffType.OFF, OnOffType.OFF); + sendAction(CMD_DOWN_OFF); + } + } + } + + private void setStates(OnOffType upState, OnOffType downState) { + this.upState = upState; + this.downState = downState; + setChannelState(upChannelId, upState); + setChannelState(downChannelId, downState); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlValueSelector.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlValueSelector.java new file mode 100644 index 0000000000000..fc2c61617e5fe --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlValueSelector.java @@ -0,0 +1,201 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import java.io.IOException; +import java.math.BigDecimal; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.StateDescription; +import org.openhab.binding.loxone.internal.types.LxState; +import org.openhab.binding.loxone.internal.types.LxUuid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A ValueSelector type of control on Loxone Miniserver. + *

+ * According to Loxone API documentation, this control covers: Push-button +/- and Push-button + functional blocks. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlValueSelector extends LxControl { + + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlValueSelector(uuid); + } + + @Override + String getType() { + return "valueselector"; + } + } + + private static final String STATE_VALUE = "value"; + private static final String STATE_MIN = "min"; + private static final String STATE_MAX = "max"; + private static final String STATE_STEP = "step"; + + private final Logger logger = LoggerFactory.getLogger(LxControlValueSelector.class); + + private Boolean increaseOnly = false; + private String format = "%.1f"; + private Double minValue; + private Double maxValue; + private Double stepValue; + private ChannelUID channelId; + private ChannelUID numberChannelId; + + private LxControlValueSelector(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + channelId = addChannel("Dimmer", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_DIMMER), + defaultChannelLabel, "Value Selector", tags, this::handleCommand, this::getChannelState); + numberChannelId = addChannel("Number", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_NUMBER), + defaultChannelLabel + " / Number", "Value Selector by number", tags, this::handleNumberCommand, + this::getChannelNumberState); + + if (details != null) { + if (details.format != null) { + this.format = details.format; + } + if (details.increaseOnly != null) { + this.increaseOnly = details.increaseOnly; + } + } + } + + private void handleCommand(Command command) throws IOException { + if (minValue == null || maxValue == null || minValue >= maxValue) { + logger.debug("Value selector min or max value missing or min>max."); + return; + } + if (command instanceof OnOffType) { + if ((OnOffType) command == OnOffType.ON) { + sendAction(maxValue.toString()); + } else { + sendAction(minValue.toString()); + } + } else if (command instanceof IncreaseDecreaseType) { + if (stepValue == null) { + logger.debug("Value selector step value missing."); + return; + } + IncreaseDecreaseType type = (IncreaseDecreaseType) command; + if (increaseOnly != null && type == IncreaseDecreaseType.DECREASE && increaseOnly) { + logger.debug("Value selector configured to allow increase only."); + return; + } + Double currentValue = getStateDoubleValue(STATE_VALUE); + if (currentValue != null) { + Double nextValue = currentValue + (type == IncreaseDecreaseType.INCREASE ? stepValue : -stepValue); + if (nextValue > maxValue) { + nextValue = maxValue; + } + if (nextValue < minValue) { + nextValue = minValue; + } + sendAction(nextValue.toString()); + } + } else if (command instanceof PercentType) { + Double value = ((PercentType) command).doubleValue() * (maxValue - minValue) / 100.0 + minValue; + sendAction(value.toString()); + } + } + + private void handleNumberCommand(Command command) throws IOException { + if (minValue == null || maxValue == null || minValue >= maxValue) { + logger.debug("Value selector min or max value missing or min>max."); + return; + } + if (command instanceof DecimalType) { + Double value = ((DecimalType) command).doubleValue(); + if (value < minValue || value > maxValue) { + logger.debug("Value {} out of {}-{} range", value, minValue, maxValue); + return; + } + sendAction(value.toString()); + } + } + + private PercentType getChannelState() { + Double value = getStateDoubleValue(STATE_VALUE); + if (value != null && minValue != null && maxValue != null && minValue <= value && value <= maxValue) { + value = ((value - minValue) * 100.0) / (maxValue - minValue); + return new PercentType(value.intValue()); + } + return null; + } + + private DecimalType getChannelNumberState() { + Double value = getStateDoubleValue(STATE_VALUE); + if (value != null && minValue != null && maxValue != null && minValue <= value && value <= maxValue) { + return new DecimalType(value); + } + return null; + } + + /** + * Get dynamic updates to the minimum, maximum and step values. Sets a new state description if these values are + * available. If no updates to the min/max/step, calls parent class method to update selector value in the + * framework. + * + * @param state state update from the Miniserver + */ + @Override + public void onStateChange(LxState state) { + String stateName = state.getName(); + Object value = state.getStateValue(); + try { + if (value instanceof Double) { + if (STATE_MIN.equals(stateName)) { + minValue = (Double) value; + } else if (STATE_MAX.equals(stateName)) { + maxValue = (Double) value; + } else if (STATE_STEP.equals(stateName)) { + stepValue = (Double) value; + if (stepValue <= 0) { + logger.warn("Value selector step value <= 0: {}", stepValue); + stepValue = null; + } + } else { + super.onStateChange(state); + return; + } + } + } catch (NumberFormatException e) { + logger.debug("Error parsing value for state {}: {}", stateName, e.getMessage()); + } + if (minValue != null && maxValue != null && stepValue != null && minValue < maxValue) { + StateDescription description = new StateDescription(new BigDecimal(minValue), new BigDecimal(maxValue), + new BigDecimal(stepValue), format, false, null); + addChannelStateDescription(channelId, description); + addChannelStateDescription(numberChannelId, description); + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlWebPage.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlWebPage.java new file mode 100644 index 0000000000000..feabcbe451feb --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/controls/LxControlWebPage.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.openhab.binding.loxone.internal.LxBindingConstants.*; + +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.StateDescription; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * A web page type of control on Loxone Miniserver. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlWebPage extends LxControl { + static class Factory extends LxControlInstance { + @Override + LxControl create(LxUuid uuid) { + return new LxControlWebPage(uuid); + } + + @Override + String getType() { + return "webpage"; + } + } + + private StringType url; + private StringType urlHd; + + private LxControlWebPage(LxUuid uuid) { + super(uuid); + } + + @Override + public void initialize(LxControlConfig config) { + super.initialize(config); + if (details != null) { + if (details.url != null) { + url = new StringType(details.url); + } + if (details.urlHd != null) { + urlHd = new StringType(details.urlHd); + } + } + ChannelUID c1 = addChannel("String", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_TEXT), + defaultChannelLabel + " / URL", "Low resolution URL", tags, null, () -> url); + addChannelStateDescription(c1, new StateDescription(null, null, null, null, true, null)); + + ChannelUID c2 = addChannel("String", new ChannelTypeUID(BINDING_ID, MINISERVER_CHANNEL_TYPE_RO_TEXT), + defaultChannelLabel + " / URL HD", "High resolution URL", tags, null, () -> urlHd); + addChannelStateDescription(c2, new StateDescription(null, null, null, null, true, null)); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/security/LxWsSecurity.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/security/LxWsSecurity.java new file mode 100644 index 0000000000000..d287a1ca7edd2 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/security/LxWsSecurity.java @@ -0,0 +1,221 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.security; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.BiConsumer; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.eclipse.smarthome.core.util.HexUtils; +import org.openhab.binding.loxone.internal.LxServerHandler; +import org.openhab.binding.loxone.internal.LxServerHandlerApi; +import org.openhab.binding.loxone.internal.LxWebSocket; +import org.openhab.binding.loxone.internal.types.LxErrorCode; +import org.openhab.binding.loxone.internal.types.LxResponse; +import org.openhab.binding.loxone.internal.types.LxWsSecurityType; + +/** + * Security abstract class providing authentication and encryption services. + * Used by the {@link LxServerHandler} during connection establishment to authenticate user and during message exchange + * for encryption and decryption or the messages. + * + * @author Pawel Pieczul - initial contribution + * + */ +public abstract class LxWsSecurity { + final int debugId; + final String user; + final String password; + final LxWebSocket socket; + final LxServerHandlerApi thingHandler; + + LxErrorCode reason; + + private String details; + private boolean cancel = false; + private final Lock authenticationLock = new ReentrantLock(); + + /** + * Create an authentication instance. + * + * @param debugId instance of the client used for debugging purposes only + * @param thingHandler API to the thing handler + * @param socket websocket to perform communication with Miniserver + * @param user user to authenticate + * @param password password to authenticate + */ + LxWsSecurity(int debugId, LxServerHandlerApi thingHandler, LxWebSocket socket, String user, String password) { + this.debugId = debugId; + this.thingHandler = thingHandler; + this.socket = socket; + this.user = user; + this.password = password; + } + + /** + * Initiate user authentication. This method will return immediately and authentication will be done in a separate + * thread asynchronously. On successful or unsuccessful completion, a provided callback will be called with + * information about failed reason and details of failure. In case of success, the reason value will be + * {@link LxErrorCode#OK} + * Only one authentication can run in parallel and must be performed sequentially (create no more threads). + * + * @param doneCallback callback to execute when authentication is finished or failed + */ + public void authenticate(BiConsumer doneCallback) { + Runnable init = () -> { + authenticationLock.lock(); + try { + execute(); + doneCallback.accept(reason, details); + } finally { + authenticationLock.unlock(); + } + }; + new Thread(init).start(); + } + + /** + * Perform user authentication using a specific authentication algorithm. + * This method will be executed in a dedicated thread to allow sending synchronous messages to the Miniserver. + * + * @return true when authentication granted + */ + abstract boolean execute(); + + /** + * Cancel authentication procedure and any pending activities. + * It is supposed to be overridden by implementing classes. + */ + public void cancel() { + cancel = true; + } + + /** + * Check a response received from the Miniserver for errors, interpret it and store the results in class fields. + * + * @param response response received from the Miniserver + * @return {@link LxErrorCode#OK} when response is correct or a specific {@link LxErrorCode} + */ + boolean checkResponse(LxResponse response) { + if (response == null || response.subResponse == null || cancel) { + reason = LxErrorCode.COMMUNICATION_ERROR; + return false; + } + reason = response.getResponseCode(); + return (reason == LxErrorCode.OK); + } + + /** + * Hash string (e.g. containing user name and password or token) according to the algorithm required by the + * Miniserver. + * + * @param string string to be hashed + * @param hashKeyHex hash key received from the Miniserver in hex format + * @return hashed string or null if failed + */ + String hashString(String string, String hashKeyHex) { + if (string == null || hashKeyHex == null) { + return null; + } + try { + byte[] hashKeyBytes = Hex.decodeHex(hashKeyHex.toCharArray()); + SecretKeySpec signKey = new SecretKeySpec(hashKeyBytes, "HmacSHA1"); + Mac mac = Mac.getInstance("HmacSHA1"); + mac.init(signKey); + byte[] rawData = mac.doFinal(string.getBytes()); + return HexUtils.bytesToHex(rawData); + } catch (DecoderException | NoSuchAlgorithmException | InvalidKeyException e) { + return null; + } + } + + /** + * Encrypt string using current encryption algorithm. + * + * @param string input string to encrypt + * @return encrypted string + */ + public String encrypt(String string) { + // by default no encryption + return string; + } + + /** + * Check if control is encrypted and decrypt it using current decryption algorithm. + * If control is not encrypted or decryption is not available or not ready, the control should be returned in its + * original form. + * + * @param control control to be decrypted + * @return decrypted control or original control in case decryption is unavailable, control is not encrypted or + * other issue occurred + */ + public String decryptControl(String control) { + // by default no decryption + return control; + } + + /** + * Set error code and return false. It is used to report detailed error information from inside the algorithms. + * + * @param reason reason for failure + * @param details details of the failure + * @return always false + */ + boolean setError(LxErrorCode reason, String details) { + if (reason != null) { + this.reason = reason; + } + if (details != null) { + this.details = details; + } + return false; + } + + /** + * Create an authentication instance. + * + * @param type type of security algorithm + * @param swVersion Miniserver's software version or null if unknown + * @param debugId instance of the client used for debugging purposes only + * @param thingHandler API to the thing handler + * @param socket websocket to perform communication with Miniserver + * @param user user to authenticate + * @param password password to authenticate + * @return created security object + */ + public static LxWsSecurity create(LxWsSecurityType type, String swVersion, int debugId, + LxServerHandlerApi thingHandler, LxWebSocket socket, String user, String password) { + LxWsSecurityType securityType = type; + if (securityType == LxWsSecurityType.AUTO && swVersion != null) { + String[] versions = swVersion.split("[.]"); + if (versions != null && versions.length > 0 && Integer.parseInt(versions[0]) <= 8) { + securityType = LxWsSecurityType.HASH; + } else { + securityType = LxWsSecurityType.TOKEN; + } + } + if (securityType == LxWsSecurityType.HASH) { + return new LxWsSecurityHash(debugId, thingHandler, socket, user, password); + } else { + return new LxWsSecurityToken(debugId, thingHandler, socket, user, password); + } + } + +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/security/LxWsSecurityHash.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/security/LxWsSecurityHash.java new file mode 100644 index 0000000000000..79e42f7ac3bab --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/security/LxWsSecurityHash.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.security; + +import org.openhab.binding.loxone.internal.LxServerHandlerApi; +import org.openhab.binding.loxone.internal.LxWebSocket; +import org.openhab.binding.loxone.internal.types.LxErrorCode; +import org.openhab.binding.loxone.internal.types.LxResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A hash-based authentication algorithm. No encryption and decryption supported. + * The algorithm computes a HMAC-SHA1 hash from the user name and password, using a key received from the Miniserver. + * This hash is sent to the Miniserver to authorize the user. + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxWsSecurityHash extends LxWsSecurity { + + private static final String CMD_GET_KEY = "jdev/sys/getkey"; + private static final String CMD_AUTHENTICATE = "authenticate/"; + + private final Logger logger = LoggerFactory.getLogger(LxWsSecurityHash.class); + + /** + * Create a hash-based authentication instance. + * + * @param debugId instance of the client used for debugging purposes only + * @param thingHandler API to the thing handler + * @param socket websocket to perform communication with Miniserver + * @param user user to authenticate + * @param password password to authenticate + */ + LxWsSecurityHash(int debugId, LxServerHandlerApi thingHandler, LxWebSocket socket, String user, String password) { + super(debugId, thingHandler, socket, user, password); + } + + @Override + boolean execute() { + logger.debug("[{}] Starting hash-based authentication.", debugId); + if (password == null || password.isEmpty()) { + return setError(LxErrorCode.USER_UNAUTHORIZED, "Enter password for hash-based authentication."); + } + LxResponse resp = socket.sendCmdWithResp(CMD_GET_KEY, true, false); + if (!checkResponse(resp)) { + return false; + } + String hash = hashString(user + ":" + password, resp.getValueAsString()); + if (hash == null) { + return setError(LxErrorCode.INTERNAL_ERROR, "Error hashing credentials."); + } + String cmd = CMD_AUTHENTICATE + hash; + if (!checkResponse(socket.sendCmdWithResp(cmd, true, false))) { + return false; + } + logger.debug("[{}] Authenticated - hash based authentication.", debugId); + return true; + } +} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsSecurityToken.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/security/LxWsSecurityToken.java similarity index 82% rename from addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsSecurityToken.java rename to bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/security/LxWsSecurityToken.java index 2515232248168..fb88b69f23096 100644 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsSecurityToken.java +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/security/LxWsSecurityToken.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.loxone.internal.core; +package org.openhab.binding.loxone.internal.security; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; @@ -44,20 +44,17 @@ import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; -import org.apache.commons.codec.binary.Hex; import org.eclipse.smarthome.core.common.ThreadPoolManager; import org.eclipse.smarthome.core.id.InstanceUUID; -import org.openhab.binding.loxone.internal.core.LxJsonResponse.LxJsonKeySalt; -import org.openhab.binding.loxone.internal.core.LxJsonResponse.LxJsonSubResponse; -import org.openhab.binding.loxone.internal.core.LxJsonResponse.LxJsonToken; -import org.openhab.binding.loxone.internal.core.LxServer.Configuration; -import org.openhab.binding.loxone.internal.core.LxWsClient.LxWebSocket; +import org.eclipse.smarthome.core.util.HexUtils; +import org.openhab.binding.loxone.internal.LxServerHandlerApi; +import org.openhab.binding.loxone.internal.LxWebSocket; +import org.openhab.binding.loxone.internal.types.LxErrorCode; +import org.openhab.binding.loxone.internal.types.LxResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.JsonElement; import com.google.gson.JsonParseException; -import com.google.gson.JsonSyntaxException; /** * A token-based authentication algorithm with AES-256 encryption and decryption. @@ -79,6 +76,35 @@ * */ class LxWsSecurityToken extends LxWsSecurity { + /** + * A sub-response value structure that is received as a response to get key-salt request command sent to the + * Miniserver during authentication procedure. + * + * @author Pawel Pieczul - initial contribution + * + */ + private class LxResponseKeySalt { + String key; + String salt; + } + + /** + * A sub-response value structure that is received as a response to token request or token update command sent to + * the Miniserver during authentication procedure. + * + * @author Pawel Pieczul - initial contribution + * + */ + private class LxResponseToken { + String token; + Integer validUntil; + Boolean unsecurePass; + @SuppressWarnings("unused") + String key; + @SuppressWarnings("unused") + Integer tokenRights; + } + // length of salt used for encrypting commands private static final int SALT_BYTES = 16; // after salt aged or reached max use count, a new salt will be generated @@ -98,9 +124,9 @@ class LxWsSecurityToken extends LxWsSecurity { // AES encryption random initialization vector length private static final int IV_LENGTH_BYTES = 16; + private static final String CMD_GET_KEY_AND_SALT = "jdev/sys/getkey2/"; private static final String CMD_GET_PUBLIC_KEY = "jdev/sys/getPublicKey"; private static final String CMD_KEY_EXCHANGE = "jdev/sys/keyexchange/"; - private static final String CMD_GET_KEY_AND_SALT = "jdev/sys/getkey2/"; private static final String CMD_REQUEST_TOKEN = "jdev/sys/gettoken/"; private static final String CMD_GET_KEY = "jdev/sys/getkey"; private static final String CMD_AUTH_WITH_TOKEN = "authwithtoken/"; @@ -121,7 +147,7 @@ class LxWsSecurityToken extends LxWsSecurity { private String token; private int tokenRefreshRetryCount; private ScheduledFuture tokenRefreshTimer; - private Lock tokenRefreshLock = new ReentrantLock(); + private final Lock tokenRefreshLock = new ReentrantLock(); private final byte[] initVector = new byte[IV_LENGTH_BYTES]; private final Logger logger = LoggerFactory.getLogger(LxWsSecurityToken.class); @@ -132,13 +158,13 @@ class LxWsSecurityToken extends LxWsSecurity { * Create a token-based authentication instance. * * @param debugId instance of the client used for debugging purposes only - * @param configuration configuration object for getting and setting custom properties (e.g. token) + * @param thingHandler API to the thing handler * @param socket websocket to perform communication with Miniserver * @param user user to authenticate * @param password password to authenticate */ - LxWsSecurityToken(int debugId, Configuration configuration, LxWebSocket socket, String user, String password) { - super(debugId, configuration, socket, user, password); + LxWsSecurityToken(int debugId, LxServerHandlerApi thingHandler, LxWebSocket socket, String user, String password) { + super(debugId, thingHandler, socket, user, password); } @Override @@ -148,16 +174,16 @@ boolean execute() { return false; } if ((token == null || token.isEmpty()) && (password == null || password.isEmpty())) { - return setError(LxOfflineReason.UNAUTHORIZED, "Enter password to acquire token."); + return setError(LxErrorCode.USER_UNAUTHORIZED, "Enter password to acquire token."); } // Get Miniserver's public key - must be over http, not websocket String msg = socket.httpGet(CMD_GET_PUBLIC_KEY); - LxJsonSubResponse resp = socket.getSubResponse(msg); + LxResponse resp = socket.getResponse(msg); if (resp == null) { - return setError(LxOfflineReason.COMMUNICATION_ERROR, "Get public key failed - null response."); + return setError(LxErrorCode.COMMUNICATION_ERROR, "Get public key failed - null response."); } // RSA cipher to encrypt our AES-256 key using Miniserver's public key - Cipher rsaCipher = getRsaCipher(resp.value.getAsString()); + Cipher rsaCipher = getRsaCipher(resp.getValueAsString()); if (rsaCipher == null) { return false; } @@ -190,7 +216,7 @@ boolean execute() { } @Override - String encrypt(String command) { + public String encrypt(String command) { if (!encryptionReady) { return command; } @@ -223,7 +249,7 @@ String encrypt(String command) { } @Override - String decryptControl(String control) { + public String decryptControl(String control) { String string = control; if (!encryptionReady || !string.startsWith(CMD_ENCRYPT_CMD)) { return string; @@ -248,7 +274,7 @@ String decryptControl(String control) { } @Override - void cancel() { + public void cancel() { super.cancel(); tokenRefreshLock.lock(); try { @@ -266,7 +292,7 @@ private boolean initialize() { encryptionReady = false; tokenRefreshRetryCount = TOKEN_REFRESH_RETRY_COUNT; if (Cipher.getMaxAllowedKeyLength("AES") < 256) { - return setError(LxOfflineReason.INTERNAL_ERROR, + return setError(LxErrorCode.INTERNAL_ERROR, "Enable Java cryptography unlimited strength (see binding doc)."); } // generate a random key for the session @@ -284,14 +310,14 @@ private boolean initialize() { aesDecryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); aesDecryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivSpec); // get token value from configuration storage - token = (String) configuration.get(SETTINGS_TOKEN); + token = thingHandler.getSetting(SETTINGS_TOKEN); logger.debug("[{}] Retrieved token value: {}", debugId, token); } catch (InvalidParameterException e) { - return setError(LxOfflineReason.INTERNAL_ERROR, "Invalid parameter: " + e.getMessage()); + return setError(LxErrorCode.INTERNAL_ERROR, "Invalid parameter: " + e.getMessage()); } catch (NoSuchAlgorithmException e) { - return setError(LxOfflineReason.INTERNAL_ERROR, "AES not supported on platform."); + return setError(LxErrorCode.INTERNAL_ERROR, "AES not supported on platform."); } catch (InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException e) { - return setError(LxOfflineReason.INTERNAL_ERROR, "AES cipher initialization failed."); + return setError(LxErrorCode.INTERNAL_ERROR, "AES cipher initialization failed."); } return true; } @@ -309,29 +335,29 @@ private Cipher getRsaCipher(String key) { logger.debug("[{}] Initialized RSA public key cipher", debugId); return cipher; } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeySpecException e) { - setError(LxOfflineReason.INTERNAL_ERROR, "Exception enabling RSA cipher: " + e.getMessage()); + setError(LxErrorCode.INTERNAL_ERROR, "Exception enabling RSA cipher: " + e.getMessage()); return null; } } private byte[] generateSessionKey(Cipher rsaCipher) { - String key = Hex.encodeHexString(aesKey.getEncoded()) + ":" + Hex.encodeHexString(initVector); + String key = HexUtils.bytesToHex(aesKey.getEncoded()) + ":" + HexUtils.bytesToHex(initVector); try { byte[] sessionKey = rsaCipher.doFinal(key.getBytes()); - logger.debug("[{}] Generated session key: {}", debugId, Hex.encodeHexString(sessionKey)); + logger.debug("[{}] Generated session key: {}", debugId, HexUtils.bytesToHex(sessionKey)); return sessionKey; } catch (IllegalBlockSizeException | BadPaddingException e) { - setError(LxOfflineReason.INTERNAL_ERROR, "Exception encrypting session key: " + e.getMessage()); + setError(LxErrorCode.INTERNAL_ERROR, "Exception encrypting session key: " + e.getMessage()); return null; } } - private String hashCredentials(LxJsonKeySalt keySalt) { + private String hashCredentials(LxResponseKeySalt keySalt) { try { MessageDigest msgDigest = MessageDigest.getInstance("SHA-1"); String pwdHashStr = password + ":" + keySalt.salt; byte[] rawData = msgDigest.digest(pwdHashStr.getBytes(StandardCharsets.UTF_8)); - String pwdHash = Hex.encodeHexString(rawData).toUpperCase(); + String pwdHash = HexUtils.bytesToHex(rawData).toUpperCase(); logger.debug("[{}] PWDHASH: {}", debugId, pwdHash); return hashString(user + ":" + pwdHash, keySalt.key); } catch (NoSuchAlgorithmException e) { @@ -342,16 +368,15 @@ private String hashCredentials(LxJsonKeySalt keySalt) { private boolean acquireToken() { // Get Miniserver hash key and salt - this command should be encrypted - LxJsonSubResponse resp = socket.sendCmdWithResp(CMD_GET_KEY_AND_SALT + user, true, true); + LxResponse resp = socket.sendCmdWithResp(CMD_GET_KEY_AND_SALT + user, true, true); if (!checkResponse(resp)) { return setError(null, "Hash key/salt get failed."); } - LxJsonKeySalt keySalt; - try { - keySalt = socket.getGson().fromJson(resp.value, LxJsonKeySalt.class); - } catch (JsonSyntaxException e) { - return setError(null, "Error parsing hash key/salt json: " + resp.value); + LxResponseKeySalt keySalt = resp.getValueAs(thingHandler.getGson(), LxResponseKeySalt.class); + if (keySalt == null) { + return setError(null, "Error parsing hash key/salt json: " + resp.getValueAsString()); } + logger.debug("[{}] Hash key: {}, salt: {}", debugId, keySalt.key, keySalt.salt); // Hash user name, password, key and salt String hash = hashCredentials(keySalt); @@ -365,25 +390,24 @@ private boolean acquireToken() { if (!checkResponse(resp)) { return setError(null, "Request token failed."); } - if (resp.value == null) { - return setError(LxOfflineReason.INTERNAL_ERROR, "Token response value is null."); - } try { - LxJsonToken tokenResponse = parseTokenResponse(resp.value); + LxResponseToken tokenResponse = parseTokenResponse(resp); if (tokenResponse == null) { return false; } token = tokenResponse.token; if (token == null) { - return setError(LxOfflineReason.INTERNAL_ERROR, "Received null token."); + return setError(LxErrorCode.INTERNAL_ERROR, "Received null token."); } } catch (JsonParseException e) { - return setError(LxOfflineReason.INTERNAL_ERROR, "Error parsing token response: " + e.getMessage()); + return setError(LxErrorCode.INTERNAL_ERROR, "Error parsing token response: " + e.getMessage()); } + persistToken(); logger.debug("[{}] Token acquired.", debugId); return true; + } private boolean useToken() { @@ -391,27 +415,27 @@ private boolean useToken() { if (hash == null) { return false; } - LxJsonSubResponse resp = socket.sendCmdWithResp(CMD_AUTH_WITH_TOKEN + hash + "/" + user, true, true); + LxResponse resp = socket.sendCmdWithResp(CMD_AUTH_WITH_TOKEN + hash + "/" + user, true, true); if (!checkResponse(resp)) { - if (reason == LxOfflineReason.UNAUTHORIZED) { + if (reason == LxErrorCode.USER_UNAUTHORIZED) { token = null; persistToken(); return setError(null, "Enter password to generate a new token."); } - return setError(null, "Token-based authentication failed with code: " + resp.code); + return setError(null, "Token-based authentication failed."); } - parseTokenResponse(resp.value); + parseTokenResponse(resp); return true; } private String hashToken() { - LxJsonSubResponse resp = socket.sendCmdWithResp(CMD_GET_KEY, true, true); + LxResponse resp = socket.sendCmdWithResp(CMD_GET_KEY, true, true); if (!checkResponse(resp)) { setError(null, "Get key command failed."); return null; } try { - String hashKey = resp.value.getAsString(); + String hashKey = resp.getValueAsString(); // here is a difference to the API spec, which says the string to hash is "user:token", but this is "token" String hash = hashString(token, hashKey); if (hash == null) { @@ -419,7 +443,7 @@ private String hashToken() { } return hash; } catch (ClassCastException | IllegalStateException e) { - setError(LxOfflineReason.INTERNAL_ERROR, "Error parsing Miniserver key."); + setError(LxErrorCode.INTERNAL_ERROR, "Error parsing Miniserver key."); return null; } } @@ -430,15 +454,13 @@ private void persistToken() { if (token != null) { properties.put(SETTINGS_PASSWORD, null); } - configuration.setSettings(properties); + thingHandler.setSettings(properties); } - private LxJsonToken parseTokenResponse(JsonElement response) { - LxJsonToken tokenResponse; - try { - tokenResponse = socket.getGson().fromJson(response, LxJsonToken.class); - } catch (JsonParseException e) { - setError(LxOfflineReason.INTERNAL_ERROR, "Error parsing token response: " + e.getMessage()); + private LxResponseToken parseTokenResponse(LxResponse response) { + LxResponseToken tokenResponse = response.getValueAs(thingHandler.getGson(), LxResponseToken.class); + if (tokenResponse == null) { + setError(LxErrorCode.INTERNAL_ERROR, "Error parsing token response."); return null; } Boolean unsecurePass = tokenResponse.unsecurePass; @@ -487,10 +509,10 @@ private void refreshToken() { tokenRefreshTimer = null; String hash = hashToken(); if (hash != null) { - LxJsonSubResponse resp = socket.sendCmdWithResp(CMD_REFRESH_TOKEN + hash + "/" + user, true, true); + LxResponse resp = socket.sendCmdWithResp(CMD_REFRESH_TOKEN + hash + "/" + user, true, true); if (checkResponse(resp)) { logger.debug("[{}] Successful token refresh.", debugId); - parseTokenResponse(resp.value); + parseTokenResponse(resp); return; } } @@ -518,7 +540,7 @@ private void scheduleTokenRefresh(long delay) { private String generateSalt() { byte[] bytes = new byte[SALT_BYTES]; secureRandom.nextBytes(bytes); - String salt = Hex.encodeHexString(bytes); + String salt = HexUtils.bytesToHex(bytes); try { salt = URLEncoder.encode(salt, "UTF-8"); } catch (UnsupportedEncodingException e) { diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxCategory.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxCategory.java new file mode 100644 index 0000000000000..d6feada393be9 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxCategory.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.types; + +import org.openhab.binding.loxone.internal.controls.LxControl; + +/** + * Category of Loxone Miniserver's {@link LxControl} object. + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxCategory extends LxContainer { + + /** + * Various categories that Loxone Miniserver's control can belong to. + * + * @author Pawel Pieczul - initial contribution + */ + public enum CategoryType { + /** + * Category for lights + */ + LIGHTS, + /** + * Category for shading / rollershutter / blinds + */ + SHADING, + /** + * Unknown category + */ + UNDEFINED + } + + private String type; // deserialized from JSON + private CategoryType catType; + + /** + * Obtain the type of this category + * + * @return type of category + */ + public CategoryType getType() { + if (catType == null && type != null) { + String tl = type.toLowerCase(); + if (tl.equals("lights")) { + catType = CategoryType.LIGHTS; + } else if (tl.equals("shading")) { + catType = CategoryType.SHADING; + } else { + catType = CategoryType.UNDEFINED; + } + } + return catType; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxConfig.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxConfig.java new file mode 100644 index 0000000000000..95de43cf759a4 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxConfig.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.types; + +import java.lang.reflect.Type; +import java.util.Map; + +import org.openhab.binding.loxone.internal.LxServerHandlerApi; +import org.openhab.binding.loxone.internal.controls.LxControl; +import org.openhab.binding.loxone.internal.controls.LxControl.LxControlConfig; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.annotations.SerializedName; + +/** + * A structure of JSON file http://miniserver/data/LoxAPP3.json used for parsing it with Gson library. + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxConfig { + + private Map rooms; + @SerializedName("cats") + private Map categories; + public Map controls; + + public class LxServerInfo { + public String serialNr; + public String location; + public String roomTitle; + public String catTitle; + public String msName; + public String projectName; + public String remoteUrl; + public String swVersion; + public String macAddress; + } + + public LxServerInfo msInfo; + + public void finalize(LxServerHandlerApi thingHandler) { + rooms.values().removeIf(o -> (o == null || o.getUuid() == null)); + categories.values().removeIf(o -> (o == null || o.getUuid() == null)); + controls.values().removeIf(c -> c == null || c.isSecured()); + controls.values().forEach(c -> c.initialize( + new LxControlConfig(thingHandler, rooms.get(c.getRoomUuid()), categories.get(c.getCategoryUuid())))); + } + + public static T deserializeObject(JsonObject parent, String name, Type type, + JsonDeserializationContext context) { + JsonElement element = parent.get(name); + if (element != null) { + return context.deserialize(element, type); + } + return null; + } + + public static String deserializeString(JsonObject parent, String name) { + JsonElement element = parent.get(name); + if (element != null) { + return element.getAsString(); + } + return null; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxContainer.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxContainer.java new file mode 100644 index 0000000000000..b3dc5408d4131 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxContainer.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.types; + +import java.util.HashSet; +import java.util.Set; + +import org.openhab.binding.loxone.internal.controls.LxControl; + +/** + * Container on Loxone Miniserver that groups {@link LxControl} objects. + *

+ * Examples of containers are rooms and categories. + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxContainer { + private LxUuid uuid; // set by JSON deserialization + private String name; // set by JSON deserialization + private final Set controls; + + /** + * Create a new container with given uuid and name + */ + LxContainer() { + controls = new HashSet<>(); + } + + /** + * Obtain container's current name + * + * @return + * container's current name + */ + public String getName() { + return name; + } + + /** + * Obtain container's UUID (assigned by Loxone Miniserver) + * + * @return + * container's UUID + */ + public LxUuid getUuid() { + return uuid; + } + + /** + * Add a new control to this container + * + * @param control control to be added + */ + public void addControl(LxControl control) { + controls.add(control); + } + + /** + * Removes a control from the container + * + * @param control control object to remove from the container + * @return true if control object existed in the container and was removed + */ + public boolean removeControl(LxControl control) { + return controls.remove(control); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxErrorCode.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxErrorCode.java new file mode 100644 index 0000000000000..4ce9dea9878c7 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxErrorCode.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.types; + +/** + * Reasons why Miniserver may be not reachable + * + * @author Pawel Pieczul - initial contribution + * + */ +public enum LxErrorCode { + /** + * No error at all + */ + OK, + /** + * User name or password incorrect or user not authorized + */ + USER_UNAUTHORIZED, + /** + * Too many failed login attempts and server's temporary ban of the user + */ + TOO_MANY_FAILED_LOGIN_ATTEMPTS, + /** + * Communication error with the Miniserv + */ + COMMUNICATION_ERROR, + /** + * Timeout of user authentication procedure + */ + USER_AUTHENTICATION_TIMEOUT, + /** + * No activity from Miniserver's client + */ + WEBSOCKET_IDLE_TIMEOUT, + /** + * Internal error, sign of something wrong with the program + */ + INTERNAL_ERROR, + /** + * Error code is missing - reason for failure is unknown + */ + ERROR_CODE_MISSING; + + /** + * Converts Miniserver status code to enumerated error value + * + * @param code status code received in message response from the Miniserver + * @return converted error code + */ + public static LxErrorCode getErrorCode(Integer code) { + if (code == null) { + return ERROR_CODE_MISSING; + } + switch (code) { + case 420: + return USER_AUTHENTICATION_TIMEOUT; + case 401: + case 500: + return USER_UNAUTHORIZED; + case 4003: + return TOO_MANY_FAILED_LOGIN_ATTEMPTS; + case 1001: + return WEBSOCKET_IDLE_TIMEOUT; + case 200: + return OK; + default: + return COMMUNICATION_ERROR; + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxResponse.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxResponse.java new file mode 100644 index 0000000000000..66cd05bb6d135 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxResponse.java @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.types; + +import java.lang.reflect.Type; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.annotations.SerializedName; + +/** + * Response to a command sent to Miniserver's control. + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxResponse { + + /** + * A sub-response value structure that is received as a response to config API HTTP request sent to the Miniserver. + * + * @author Pawel Pieczul - initial contribution + * + */ + public class LxResponseCfgApi { + public String snr; + public String version; + } + + /** + * A sub-response structure that is part of a {@link LxResponse} class and contains a response to a command sent to + * Miniserver's control. + * + * @author Pawel Pieczul - initial contribution + * + */ + private class LxSubResponse { + @SerializedName("control") + private String command; + @SerializedName(value = "Code", alternate = { "code" }) + private Integer code; + private JsonElement value; + + private boolean isSubResponseOk() { + return (getResponseCode() == LxErrorCode.OK) && (command != null) && (value != null); + } + + private LxErrorCode getResponseCode() { + return LxErrorCode.getErrorCode(code); + } + } + + @SerializedName("LL") + public LxSubResponse subResponse; + private final Logger logger = LoggerFactory.getLogger(LxResponse.class); + + /** + * Return true when response has correct syntax and return code was successful + * + * @return true when response is ok + */ + public boolean isResponseOk() { + return (subResponse != null && subResponse.isSubResponseOk()); + } + + /** + * Gets command to which this response relates + * + * @return command name + */ + public String getCommand() { + return (subResponse != null ? subResponse.command : null); + } + + /** + * Gets error code from the response in numerical form or null if absent + * + * @return error code value + */ + public Integer getResponseCodeNumber() { + return (subResponse != null ? subResponse.code : null); + } + + /** + * Gets error code from the response as an enumerated value + * + * @return error code + */ + public LxErrorCode getResponseCode() { + return LxErrorCode.getErrorCode(getResponseCodeNumber()); + } + + /** + * Gets response value as a string + * + * @return response value as string + */ + public String getValueAsString() { + return (subResponse != null && subResponse.value != null ? subResponse.value.getAsString() : null); + } + + /** + * Deserializes response value as a given type + * + * @param gson GSON object used for deserialization + * @param class to deserialize response to + * @param type class type to deserialize response to + * @return deserialized response + */ + public T getValueAs(Gson gson, Type type) { + if (subResponse != null && subResponse.value != null) { + try { + return gson.fromJson(subResponse.value, type); + } catch (NumberFormatException | JsonParseException e) { + logger.debug("Error parsing response value as {}: {}", type, e.getMessage()); + } + } + return null; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxState.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxState.java new file mode 100644 index 0000000000000..8be6f140caa3a --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxState.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.types; + +import org.openhab.binding.loxone.internal.controls.LxControl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A state of a Loxone control ({@link LxControl}) + *

+ * Each control object may have a number of states defined, that describe the overall condition of the control. + * List of states is read from LoxApp3.json configuration file. + *

+ * Each state is identified by its own UUID and a name of the state. Names are proprietary to a particular type of the + * control and as such are defined in {@link LxControl} child classes implementation (e.g. {@link LxControlSwitch} + * Objects of this class are used to bind state updates received from the Miniserver to a control object. + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxState { + private final LxUuid uuid; + private final String name; + private final LxControl control; + private final Logger logger = LoggerFactory.getLogger(LxState.class); + private Object stateValue; + + /** + * Create a control state object. + * + * @param uuid UUID of the state + * @param name name of the state + * @param control control to which this state belongs + */ + public LxState(LxUuid uuid, String name, LxControl control) { + this.uuid = uuid; + this.name = name; + this.control = control; + } + + /** + * Gets UUID of the state + * + * @return state's UUID + */ + public LxUuid getUuid() { + return uuid; + } + + /** + * Sets current value of the control's state + * + * @param value current state's value to set + */ + public void setStateValue(Object value) { + logger.debug("State set ({},{}) control ({},{}) value={}", uuid, name, control.getUuid(), control.getName(), + value); + if (value != null && !value.equals(this.stateValue)) { + this.stateValue = value; + control.onStateChange(this); + } + } + + /** + * Gets current value of the control's state + * + * @return current state's value + */ + public Object getStateValue() { + return stateValue; + } + + /** + * Gets state's name. + *

+ * State's name is proprietary per control type. + * + * @return state's name + */ + public String getName() { + return name; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxTemperatureHSBType.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxTemperatureHSBType.java new file mode 100644 index 0000000000000..79583d7065109 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxTemperatureHSBType.java @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.types; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.smarthome.core.library.types.HSBType; + +/** + * Temperature HSB Type which acceptss a color in the form brigthness,temperature (Kelvin) + * + * @author Michael Mattan - initial contribution + * + */ +public class LxTemperatureHSBType extends HSBType { + + private static final long serialVersionUID = -2821122730407485795L; + + public static HSBType fromBrightnessTemperature(String value) { + List constituents = Arrays.stream(value.split(",")).map(in -> in.trim()).collect(Collectors.toList()); + + if (constituents.size() == 2) { + int brightness = constrain(Integer.valueOf(constituents.get(0)), 0, 100); + int temperature = constrain(Integer.valueOf(constituents.get(1)), 0, 65500); + + int red = map(brightness, 0, 100, 0, calculateRed(temperature)); + int green = map(brightness, 0, 100, 0, calculateGreen(temperature)); + int blue = map(brightness, 0, 100, 0, calculateBlue(temperature)); + + return HSBType.fromRGB(red, green, blue); + } else { + throw new IllegalArgumentException(value + " is not a valid TemperatureHSBType syntax"); + } + } + + /** + * Re-maps a number from one range to another. That is, a value of fromLow would get mapped to toLow, a value of + * fromHigh to toHigh, values in-between to values in-between, etc. + * + * @param x the number to map + * @param fromLow the lower bound of the value's current range + * @param fromHigh the upper bound of the value's current range + * @param toLow the lower bound of the value's target range + * @param toHigh the upper bound of the value's target range + * @return the mapped value + */ + private static int map(int x, int fromLow, int fromHigh, int toLow, int toHigh) { + return (x - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; + } + + /** + * calculates the red value based on the Kelvin temperature + * + * @param temp the Kelvin temperature + * @return the red value + */ + private static int calculateRed(int temp) { + int red = 255; + int temperature = temp / 100; + + if (temperature > 66) { + red = temperature - 60; + red = ((Long) Math.round(329.698727466 * Math.pow(red, -0.1332047592))).intValue(); + } + + return constrain(red, 0, 255); + } + + /** + * calculates the green value based on the Kelvin temperature + * + * @param temp green Kelvin temperature + * @return the red value + */ + private static int calculateGreen(int temp) { + int green; + int temperature = temp / 100; + + if (temperature <= 66) { + green = temperature; + green = ((Long) Math.round((99.4708025861 * Math.log(green)) - 161.1195681661)).intValue(); + } else { + green = temperature - 60; + green = ((Long) Math.round(288.1221695283 * Math.pow(green, -0.0755148492))).intValue(); + } + + return constrain(green, 0, 255); + } + + /** + * calculates the blue value based on the Kelvin temperature + * + * @param temp the Kelvin temperature + * @return the blue value + */ + private static int calculateBlue(int temp) { + int blue = 255; + int temperature = temp / 100; + + if (temperature < 65) { + if (temperature <= 19) { + blue = 0; + } else { + blue = temperature - 10; + blue = ((Long) Math.round((138.5177312231 * Math.log(blue)) - 305.0447927307)).intValue(); + } + } + + return constrain(blue, 0, 255); + } + + /** + * Constrains a number to be within a range. + * + * @param x the number to constrain + * @param min the minimum value + * @param max the maximum value + * @return the constrained value + */ + private static int constrain(int x, int min, int max) { + if (x >= min && x <= max) { + return x; + } else if (x < min) { + return min; + } else { + return max; + } + } + +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxUuid.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxUuid.java new file mode 100644 index 0000000000000..bfa4372f793b1 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxUuid.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.types; + +import java.lang.reflect.Type; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; + +/** + * Unique identifier of an object on Loxone Miniserver. + *

+ * It is defined by the Miniserver. UUID can represent a control, room, category, etc. and provides a unique ID space + * across all objects residing on the Miniserver. + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxUuid { + private final String uuid; + private final String uuidOriginal; + + public static final JsonDeserializer DESERIALIZER = new JsonDeserializer() { + @Override + public LxUuid deserialize(JsonElement json, Type type, JsonDeserializationContext context) + throws JsonParseException { + String uuid = json.getAsString(); + if (uuid != null) { + return new LxUuid(uuid); + } + return null; + } + }; + + /** + * Create a new {@link LxUuid} object from an UUID on a Miniserver. + * + * @param uuid identifier retrieved from Loxone Miniserver + */ + public LxUuid(String uuid) { + uuidOriginal = uuid; + this.uuid = init(uuid); + } + + public LxUuid(byte data[], int offset) { + String id = String.format("%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x", + ByteBuffer.wrap(data, offset, 4).order(ByteOrder.LITTLE_ENDIAN).getInt(), + ByteBuffer.wrap(data, offset + 4, 2).order(ByteOrder.LITTLE_ENDIAN).getShort(), + ByteBuffer.wrap(data, offset + 6, 2).order(ByteOrder.LITTLE_ENDIAN).getShort(), data[offset + 8], + data[offset + 9], data[offset + 10], data[offset + 11], data[offset + 12], data[offset + 13], + data[offset + 14], data[offset + 15]); + uuidOriginal = id; + this.uuid = init(id); + } + + private String init(String uuid) { + return uuidOriginal.replaceAll("[^a-zA-Z0-9-]", "-").toUpperCase(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null) { + return false; + } + if (o.getClass() != getClass()) { + return false; + } + LxUuid id = (LxUuid) o; + return uuid.equals(id.uuid); + } + + @Override + public int hashCode() { + return uuid.hashCode(); + } + + @Override + public String toString() { + return uuid; + } + + /** + * Returns an original string that was used to create UUID. + * + * @return original string for the UUID + */ + public String getOriginalString() { + return uuidOriginal; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxWsBinaryHeader.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxWsBinaryHeader.java new file mode 100644 index 0000000000000..5caf6fc3f8ba4 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxWsBinaryHeader.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.types; + +/** + * A header of a binary message received from Loxone Miniserver on a websocket connection. + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxWsBinaryHeader { + /** + * Type of a binary message received from the Miniserver + * + * @author Pawel Pieczul + * + */ + public enum LxWsMessageType { + /** + * Text message - jetty websocket client will pass it on automatically to a callback + */ + TEXT_MESSAGE, + /** + * Binary file + */ + BINARY_FILE, + /** + * A set of value states for controls that changed their state + */ + EVENT_TABLE_OF_VALUE_STATES, + /** + * A set of text states for controls that changed their state + */ + EVENT_TABLE_OF_TEXT_STATES, + EVENT_TABLE_OF_DAYTIMER_STATES, + OUT_OF_SERVICE_INDICATOR, + /** + * Response to keepalive request message + */ + KEEPALIVE_RESPONSE, + EVENT_TABLE_OF_WEATHER_STATES, + /** + * Unknown header + */ + UNKNOWN + } + + private LxWsMessageType type = LxWsMessageType.UNKNOWN; + + /** + * Create header from binary buffer at a given offset + * + * @param buffer buffer with received message + * @param offset offset in bytes at which header is expected + */ + public LxWsBinaryHeader(byte[] buffer, int offset) throws IndexOutOfBoundsException { + if (buffer[offset] != 0x03) { + return; + } + switch (buffer[offset + 1]) { + case 0: + type = LxWsMessageType.TEXT_MESSAGE; + break; + case 1: + type = LxWsMessageType.BINARY_FILE; + break; + case 2: + type = LxWsMessageType.EVENT_TABLE_OF_VALUE_STATES; + break; + case 3: + type = LxWsMessageType.EVENT_TABLE_OF_TEXT_STATES; + break; + case 4: + type = LxWsMessageType.EVENT_TABLE_OF_DAYTIMER_STATES; + break; + case 5: + type = LxWsMessageType.OUT_OF_SERVICE_INDICATOR; + break; + case 6: + type = LxWsMessageType.KEEPALIVE_RESPONSE; + break; + case 7: + type = LxWsMessageType.EVENT_TABLE_OF_WEATHER_STATES; + break; + default: + type = LxWsMessageType.UNKNOWN; + break; + } + // These fields are not used today , but left it for future reference + // estimated = ((buffer[offset + 2] & 0x01) != 0); + // length = ByteBuffer.wrap(buffer, offset + 3, 4).getInt(); + } + + public LxWsMessageType getType() { + return type; + } +} diff --git a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsSecurityType.java b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxWsSecurityType.java similarity index 87% rename from addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsSecurityType.java rename to bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxWsSecurityType.java index 95caafb935efd..024776a650ec9 100644 --- a/addons/binding/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/core/LxWsSecurityType.java +++ b/bundles/org.openhab.binding.loxone/src/main/java/org/openhab/binding/loxone/internal/types/LxWsSecurityType.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.loxone.internal.core; +package org.openhab.binding.loxone.internal.types; /** * Types of security authentication/encryption methods. @@ -33,11 +33,11 @@ public enum LxWsSecurityType { TOKEN; /** * Encode security type based on index - * + * * @param index - * 0 for auto, 1 for hash, 2 for token + * 0 for auto, 1 for hash, 2 for token * @return - * security type fo given index + * security type fo given index */ public static LxWsSecurityType getType(int index) { switch (index) { diff --git a/bundles/org.openhab.binding.loxone/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.loxone/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..c579467fa879d --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,8 @@ + + + Loxone Binding + This is the binding for Loxone Miniserver + Pawel Pieczul + diff --git a/bundles/org.openhab.binding.loxone/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.loxone/src/main/resources/ESH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..3febdcbf1feb8 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/main/resources/ESH-INF/thing/thing-types.xml @@ -0,0 +1,210 @@ + + + + + + + IP gateway for Loxone Smarthome system + + + + + + Connection to Miniserver + + + + + Authentication and encryption parameters + + + + + Various timeout parameters + true + + + + + Various sizing parameters + true + + + + + network-address + Host address or IP of the Loxone Miniserver + + + + Web interface port of the Loxone Miniserver + 80 + + + + + Method used to authorize user in the Miniserver + 0 + + + + + + true + + + + User name on the Loxone Miniserver + + + + password + User password on the Loxone Miniserver. In token-based authentication this password will be cleared after token is acquired. + + + + Token acquired by the binding from the Miniserver. Applicable only in token-based authentication mode. + true + + + + + Time between binding initialization and first connection attempt (seconds, 0-120) + 1 + + + + Time between sending two consecutive keep-alive messages (seconds, 1-600) + 240 + + + + Time between failed websocket connect attempts (seconds, 0-600) + 10 + + + + Time to wait for a response from Miniserver to a request sent from the binding (seconds, 0-60) + 4 + + + + Time in seconds between user login error as a result of wrong name/password or no authority and next connection attempt (seconds, 0-3600) + 60 + + + + Time between connection close (as a result of some communication error) and next connection attempt (seconds, 0-3600) + 30 + + + + Websocket client's maximum binary message size in kB + 3072 + + + + Websocket client's maximum text message size in kB + 512 + + + + + + + Switch + + Loxone's virtual input of switch type and push button controls. + + + + Switch + + Loxone's digital information controls (InfoOnlyDigital, read-only). + + + + + Rollershutter + + Loxone's Jalousies (rollershutters, blinds). + + + + String + + Loxone's state information controls (TextState, read-only). + + + + + Number + + Loxone's Virtual Inputs. + + + + Number + + Loxone's time counter (TimedSwitch, read-only). + + + + + Number + + Loxone's state information controls (InfoOnlyAnalog, read-only). + + + + + Number + + Loxone's light controllers (LightController). + + + + + + + + + + + + + + + + + + Number + + Loxone's radio button controls (Radio). + + + + + Dimmer + + Loxone's dimmer control + + + + Color + + Loxone's Color Picker V2 control + + + + DateTime + + Date and time of an event + + + + + diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlAlarmNoPresenceTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlAlarmNoPresenceTest.java new file mode 100644 index 0000000000000..75e46232f9885 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlAlarmNoPresenceTest.java @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.junit.Assert.assertEquals; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.smarthome.core.library.types.DateTimeType; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlAlarm} - version for alarm without presence sensors + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlAlarmNoPresenceTest extends LxControlTest { + static final String ARM_DELAYED_CHANNEL = " / Arm Delayed"; + static final String NEXT_LEVEL_CHANNEL = " / Next Level"; + static final String NEXT_LEVEL_DELAY_CHANNEL = " / Next Level Delay"; + static final String NEXT_LEVEL_DELAY_TOTAL_CHANNEL = " / Next Level Delay Total"; + static final String LEVEL_CHANNEL = " / Level"; + static final String START_TIME_CHANNEL = " / Start Time"; + static final String ARMED_DELAY_CHANNEL = " / Armed Delay"; + static final String ARMED_TOTAL_DELAY_CHANNEL = " / Armed Total Delay"; + static final String SENSORS_CHANNEL = " / Sensors"; + static final String QUIT_CHANNEL = " / Acknowledge"; + + static private final String numberChannels[] = { NEXT_LEVEL_CHANNEL, NEXT_LEVEL_DELAY_CHANNEL, + NEXT_LEVEL_DELAY_TOTAL_CHANNEL, LEVEL_CHANNEL, ARMED_DELAY_CHANNEL, ARMED_TOTAL_DELAY_CHANNEL }; + + @Before + public void setup() { + setupControl("233d5db0-0333-5865-ffff403fb0c34b9e", "0b734138-037d-034e-ffff403fb0c34b9e", + "0fe650c2-0004-d446-ffff504f9410790f", "Burglar Alarm No Presence"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlAlarm.class, 2, 1, 11, 12, 10); + } + + @Test + public void testChannels() { + // read-write channels + testChannel("Switch"); + testChannel("Switch", ARM_DELAYED_CHANNEL); + testChannel("Switch", QUIT_CHANNEL, null, null, null, null, true, null); + // read-only channels + testChannel("Number", NEXT_LEVEL_CHANNEL); + testChannel("Number", NEXT_LEVEL_DELAY_CHANNEL); + testChannel("Number", NEXT_LEVEL_DELAY_TOTAL_CHANNEL); + testChannel("Number", LEVEL_CHANNEL); + testChannel("Number", ARMED_DELAY_CHANNEL); + testChannel("Number", ARMED_TOTAL_DELAY_CHANNEL); + testChannel("String", SENSORS_CHANNEL); + testChannel("DateTime", START_TIME_CHANNEL); + } + + @Test + public void testCommandsDefaultChannel() { + testAction(null); + for (int i = 0; i < 20; i++) { + changeLoxoneState("disabledmove", 0.0); + executeCommand(OnOffType.ON); + testAction("on"); + executeCommand(OnOffType.OFF); + testAction("off"); + changeLoxoneState("disabledmove", 1.0); + executeCommand(OnOffType.ON); + testAction("on"); + executeCommand(OnOffType.OFF); + testAction("off"); + } + } + + @Test + public void testCommandsArmWithDelayChannel() { + testAction(null); + for (int i = 0; i < 20; i++) { + changeLoxoneState("disabledmove", 0.0); + executeCommand(ARM_DELAYED_CHANNEL, OnOffType.ON); + testAction("delayedon"); + executeCommand(ARM_DELAYED_CHANNEL, OnOffType.OFF); + testAction("off"); + changeLoxoneState("disabledmove", 1.0); + executeCommand(ARM_DELAYED_CHANNEL, OnOffType.ON); + testAction("delayedon"); + executeCommand(ARM_DELAYED_CHANNEL, OnOffType.OFF); + testAction("off"); + } + } + + @Test + public void testCommandsQuitChannel() { + testAction(null); + for (int i = 0; i < 20; i++) { + executeCommand(QUIT_CHANNEL, OnOffType.ON); + testAction("quit"); + executeCommand(QUIT_CHANNEL, OnOffType.OFF); + testAction(null); + } + } + + @Test + public void testNumberChannels() { + testNumberChannel(NEXT_LEVEL_CHANNEL, "nextlevel"); + testNumberChannel(NEXT_LEVEL_DELAY_CHANNEL, "nextleveldelay"); + testNumberChannel(NEXT_LEVEL_DELAY_TOTAL_CHANNEL, "nextleveldelaytotal"); + testNumberChannel(LEVEL_CHANNEL, "level"); + testNumberChannel(ARMED_DELAY_CHANNEL, "armeddelay"); + testNumberChannel(ARMED_TOTAL_DELAY_CHANNEL, "armeddelaytotal"); + } + + @Test + public void testStartedTimeChannel() { + changeLoxoneState("starttime", "2019-11-18 14:54:21"); + LocalDateTime ldt = LocalDateTime.of(2019, 11, 18, 14, 54, 21); + ZonedDateTime dt = ldt.atZone(ZoneId.systemDefault()); + testChannelState(START_TIME_CHANNEL, new DateTimeType(dt)); + + changeLoxoneState("starttime", "something else"); + testChannelState(START_TIME_CHANNEL, null); + + changeLoxoneState("starttime", "1981-01-02 03:04:05"); + ldt = LocalDateTime.of(1981, 1, 2, 3, 4, 5); + dt = ldt.atZone(ZoneId.systemDefault()); + testChannelState(START_TIME_CHANNEL, new DateTimeType(dt)); + + changeLoxoneState("starttime", "1981-13-02 03:04:05"); + testChannelState(START_TIME_CHANNEL, null); + } + + @Test + public void testSensorsChannel() { + testChannelState(SENSORS_CHANNEL, null); + for (int i = 0; i < 20; i++) { + changeLoxoneState("sensors", "test sensors channel string " + i); + testChannelState(SENSORS_CHANNEL, new StringType("test sensors channel string " + i)); + } + } + + @Test + public void testLevelAndAcknowledge() { + changeLoxoneState("level", 0.0); + testChannel("Switch", QUIT_CHANNEL, null, null, null, null, true, null); + for (Double i = 1.0; i <= 6.0; i++) { + changeLoxoneState("level", i); + testChannel("Switch", QUIT_CHANNEL, null, null, null, null, false, null); + changeLoxoneState("level", 0.0); + testChannel("Switch", QUIT_CHANNEL, null, null, null, null, true, null); + } + } + + private void testNumberChannel(String channel, String state) { + Map states = new HashMap<>(); + for (String s : numberChannels) { + states.put(s, getChannelState(s)); + } + for (Double i = -100.0; i <= 100.0; i += 2.341) { + changeLoxoneState(state, i); + testChannelState(channel, new DecimalType(i)); + states.entrySet().stream().filter(v -> !v.getKey().equals(channel)).forEach(v -> { + String key = v.getKey(); + assertEquals(states.get(key), getChannelState(key)); + }); + } + changeLoxoneState(state, Double.NaN); + testChannelState(channel, UnDefType.UNDEF); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlAlarmWithPresenceTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlAlarmWithPresenceTest.java new file mode 100644 index 0000000000000..41524cf3a1a45 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlAlarmWithPresenceTest.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlAlarm} - version with motion sensors + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlAlarmWithPresenceTest extends LxControlAlarmNoPresenceTest { + private static final String MOTION_SENSORS_CHANNEL = " / Motion Sensors"; + + @Override + @Before + public void setup() { + setupControl("133d5db0-0333-5865-ffff403fb0c34b9e", "0b734138-037d-034e-ffff403fb0c34b9e", + "0fe650c2-0004-d446-ffff504f9410790f", "Burglar Alarm With Presence"); + } + + @Override + @Test + public void testControlCreation() { + testControlCreation(LxControlAlarm.class, 2, 1, 12, 13, 10); + } + + @Override + @Test + public void testChannels() { + super.testChannels(); + testChannel("Switch", MOTION_SENSORS_CHANNEL); + } + + @Override + @Test + public void testCommandsDefaultChannel() { + testAction(null); + for (int i = 0; i < 20; i++) { + changeLoxoneState("disabledmove", 0.0); + executeCommand(OnOffType.ON); + testAction("on/1"); + executeCommand(OnOffType.OFF); + testAction("off"); + changeLoxoneState("disabledmove", 1.0); + executeCommand(OnOffType.ON); + testAction("on/0"); + executeCommand(OnOffType.OFF); + testAction("off"); + } + } + + @Override + @Test + public void testCommandsArmWithDelayChannel() { + testAction(null); + for (int i = 0; i < 20; i++) { + changeLoxoneState("disabledmove", 0.0); + executeCommand(ARM_DELAYED_CHANNEL, OnOffType.ON); + testAction("delayedon/1"); + executeCommand(ARM_DELAYED_CHANNEL, OnOffType.OFF); + testAction("off"); + changeLoxoneState("disabledmove", 1.0); + executeCommand(ARM_DELAYED_CHANNEL, OnOffType.ON); + testAction("delayedon/0"); + executeCommand(ARM_DELAYED_CHANNEL, OnOffType.OFF); + testAction("off"); + } + } + + @Test + public void testCommandsMotionSensors() { + testAction(null); + for (int i = 0; i < 20; i++) { + executeCommand(MOTION_SENSORS_CHANNEL, OnOffType.ON); + testAction("dismv/0"); + executeCommand(MOTION_SENSORS_CHANNEL, OnOffType.OFF); + testAction("dismv/1"); + } + } + + @Test + public void testLoxoneMotionSensorsChanges() { + for (int i = 0; i < 20; i++) { + changeLoxoneState("disabledmove", 1.0); + testChannelState(MOTION_SENSORS_CHANNEL, OnOffType.OFF); + changeLoxoneState("disabledmove", 0.0); + testChannelState(MOTION_SENSORS_CHANNEL, OnOffType.ON); + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlDimmerTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlDimmerTest.java new file mode 100644 index 0000000000000..17a22a9ec39ab --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlDimmerTest.java @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.StopMoveType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlDimmer} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlDimmerTest extends LxControlTest { + @Before + public void setup() { + setupControl("131b19cd-03c0-640f-ffff403fb0c34b9e", "0b734138-037d-034e-ffff403fb0c34b9e", + "0fe650c2-0004-d446-ffff504f9410790f", "Dimmer Control"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlDimmer.class, 1, 0, 1, 1, 4); + } + + @Test + public void testChannels() { + testChannel("Dimmer"); + } + + @Test + public void testLoxonePositionMinMaxChanges() { + // filling in missing state values + testChannelState(null); + changeLoxoneState("step", 1.0); + testChannelState(null); + changeLoxoneState("position", 50.0); + testChannelState(null); + changeLoxoneState("min", 0.0); + testChannelState(null); + changeLoxoneState("max", 100.0); + testChannelState(new PercentType(50)); + + // potential division by zero + changeLoxoneState("min", 55.0); + changeLoxoneState("max", 55.0); + testChannelState(null); + + changeLoxoneState("min", 200.0); + changeLoxoneState("max", 400.0); + // out of range + changeLoxoneState("position", 199.9); + testChannelState(null); + changeLoxoneState("position", 400.1); + testChannelState(null); + // scaling within range + changeLoxoneState("position", 200.0); + testChannelState(PercentType.ZERO); + changeLoxoneState("position", 400.0); + testChannelState(PercentType.HUNDRED); + changeLoxoneState("position", 300.0); + testChannelState(new PercentType(50)); + // special value meaning switched off + changeLoxoneState("position", 0.0); + testChannelState(PercentType.ZERO); + + // reversed range boundaries + changeLoxoneState("min", 50.0); + changeLoxoneState("max", 20.0); + // here dimmer still turned off + testChannelState(PercentType.ZERO); + // here within wrong range + changeLoxoneState("position", 30.0); + testChannelState(null); + } + + @Test + public void testOnOffPercentCommands() { + executeCommand(OnOffType.ON); + testAction("On"); + executeCommand(OnOffType.OFF); + testAction("Off"); + + changeLoxoneState("min", 1000.0); + changeLoxoneState("max", 3000.0); + changeLoxoneState("step", 100.0); + executeCommand(PercentType.HUNDRED); + testAction("3000.0"); + executeCommand(new PercentType(50)); + testAction("2000.0"); + executeCommand(new PercentType(1)); + testAction("1020.0"); + executeCommand(PercentType.ZERO); + testAction("0.0"); + + executeCommand(StopMoveType.MOVE); + testAction(null); + } + + @Test + public void testIncreaseDecreaseCommands() { + changeLoxoneState("min", 123.0); + changeLoxoneState("max", 456.0); + changeLoxoneState("step", 23.0); + changeLoxoneState("position", 400.0); + testChannelState(new PercentType(83)); + executeCommand(IncreaseDecreaseType.INCREASE); + testAction("423.0"); + changeLoxoneState("position", 423.0); + testChannelState(new PercentType(90)); + executeCommand(IncreaseDecreaseType.INCREASE); + testAction("446.0"); + changeLoxoneState("position", 446.0); + testChannelState(new PercentType(96)); + executeCommand(IncreaseDecreaseType.INCREASE); + testAction("456.0"); // trim to max + changeLoxoneState("position", 456.0); + testChannelState(PercentType.HUNDRED); + changeLoxoneState("step", 100.0); + executeCommand(IncreaseDecreaseType.DECREASE); + testAction("356.0"); + changeLoxoneState("position", 356.0); + testChannelState(new PercentType(69)); + executeCommand(IncreaseDecreaseType.DECREASE); + testAction("256.0"); + changeLoxoneState("position", 256.0); + testChannelState(new PercentType(39)); + executeCommand(IncreaseDecreaseType.DECREASE); + testAction("156.0"); + changeLoxoneState("position", 156.0); + testChannelState(new PercentType(9)); + executeCommand(IncreaseDecreaseType.DECREASE); + testAction("123.0"); // trim to min + changeLoxoneState("position", 123.0); + testChannelState(PercentType.ZERO); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlInfoOnlyAnalogTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlInfoOnlyAnalogTest.java new file mode 100644 index 0000000000000..a2d7f44454500 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlInfoOnlyAnalogTest.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.UnDefType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlInfoOnlyAnalog} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlInfoOnlyAnalogTest extends LxControlTest { + @Before + public void setup() { + setupControl("0fec5dc3-003e-8800-ffff403fb0c34b9e", "0fe3a451-0283-2afa-ffff403fb0c34b9e", + "0fe665f4-0161-4773-ffff403fb0c34b9e", "Info Only Analog"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlInfoOnlyAnalog.class, 1, 0, 1, 1, 1); + } + + @Test + public void testChannels() { + testChannel("Number", null, null, null, null, "%.2f", true, null); + } + + @Test + public void testLoxoneStateChanges() { + for (Double i = -1000.0; i < 1000.0; i += 33.7324323) { + changeLoxoneState("value", i); + testChannelState(new DecimalType(i)); + } + changeLoxoneState("value", 0.0); + testChannelState(DecimalType.ZERO); + changeLoxoneState("value", Double.NaN); + testChannelState(UnDefType.UNDEF); + changeLoxoneState("value", Double.MAX_VALUE); + testChannelState(new DecimalType(Double.MAX_VALUE)); + changeLoxoneState("value", Double.POSITIVE_INFINITY); + testChannelState(UnDefType.UNDEF); + changeLoxoneState("value", Double.MIN_VALUE); + testChannelState(new DecimalType(Double.MIN_VALUE)); + changeLoxoneState("value", Double.NEGATIVE_INFINITY); + testChannelState(UnDefType.UNDEF); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlInfoOnlyDigitalTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlInfoOnlyDigitalTest.java new file mode 100644 index 0000000000000..40bdda911da90 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlInfoOnlyDigitalTest.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.types.UnDefType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlInfoOnlyDigital} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlInfoOnlyDigitalTest extends LxControlTest { + @Before + public void setup() { + setupControl("101b50f7-0306-98fb-ffff403fb0c34b9e", "0e368d32-014f-4604-ffff403fb0c34b9e", + "101b563d-0302-78bd-ffff403fb0c34b9e", "Info Only Digital"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlInfoOnlyDigital.class, 1, 0, 1, 1, 1); + } + + @Test + public void testChannels() { + testChannel("Switch"); + } + + @Test + public void testLoxoneStateChanges() { + for (Double i = 2.0; i < 100.0; i++) { + changeLoxoneState("active", 0.0); + testChannelState(OnOffType.OFF); + changeLoxoneState("active", 1.0); + testChannelState(OnOffType.ON); + changeLoxoneState("active", 1.0 / i); + testChannelState(UnDefType.UNDEF); + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlJalousieTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlJalousieTest.java new file mode 100644 index 0000000000000..091353cd8f7a6 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlJalousieTest.java @@ -0,0 +1,193 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.StopMoveType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlJalousie} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlJalousieTest extends LxControlTest { + + private static final String ROLLERSHUTTER_CHANNEL = null; + private static final String SHADE_CHANNEL = " / Shade"; + private static final String AUTO_SHADE_CHANNEL = " / Auto Shade"; + + @Before + public void setup() { + setupControl("0e367c09-0161-e2c1-ffff403fb0c34b9e", "0e368d32-014f-4604-ffff403fb0c34b9e", + "0b734138-033e-02d8-ffff403fb0c34b9e", "Window Blinds"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlJalousie.class, 1, 0, 3, 3, 11); + } + + @Test + public void testChannels() { + testChannel("Rollershutter"); + testChannel("Switch", SHADE_CHANNEL); + testChannel("Switch", AUTO_SHADE_CHANNEL); + } + + @Test + public void testLoxonePositionAutoShadeStates() { + boolean a = false; + testChannelState(ROLLERSHUTTER_CHANNEL, null); + testChannelState(SHADE_CHANNEL, OnOffType.OFF); + testChannelState(AUTO_SHADE_CHANNEL, null); + for (int i = 0; i <= 100; i++) { + changeLoxoneState("position", i / 100.0); + testChannelState(ROLLERSHUTTER_CHANNEL, new PercentType(i)); + testChannelState(SHADE_CHANNEL, OnOffType.OFF); + changeLoxoneState("autoactive", a ? 1.0 : 0.0); + testChannelState(AUTO_SHADE_CHANNEL, a ? OnOffType.ON : OnOffType.OFF); + a = !a; + } + changeLoxoneState("position", 100.1); + testChannelState(ROLLERSHUTTER_CHANNEL, null); + changeLoxoneState("position", -0.1); + testChannelState(ROLLERSHUTTER_CHANNEL, null); + } + + @Test + public void testCommands() { + for (int i = 0; i < 20; i++) { + executeCommand(SHADE_CHANNEL, OnOffType.ON); + testAction("shade"); + executeCommand(SHADE_CHANNEL, OnOffType.OFF); + testAction(null); + executeCommand(SHADE_CHANNEL, DecimalType.ZERO); + testAction(null); + executeCommand(AUTO_SHADE_CHANNEL, OnOffType.ON); + testAction("auto"); + executeCommand(AUTO_SHADE_CHANNEL, OnOffType.OFF); + testAction("NoAuto"); + executeCommand(AUTO_SHADE_CHANNEL, DecimalType.ZERO); + testAction(null); + executeCommand(ROLLERSHUTTER_CHANNEL, UpDownType.UP); + testAction("FullUp"); + executeCommand(ROLLERSHUTTER_CHANNEL, UpDownType.DOWN); + testAction("FullDown"); + executeCommand(ROLLERSHUTTER_CHANNEL, StopMoveType.STOP); + testAction("Stop"); + executeCommand(ROLLERSHUTTER_CHANNEL, StopMoveType.MOVE); + testAction(null); + } + } + + @Test + public void testMovingToPosition() { + changeLoxoneState("position", 0.1); + testChannelState(ROLLERSHUTTER_CHANNEL, new PercentType(10)); + executeCommand(ROLLERSHUTTER_CHANNEL, new PercentType(73)); + testAction("FullDown"); + changeLoxoneState("up", 0.0); + changeLoxoneState("down", 1.0); + for (int i = 10; i <= 72; i++) { + changeLoxoneState("position", i / 100.0); + testChannelState(ROLLERSHUTTER_CHANNEL, new PercentType(i)); + } + changeLoxoneState("position", 0.73); + testAction("Stop"); + changeLoxoneState("position", 0.74); + testAction(null); + changeLoxoneState("up", 0.0); + changeLoxoneState("down", 0.0); + executeCommand(ROLLERSHUTTER_CHANNEL, new PercentType(10)); + testAction("FullUp"); + changeLoxoneState("up", 1.0); + changeLoxoneState("down", 0.0); + for (int i = 74; i >= 11; i--) { + changeLoxoneState("position", i / 100.0); + testChannelState(ROLLERSHUTTER_CHANNEL, new PercentType(i)); + } + changeLoxoneState("position", 0.10); + testAction("Stop"); + changeLoxoneState("position", 0.09); + testAction(null); + + executeCommand(ROLLERSHUTTER_CHANNEL, new PercentType(50)); + testAction("FullDown"); + changeLoxoneState("up", 0.0); + changeLoxoneState("down", 1.0); + changeLoxoneState("position", 0.80); + testAction("Stop"); + changeLoxoneState("position", 0.50); + testAction(null); + + executeCommand(ROLLERSHUTTER_CHANNEL, new PercentType(90)); + testAction("FullDown"); + changeLoxoneState("up", 0.0); + changeLoxoneState("down", 0.0); + changeLoxoneState("position", 0.95); + testAction(null); + changeLoxoneState("position", 0.85); + testAction(null); + + changeLoxoneState("down", 1.0); + changeLoxoneState("position", 0.85); + testAction(null); + changeLoxoneState("position", 0.95); + testAction("Stop"); + changeLoxoneState("position", 0.85); + testAction(null); + changeLoxoneState("position", 0.95); + testAction(null); + changeLoxoneState("down", 0.0); + + executeCommand(ROLLERSHUTTER_CHANNEL, new PercentType(30)); + testAction("FullUp"); + changeLoxoneState("up", 1.0); + changeLoxoneState("down", 0.0); + changeLoxoneState("position", 0.40); + testAction(null); + changeLoxoneState("position", 0.20); + testAction("Stop"); + changeLoxoneState("position", 0.40); + testAction(null); + changeLoxoneState("position", 0.20); + testAction(null); + changeLoxoneState("up", 0.0); + + executeCommand(ROLLERSHUTTER_CHANNEL, PercentType.HUNDRED); + testAction("FullDown"); + changeLoxoneState("up", 0.0); + changeLoxoneState("down", 1.0); + changeLoxoneState("position", 0.80); + testAction(null); + changeLoxoneState("position", 1.00); + testAction("Stop"); + changeLoxoneState("down", 0.0); + + executeCommand(ROLLERSHUTTER_CHANNEL, PercentType.ZERO); + testAction("FullUp"); + changeLoxoneState("up", 1.0); + changeLoxoneState("down", 0.0); + changeLoxoneState("position", 0.20); + testAction(null); + changeLoxoneState("position", 0.00); + testAction("Stop"); + changeLoxoneState("up", 0.0); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlLeftRightAnalogTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlLeftRightAnalogTest.java new file mode 100644 index 0000000000000..da846473746e9 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlLeftRightAnalogTest.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.junit.Before; + +/** + * Test class for (@link LxControlLeftRightAnalog} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlLeftRightAnalogTest extends LxControlUpDownAnalogTest { + @Override + @Before + public void setup() { + min = 1072.123; + max = 1123.458; + step = 23.987; + format = "value: %.2f"; + setupControl("131b1a96-02b9-f6e9-ffff403fb0c34b9e", "0b734138-037d-034e-ffff403fb0c34b9e", + "0fe650c2-0004-d446-ffff504f9410790f", "Left Right Analog Input"); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlLeftRightDigitalTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlLeftRightDigitalTest.java new file mode 100644 index 0000000000000..9cd19bc705a13 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlLeftRightDigitalTest.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlLeftRightDigital} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlLeftRightDigitalTest extends LxControlUpDownDigitalTest { + @Override + @Before + public void setup() { + upChannel = " / Left"; + downChannel = " / Right"; + setupControl("0fd08ca6-01a6-d72a-efff403fb0c34b9e", "0b734138-037d-034e-ffff403fb0c34b9e", + "0b734138-033e-02d4-ffff403fb0c34b9e", "Second Floor Scene"); + } + + @Override + @Test + public void testControlCreation() { + testControlCreation(LxControlLeftRightDigital.class, 1, 0, 2, 2, 0); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlLightControllerV2Test.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlLightControllerV2Test.java new file mode 100644 index 0000000000000..66076ab9f1c71 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlLightControllerV2Test.java @@ -0,0 +1,262 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.eclipse.smarthome.core.types.StateOption; +import org.eclipse.smarthome.core.types.UnDefType; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * Test class for (@link LxControlSwitch} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlLightControllerV2Test extends LxControlTest { + @Before + public void setup() { + setupControl("1076668f-0101-7076-ffff403fb0c34b9e", "0b734138-03ac-03f0-ffff403fb0c34b9e", + "0b734138-033e-02d4-ffff403fb0c34b9e", "Lighting controller"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlLightControllerV2.class, 1, 5, 1, 6, 4); + testSubControl(LxControlSwitch.class, "Overall Switch"); + testSubControl(LxControlSwitch.class, "Hall Study Lights"); + testSubControl(LxControlSwitch.class, "Roof Lights"); + testSubControl(LxControlSwitch.class, "Hall Left Lights"); + testSubControl(LxControlSwitch.class, "Hall Right Lights"); + } + + @Test + public void testChannels() { + testChannel("Number"); + } + + @Test + public void testCommands() { + for (int i = 0; i < 20; i++) { + executeCommand(UpDownType.UP); + testAction("plus"); + executeCommand(UpDownType.DOWN); + testAction("minus"); + } + executeCommand(new DecimalType(1)); + testAction(null); + + loadMoodList1(); + + executeCommand(new DecimalType(0)); + testAction(null); + executeCommand(new DecimalType(1)); + testAction(null); + executeCommand(new DecimalType(2)); + testAction("changeTo/2"); + executeCommand(new DecimalType(3)); + testAction("changeTo/3"); + executeCommand(new DecimalType(4)); + testAction("changeTo/4"); + executeCommand(new DecimalType(5)); + testAction("changeTo/5"); + executeCommand(new DecimalType(6)); + testAction(null); + executeCommand(new DecimalType(777)); + testAction("changeTo/777"); + executeCommand(new DecimalType(778)); + testAction("changeTo/778"); + executeCommand(new DecimalType(779)); + testAction(null); + clearMoodList(); + } + + @Test + public void testMoodListChanges() { + for (int i = 0; i < 20; i++) { + loadMoodList1(); + loadMoodList2(); + } + clearMoodList(); + } + + @Test + public void testActiveMoodChanges() { + loadMoodList2(); + for (int i = 0; i < 10; i++) { + changeLoxoneState("activemoods", "[4]"); + testActiveMoods(779, 4); + changeLoxoneState("activemoods", "[4,6]"); + testActiveMoods(779, 4, 6); + changeLoxoneState("activemoods", "[4,6,7,8,5]"); + testActiveMoods(779, 4, 6, 7, 8, 5); + changeLoxoneState("activemoods", "[4,6,7,8,5,777,778]"); + testActiveMoods(779, 4, 6, 7, 8, 5, 777, 778); + changeLoxoneState("activemoods", "[6,8,777]"); + testActiveMoods(779, 6, 8, 777); + changeLoxoneState("activemoods", "[779]"); + testActiveMoods(779, 779); + changeLoxoneState("activemoods", "[1,2,3,500,900]"); + testActiveMoods(779); + changeLoxoneState("activemoods", "[1,2,3,6,500,900]"); + testActiveMoods(779, 6); + changeLoxoneState("activemoods", "[5]"); + testActiveMoods(779, 5); + changeLoxoneState("activemoods", "[778]"); + testActiveMoods(779, 778); + } + clearMoodList(); + } + + @Test + public void testMoodAddRemove() { + loadMoodList1(); + for (int i = 0; i < 10; i++) { + handler.extraControls.values().forEach(c -> { + LxControlMood m = (LxControlMood) c; + if (!m.getId().equals(778)) { + executeCommand(m, OnOffType.ON); + testAction("addMood/" + m.getId()); + executeCommand(m, OnOffType.OFF); + testAction("removeMood/" + m.getId()); + } + }); + } + clearMoodList(); + } + + private void testActiveMoods(Integer offId, Integer... moods) { + List ids = new ArrayList<>(); + for (Integer id : moods) { + if (!offId.equals(id)) { + LxControlMood m = getMood(id); + testChannelState(m, OnOffType.ON); + } + ids.add(id); + } + handler.extraControls.values().stream() + .filter(c -> !ids.contains(((LxControlMood) c).getId()) && !((LxControlMood) c).getId().equals(offId)) + .forEach(c -> testChannelState(c, OnOffType.OFF)); + + if (ids.size() == 1) { + testChannelState(new DecimalType(ids.get(0))); + } else { + testChannelState(UnDefType.UNDEF); + } + } + + private void loadMoodList1() { + String list = loadMoodList("MoodList1.json"); + changeLoxoneState("moodlist", list); + List options = new ArrayList<>(); + options.add(new StateOption("2", "Side Lights")); + options.add(new StateOption("3", "Play Lights")); + options.add(new StateOption("4", "Study Only")); + options.add(new StateOption("5", "Low Lights")); + options.add(new StateOption("777", "Bright")); + options.add(new StateOption("778", "Off")); + testMoodList(options, 778); + } + + private void loadMoodList2() { + String list = loadMoodList("MoodList2.json"); + changeLoxoneState("moodlist", list); + List options = new ArrayList<>(); + options.add(new StateOption("4", "Study Only Changed Name")); // changed name + options.add(new StateOption("5", "Low Lights")); // same as in list 1 + options.add(new StateOption("6", "Play Lights")); // changed id + options.add(new StateOption("7", "New Mood 1")); + options.add(new StateOption("8", "New Mood 2")); + options.add(new StateOption("777", "Bright")); + options.add(new StateOption("778", "Off")); + options.add(new StateOption("779", "New Off")); + testMoodList(options, 779); + } + + private LxControlMood getMood(Integer id) { + LxControl ctrl = handler.extraControls.get(new LxUuid("1076668f-0101-7076-ffff403fb0c34b9e-M" + id)); + assertNotNull(ctrl); + assertThat(ctrl, is(instanceOf(LxControlMood.class))); + return (LxControlMood) ctrl; + } + + private void clearMoodList() { + changeLoxoneState("moodlist", "[]"); + List options = new ArrayList<>(); + testMoodList(options, 0); + } + + private void testMoodList(List options, Integer offId) { + assertEquals(options.size(), handler.extraControls.size()); + if (options.size() == 0) { + return; + } + Integer min = null; + Integer max = null; + for (StateOption o : options) { + testMood(o.getLabel(), o.getValue(), o.getValue().equals(offId.toString())); + Integer id = Integer.parseInt(o.getValue()); + assertNotNull(id); + if (min == null || id < min) { + min = id; + } + if (max == null || id > max) { + max = id; + } + } + assertNotNull(min); + assertNotNull(max); + testChannel("Number", null, new BigDecimal(min), new BigDecimal(max), BigDecimal.ONE, null, false, options); + } + + private void testMood(String name, String id, boolean isStatic) { + LxControlMood mood = getMood(Integer.parseInt(id)); + assertEquals(new LxUuid("0b734138-03ac-03f0-ffff403fb0c34b9e"), mood.getRoom().getUuid()); + assertEquals(new LxUuid("0b734138-033e-02d4-ffff403fb0c34b9e"), mood.getCategory().getUuid()); + assertEquals(name, mood.getName()); + assertEquals(id, mood.getId().toString()); + if (isStatic) { + assertEquals(0, mood.getChannels().size()); + } else { + assertEquals(1, mood.getChannels().size()); + testChannel(mood, "Switch"); + } + } + + private String loadMoodList(String name) { + InputStream stream = LxControlLightControllerV2Test.class.getResourceAsStream(name); + assertNotNull(stream); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + assertNotNull(reader); + String msg = reader.lines().collect(Collectors.joining(System.lineSeparator())); + assertNotNull(msg); + // mood list comes as a single line JSON from Loxone Miniserver + msg = msg.replaceAll("[\\t+\\n+]", ""); + return msg; + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlPushbuttonTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlPushbuttonTest.java new file mode 100644 index 0000000000000..a04d69f56881a --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlPushbuttonTest.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlPushbutton} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlPushbuttonTest extends LxControlSwitchTest { + @Override + @Before + public void setup() { + setupControl("0e3684cc-026e-28e0-ffff403fb0c34b9e", "0b734138-038c-035e-ffff403fb0c34b9e", + "0b734138-033e-02d8-ffff403fb0c34b9e", "Kitchen All Blinds Up"); + } + + @Override + @Test + public void testControlCreation() { + testControlCreation(LxControlPushbutton.class, 1, 0, 1, 1, 1); + } + + @Override + @Test + public void testChannels() { + testChannel("Switch"); + } + + @Override + @Test + public void testCommands() { + for (int i = 0; i < 100; i++) { + executeCommand(OnOffType.ON); + testAction("Pulse"); + executeCommand(DecimalType.ZERO); + testAction(null); + executeCommand(OnOffType.OFF); + testAction("Off"); + executeCommand(StringType.EMPTY); + testAction(null); + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlRadioTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlRadioTest.java new file mode 100644 index 0000000000000..b7724f486df9f --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlRadioTest.java @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.types.StateOption; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlRadio} - variant with no 'all off' selection + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlRadioTest extends LxControlTest { + @Before + public void setup() { + setupControl("4255054f-0355-af47-ffff403fb0c34b9e", "11d68cf4-0080-7697-ffff403fb0c34b9e", + "0fe650c2-0004-d446-ffff504f9410790f", "Sprinkler 1"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlRadio.class, 2, 0, 1, 1, 1); + } + + @Test + public void testChannels() { + List opts = new ArrayList<>(); + for (Integer i = 1; i <= 6; i++) { + opts.add(new StateOption(i.toString(), "Sprinkler " + i.toString())); + } + testChannel("Number", null, BigDecimal.ZERO, new BigDecimal(16), BigDecimal.ONE, null, false, opts); + } + + @Test + public void testLoxoneCommonStateChanges() { + testChannelState(null); + for (int i = 1; i <= 6; i++) { + changeLoxoneState("activeoutput", i * 1.0); + testChannelState(new DecimalType(i)); + } + changeLoxoneState("activeoutput", 0.5); + testChannelState(null); + + changeLoxoneState("activeoutput", 7.0); + testChannelState(null); + changeLoxoneState("activeoutput", 17.0); + testChannelState(null); + } + + @Test + public void testLoxoneZeroIndexChanges() { + changeLoxoneState("activeoutput", 0.0); + testChannelState(null); + } + + @Test + public void testCommonCommands() { + for (Integer i = 1; i <= 6; i++) { + executeCommand(new DecimalType(i)); + testAction(i.toString()); + } + executeCommand(new DecimalType(7)); + testAction(null); + executeCommand(new DecimalType(17)); + testAction(null); + + executeCommand(PercentType.HUNDRED); + testAction(null); + } + + @Test + public void testZeroIndexCommands() { + executeCommand(DecimalType.ZERO); + testAction(null); + executeCommand(OnOffType.OFF); + testAction(null); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlRadioTest2.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlRadioTest2.java new file mode 100644 index 0000000000000..01862b8be61a1 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlRadioTest2.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.types.StateOption; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlRadio} - variant with 'all off' selection + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlRadioTest2 extends LxControlRadioTest { + @Override + @Before + public void setup() { + setupControl("1255054f-0355-af47-ffff403fb0c34b9e", "11d68cf4-0080-7697-ffff403fb0c34b9e", + "0fe650c2-0004-d446-ffff504f9410790f", "Sprinkler 2"); + } + + @Override + @Test + public void testChannels() { + List opts = new ArrayList<>(); + for (Integer i = 1; i <= 6; i++) { + opts.add(new StateOption(i.toString(), "Sprinkler " + i.toString())); + } + opts.add(new StateOption("0", "All Off")); + testChannel("Number", null, BigDecimal.ZERO, new BigDecimal(16), BigDecimal.ONE, null, false, opts); + } + + @Override + @Test + public void testLoxoneZeroIndexChanges() { + changeLoxoneState("activeoutput", 0.0); + testChannelState(new DecimalType(0)); + } + + @Override + @Test + public void testZeroIndexCommands() { + executeCommand(DecimalType.ZERO); + testAction("reset"); + executeCommand(OnOffType.OFF); + testAction("reset"); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlSliderTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlSliderTest.java new file mode 100644 index 0000000000000..e9fbb9d927f83 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlSliderTest.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlSlider} - this is actually the same control as up down analog + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlSliderTest extends LxControlUpDownAnalogTest { + @Override + @Before + public void setup() { + min = 120.0; + max = 450.0; + step = 3.333; + format = "%.1f"; + setupControl("131fb314-0370-c93c-ffff403fb0c34b9e", "0b734138-037d-034e-ffff403fb0c34b9e", + "0fe650c2-0004-d446-ffff504f9410790f", "Slider Virtual Input"); + } + + @Override + @Test + public void testControlCreation() { + testControlCreation(LxControlSlider.class, 1, 0, 1, 1, 2); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlSwitchTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlSwitchTest.java new file mode 100644 index 0000000000000..4a5d3bf453cdb --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlSwitchTest.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.types.UnDefType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlSwitch} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlSwitchTest extends LxControlTest { + @Before + public void setup() { + setupControl("0f2f6b5d-0349-83b1-ffff403fb0c34b9e", "0b734138-038c-0382-ffff403fb0c34b9e", + "0b734138-033e-02d4-ffff403fb0c34b9e", "Switch Button"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlSwitch.class, 1, 0, 1, 1, 1); + } + + @Test + public void testChannels() { + testChannel("Switch"); + } + + @Test + public void testLoxoneStateChanges() { + for (Double i = 2.0; i < 100.0; i++) { + changeLoxoneState("active", 0.0); + testChannelState(OnOffType.OFF); + changeLoxoneState("active", 1.0); + testChannelState(OnOffType.ON); + changeLoxoneState("active", 1.0 / i); + testChannelState(UnDefType.UNDEF); + } + } + + @Test + public void testCommands() { + for (int i = 0; i < 100; i++) { + executeCommand(OnOffType.ON); + testAction("On"); + executeCommand(DecimalType.ZERO); + testAction(null); + executeCommand(OnOffType.OFF); + testAction("Off"); + executeCommand(StringType.EMPTY); + testAction(null); + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlTest.java new file mode 100644 index 0000000000000..56388173885c3 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlTest.java @@ -0,0 +1,242 @@ +/** + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.type.ChannelKind; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.StateDescription; +import org.eclipse.smarthome.core.types.StateOption; +import org.openhab.binding.loxone.internal.types.LxCategory; +import org.openhab.binding.loxone.internal.types.LxContainer; +import org.openhab.binding.loxone.internal.types.LxState; +import org.openhab.binding.loxone.internal.types.LxUuid; + +/** + * Common test framework class for all (@link LxControl} objects + * + * @author Pawel Pieczul - initial contribution + * + */ +class LxControlTest { + LxServerHandlerDummy handler; + LxUuid controlUuid; + LxUuid roomUuid; + LxUuid categoryUuid; + String controlName; + + void setupControl(String controlUuid, String roomUuid, String categoryUuid, String controlName) { + this.controlUuid = new LxUuid(controlUuid); + this.roomUuid = new LxUuid(roomUuid); + this.categoryUuid = new LxUuid(categoryUuid); + this.controlName = controlName; + handler = new LxServerHandlerDummy(); + handler.loadConfiguration(); + } + + void testControlCreation(Class testClass, int numberOfControls, int numberOfSubcontrols, + int numberOfChannels, int numberOfChannelsWithSubs, int numberOfStates) { + assertEquals(numberOfControls, numberOfControls(testClass)); + LxControl c = getControl(controlUuid); + assertNotNull(c); + Map subC = c.getSubControls(); + assertNotNull(subC); + assertEquals(numberOfSubcontrols, subC.size()); + assertEquals(controlUuid, c.getUuid()); + assertEquals(controlName, c.getName()); + assertEquals(controlName, c.getLabel()); + LxContainer room = c.getRoom(); + assertNotNull(room); + assertEquals(roomUuid, room.getUuid()); + LxCategory cat = c.getCategory(); + assertNotNull(cat); + assertEquals(categoryUuid, cat.getUuid()); + assertEquals(numberOfChannels, c.getChannels().size()); + assertEquals(numberOfChannelsWithSubs, c.getChannelsWithSubcontrols().size()); + assertEquals(numberOfStates, c.getStates().size()); + } + + void testChannel(LxControl ctrl, String itemType, String namePostFix, BigDecimal min, BigDecimal max, + BigDecimal step, String format, Boolean readOnly, List options) { + assertNotNull(ctrl); + Channel c = getChannel(getExpectedName(ctrl.getLabel(), ctrl.getRoom().getName(), namePostFix), ctrl); + assertNotNull(c); + assertNotNull(c.getUID()); + assertNotNull(c.getDescription()); + assertEquals(itemType, c.getAcceptedItemType()); + assertEquals(ChannelKind.STATE, c.getKind()); + StateDescription d = handler.stateDescriptions.get(c.getUID()); + if (readOnly != null || min != null || max != null || step != null || format != null || options != null) { + assertNotNull(d); + assertEquals(min, d.getMinimum()); + assertEquals(max, d.getMaximum()); + assertEquals(step, d.getStep()); + assertEquals(format, d.getPattern()); + assertEquals(readOnly, d.isReadOnly()); + List opts = d.getOptions(); + if (options == null) { + assertTrue(opts.isEmpty()); + } else { + assertNotNull(opts); + assertEquals(options.size(), opts.size()); + options.forEach(o -> { + String label = o.getLabel(); + long num = opts.stream().filter( + f -> label != null && label.equals(f.getLabel()) && o.getValue().equals(f.getValue())) + .collect(Collectors.counting()); + assertEquals(1, num); + }); + } + } else { + assertNull(d); + } + } + + void testChannel(String itemType, String namePostFix, BigDecimal min, BigDecimal max, BigDecimal step, + String format, Boolean readOnly, List options) { + LxControl ctrl = getControl(controlUuid); + testChannel(ctrl, itemType, namePostFix, min, max, step, format, readOnly, options); + } + + void testChannel(String itemType) { + testChannel(itemType, null, null, null, null, null, null, null); + } + + void testChannel(LxControl ctrl, String itemType) { + testChannel(ctrl, itemType, null, null, null, null, null, null, null); + } + + void testChannel(String itemType, String namePostFix) { + testChannel(itemType, namePostFix, null, null, null, null, null, null); + } + + State getChannelState(LxControl ctrl, String namePostFix) { + assertNotNull(ctrl); + Channel c = getChannel(getExpectedName(ctrl.getLabel(), ctrl.getRoom().getName(), namePostFix), ctrl); + assertNotNull(c); + return ctrl.getChannelState(c.getUID()); + } + + State getChannelState(String namePostFix) { + LxControl ctrl = getControl(controlUuid); + return getChannelState(ctrl, namePostFix); + } + + void testChannelState(LxControl ctrl, String namePostFix, State expectedValue) { + State current = getChannelState(ctrl, namePostFix); + if (expectedValue != null) { + assertNotNull(current); + } + assertEquals(expectedValue, current); + } + + void testChannelState(String namePostFix, State expectedValue) { + LxControl ctrl = getControl(controlUuid); + testChannelState(ctrl, namePostFix, expectedValue); + } + + void testChannelState(State expectedValue) { + testChannelState((String) null, expectedValue); + } + + void testChannelState(LxControl ctrl, State expectedValue) { + testChannelState(ctrl, null, expectedValue); + } + + void changeLoxoneState(String stateName, Object value) { + LxControl ctrl = getControl(controlUuid); + assertNotNull(ctrl); + LxState state = ctrl.getStates().get(stateName); + assertNotNull(state); + state.setStateValue(value); + } + + void executeCommand(LxControl ctrl, String namePostFix, Command command) { + assertNotNull(ctrl); + Channel c = getChannel(getExpectedName(ctrl.getLabel(), ctrl.getRoom().getName(), namePostFix), ctrl); + assertNotNull(c); + try { + ctrl.handleCommand(c.getUID(), command); + } catch (IOException e) { + fail("This exception should never happen in test environment."); + } + } + + void executeCommand(String namePostFix, Command command) { + LxControl ctrl = getControl(controlUuid); + executeCommand(ctrl, namePostFix, command); + } + + void executeCommand(LxControl ctrl, Command command) { + executeCommand(ctrl, null, command); + } + + void executeCommand(Command command) { + executeCommand((String) null, command); + } + + void testAction(String expectedAction, int numberOfActions) { + assertEquals(numberOfActions, handler.actionQueue.size()); + if (numberOfActions > 0) { + String action = handler.actionQueue.poll(); + assertNotNull(action); + assertEquals(controlUuid + "/" + expectedAction, action); + } + } + + void testAction(String expectedAction) { + if (expectedAction == null) { + testAction(null, 0); + } else { + testAction(expectedAction, 1); + } + } + + void testSubControl(Type type, String name) { + LxControl ctrl = getControl(controlUuid); + assertNotNull(ctrl); + long n = ctrl.getSubControls().values().stream().filter(c -> name.equals(c.getName())) + .collect(Collectors.counting()); + assertEquals(1L, n); + } + + private Channel getChannel(String name, LxControl c) { + List channels = c.getChannels(); + List filtered = channels.stream().filter(a -> name.equals(a.getLabel())).collect(Collectors.toList()); + assertEquals(1, filtered.size()); + return filtered.get(0); + } + + private long numberOfControls(Class c) { + Collection v = handler.controls.values(); + return v.stream().filter(o -> c.equals(o.getClass())).collect(Collectors.counting()); + } + + private LxControl getControl(LxUuid uuid) { + return handler.controls.get(uuid); + } + + private String getExpectedName(String controlName, String roomName, String postFix) { + return roomName + " / " + controlName + (postFix != null ? postFix : ""); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlTextStateTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlTextStateTest.java new file mode 100644 index 0000000000000..324c8c15bd99b --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlTextStateTest.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.eclipse.smarthome.core.library.types.StringType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlTextState} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlTextStateTest extends LxControlTest { + @Before + public void setup() { + setupControl("106bed36-016d-6dd8-ffffffe6109fb656", "0b734138-038c-0386-ffff403fb0c34b9e", + "0fe665f4-0161-4773-ffff403fb0c34b9e", "Gate"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlTextState.class, 1, 0, 1, 1, 1); + } + + @Test + public void testChannels() { + testChannel("String", null, null, null, null, null, true, null); + } + + @Test + public void testLoxoneStateChanges() { + String s = new String(); + for (char c = ' '; c <= '~'; c++) { + changeLoxoneState("textandicon", s); + testChannelState(new StringType(s)); + s = s + c; + } + s = s + "\n\tabc\ndef\n"; + changeLoxoneState("textandicon", s); + testChannelState(new StringType(s)); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlTimedSwitchTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlTimedSwitchTest.java new file mode 100644 index 0000000000000..6a423c7508c95 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlTimedSwitchTest.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import java.math.BigDecimal; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlTimedSwitch} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlTimedSwitchTest extends LxControlTest { + private static final String DELAY_CHANNEL = " / Deactivation Delay"; + + @Before + public void setup() { + setupControl("1326771c-030e-3a7c-ffff403fb0c34b9e", "0b734138-037d-034e-ffff403fb0c34b9e", + "0fe650c2-0004-d446-ffff504f9410790f", "Stairwell Light Switch"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlTimedSwitch.class, 1, 0, 2, 2, 2); + } + + @Test + public void testChannels() { + testChannel("Switch"); + testChannel("Number", DELAY_CHANNEL, new BigDecimal(-1), null, null, null, true, null); + } + + @Test + public void testLoxoneStateChanges() { + testChannelState(null); + testChannelState(DELAY_CHANNEL, null); + changeLoxoneState("deactivationdelaytotal", 100.0); + for (int i = 0; i < 100; i++) { + changeLoxoneState("deactivationdelay", 0.0); + testChannelState(OnOffType.OFF); + testChannelState(DELAY_CHANNEL, DecimalType.ZERO); + changeLoxoneState("deactivationdelay", -1.0); + testChannelState(OnOffType.ON); + testChannelState(DELAY_CHANNEL, DecimalType.ZERO); + } + for (Double i = 100.0; i >= 1.0; i--) { + changeLoxoneState("deactivationdelay", i); + testChannelState(OnOffType.ON); + testChannelState(DELAY_CHANNEL, new DecimalType(i)); + } + changeLoxoneState("deactivationdelay", 0.0); + testChannelState(OnOffType.OFF); + testChannelState(DELAY_CHANNEL, DecimalType.ZERO); + } + + @Test + public void testCommands() { + for (int i = 0; i < 100; i++) { + executeCommand(OnOffType.ON); + testAction("Pulse"); + executeCommand(OnOffType.OFF); + testAction("Off"); + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlTrackerTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlTrackerTest.java new file mode 100644 index 0000000000000..1e4895a1494b5 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlTrackerTest.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.eclipse.smarthome.core.library.types.StringType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlTracker} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlTrackerTest extends LxControlTest { + @Before + public void setup() { + setupControl("132aa43b-01d4-56ea-ffff403fb0c34b9e", "0b734138-037d-034e-ffff403fb0c34b9e", + "0fe650c2-0004-d446-ffff504f9410790f", "Tracker Control"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlTracker.class, 1, 0, 1, 1, 1); + } + + @Test + public void testChannels() { + testChannel("String", null, null, null, null, null, true, null); + } + + @Test + public void testLoxoneStateChanges() { + for (int i = 0; i < 20; i++) { + String s = new String(); + for (int j = 0; j < i; j++) { + for (char c = 'a'; c <= 'a' + j; c++) { + s = s + c; + } + if (j != i - 1) { + s = s + '|'; + } + } + changeLoxoneState("entries", s); + testChannelState(new StringType(s)); + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlUpDownAnalogTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlUpDownAnalogTest.java new file mode 100644 index 0000000000000..2ee861f2de133 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlUpDownAnalogTest.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import java.math.BigDecimal; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.UnDefType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlUpDownAnalog} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlUpDownAnalogTest extends LxControlTest { + Double min; + Double max; + Double step; + String format; + + @Before + public void setup() { + min = 50.0; + max = 150.0; + step = 10.0; + format = "%.1f"; + setupControl("131b1a96-02b9-f6e9-eeff403fb0c34b9e", "0b734138-037d-034e-ffff403fb0c34b9e", + "0fe650c2-0004-d446-ffff504f9410790f", "Up Down Analog Input"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlUpDownAnalog.class, 1, 0, 1, 1, 2); + } + + @Test + public void testChannels() { + testChannel("Number", null, new BigDecimal(min), new BigDecimal(max), new BigDecimal(step), format, false, + null); + } + + @Test + public void testLoxoneStateChanges() { + testChannelState(null); + for (int j = 0; j < 2; j++) { + changeLoxoneState("error", 0.0); + for (Double i = min - 50.0; i < min; i += 0.5) { + changeLoxoneState("value", i); + testChannelState(null); + } + for (Double i = min; i <= max; i += 0.5) { + changeLoxoneState("value", i); + testChannelState(new DecimalType(i)); + } + for (Double i = max + 0.5; i < max + 50.0; i += 0.5) { + changeLoxoneState("value", i); + testChannelState(null); + } + changeLoxoneState("error", 1.0); + for (Double i = min - 50.0; i < max + 50.0; i += 0.5) { + changeLoxoneState("value", i); + testChannelState(UnDefType.UNDEF); + } + } + } + + @Test + public void testCommands() { + for (Double i = min - 50.0; i < min; i += 0.5) { + executeCommand(new DecimalType(i)); + testAction(null); + } + for (Double i = min; i <= max; i += 0.5) { + executeCommand(new DecimalType(i)); + testAction(i.toString()); + } + for (Double i = max + 0.5; i < max + 50.0; i += 0.5) { + executeCommand(new DecimalType(i)); + testAction(null); + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlUpDownDigitalTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlUpDownDigitalTest.java new file mode 100644 index 0000000000000..c81c38437c76b --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlUpDownDigitalTest.java @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlUpDownDigital} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlUpDownDigitalTest extends LxControlTest { + String upChannel; + String downChannel; + + @Before + public void setup() { + upChannel = " / Up"; + downChannel = " / Down"; + setupControl("0fd08ca6-01a6-d72a-ffff403fb0c34b9e", "0b734138-037d-034e-ffff403fb0c34b9e", + "0b734138-033e-02d4-ffff403fb0c34b9e", "First Floor Scene"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlUpDownDigital.class, 1, 0, 2, 2, 0); + } + + @Test + public void testChannels() { + testChannel("Switch", upChannel); + testChannel("Switch", downChannel); + } + + @Test + public void testCommands() { + testChannelState(upChannel, OnOffType.OFF); + testChannelState(downChannel, OnOffType.OFF); + + executeCommand(upChannel, OnOffType.ON); + testAction("UpOn"); + testChannelState(upChannel, OnOffType.ON); + testChannelState(downChannel, OnOffType.OFF); + executeCommand(upChannel, OnOffType.OFF); + testAction("UpOff"); + testChannelState(upChannel, OnOffType.OFF); + testChannelState(downChannel, OnOffType.OFF); + + executeCommand(downChannel, OnOffType.ON); + testAction("DownOn"); + testChannelState(upChannel, OnOffType.OFF); + testChannelState(downChannel, OnOffType.ON); + executeCommand(downChannel, OnOffType.OFF); + testAction("DownOff"); + testChannelState(upChannel, OnOffType.OFF); + testChannelState(downChannel, OnOffType.OFF); + + executeCommand(upChannel, OnOffType.ON); + testAction("UpOn"); + testChannelState(upChannel, OnOffType.ON); + testChannelState(downChannel, OnOffType.OFF); + executeCommand(downChannel, OnOffType.ON); + testAction("DownOn"); + testChannelState(upChannel, OnOffType.OFF); + testChannelState(downChannel, OnOffType.ON); + + executeCommand(upChannel, OnOffType.ON); + testAction("UpOn"); + testChannelState(upChannel, OnOffType.ON); + testChannelState(downChannel, OnOffType.OFF); + + executeCommand(upChannel, OnOffType.OFF); + testAction("UpOff"); + testChannelState(upChannel, OnOffType.OFF); + testChannelState(downChannel, OnOffType.OFF); + + executeCommand(upChannel, OnOffType.OFF); + testAction(null); + testChannelState(upChannel, OnOffType.OFF); + testChannelState(downChannel, OnOffType.OFF); + + executeCommand(downChannel, OnOffType.OFF); + testAction(null); + testChannelState(upChannel, OnOffType.OFF); + testChannelState(downChannel, OnOffType.OFF); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlValueSelectorIncrTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlValueSelectorIncrTest.java new file mode 100644 index 0000000000000..1e1e9234dda72 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlValueSelectorIncrTest.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlValueSelector} - version which allows for increasing only + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlValueSelectorIncrTest extends LxControlValueSelectorTest { + @Override + @Before + public void setup() { + setupControl("132a7b7e-0022-3aac-ffff403fb0c34b9e", "0b734138-037d-034e-ffff403fb0c34b9e", + "0fe650c2-0004-d446-ffff504f9410790f", "Selection Switch Increase Only"); + } + + @Override + @Test + public void testIncreaseDecreaseCommands() { + changeLoxoneState("min", 123.0); + changeLoxoneState("max", 456.0); + changeLoxoneState("step", 23.0); + changeLoxoneState("value", 400.0); + testChannelState(new PercentType(83)); + executeCommand(IncreaseDecreaseType.INCREASE); + testAction("423.0"); + changeLoxoneState("value", 423.0); + testChannelState(new PercentType(90)); + executeCommand(IncreaseDecreaseType.INCREASE); + testAction("446.0"); + changeLoxoneState("value", 446.0); + testChannelState(new PercentType(96)); + executeCommand(IncreaseDecreaseType.INCREASE); + testAction("456.0"); // trim to max + changeLoxoneState("value", 456.0); + testChannelState(PercentType.HUNDRED); + executeCommand(IncreaseDecreaseType.DECREASE); + testAction(null); + changeLoxoneState("step", 100.0); + executeCommand(IncreaseDecreaseType.DECREASE); + testAction(null); + changeLoxoneState("value", 123.0); + testChannelState(PercentType.ZERO); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlValueSelectorTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlValueSelectorTest.java new file mode 100644 index 0000000000000..67ac449a97e8c --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlValueSelectorTest.java @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import java.math.BigDecimal; + +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.StopMoveType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlValueSelector} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlValueSelectorTest extends LxControlTest { + private static final String NUMBER_CHANNEL = " / Number"; + + @Before + public void setup() { + setupControl("432a7b7e-0022-3aac-ffff403fb0c34b9e", "0b734138-037d-034e-ffff403fb0c34b9e", + "0fe650c2-0004-d446-ffff504f9410790f", "Selection Switch"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlValueSelector.class, 2, 0, 2, 2, 4); + } + + @Test + public void testChannels() { + testChannel("Dimmer"); + testChannel("Number", NUMBER_CHANNEL); + } + + @Test + public void testLoxoneValueMinMaxChanges() { + // filling in missing state values + testChannelState(null); + changeLoxoneState("step", 1.0); + testChannelState(null); + changeLoxoneState("value", 50.0); + testChannelState(null); + changeLoxoneState("min", 0.0); + testChannelState(null); + changeLoxoneState("max", 100.0); + testChannelState(new PercentType(50)); + testChannel("Dimmer", null, BigDecimal.ZERO, new BigDecimal(100), BigDecimal.ONE, "%.0f", false, null); + testChannel("Number", NUMBER_CHANNEL, BigDecimal.ZERO, new BigDecimal(100), BigDecimal.ONE, "%.0f", false, + null); + + // potential division by zero + changeLoxoneState("min", 55.0); + changeLoxoneState("max", 55.0); + testChannelState(null); + + changeLoxoneState("min", 200.0); + changeLoxoneState("max", 400.0); + testChannel("Dimmer", null, new BigDecimal(200), new BigDecimal(400), BigDecimal.ONE, "%.0f", false, null); + testChannel("Number", NUMBER_CHANNEL, new BigDecimal(200), new BigDecimal(400), BigDecimal.ONE, "%.0f", false, + null); + + // out of range + changeLoxoneState("value", 199.9); + testChannelState(null); + changeLoxoneState("value", 400.1); + testChannelState(null); + changeLoxoneState("value", 0.0); + testChannelState(null); + // scaling within range + changeLoxoneState("value", 200.0); + testChannelState(PercentType.ZERO); + changeLoxoneState("value", 400.0); + testChannelState(PercentType.HUNDRED); + changeLoxoneState("value", 300.0); + testChannelState(new PercentType(50)); + // reversed range boundaries + changeLoxoneState("min", 50.0); + changeLoxoneState("max", 20.0); + changeLoxoneState("value", 30.0); + testChannelState(null); + } + + @Test + public void testOnOffPercentCommands() { + changeLoxoneState("min", 1000.0); + changeLoxoneState("max", 3000.0); + changeLoxoneState("step", 100.0); + + executeCommand(OnOffType.ON); + testAction("3000.0"); + executeCommand(OnOffType.OFF); + testAction("1000.0"); + + executeCommand(PercentType.HUNDRED); + testAction("3000.0"); + executeCommand(new PercentType(50)); + testAction("2000.0"); + executeCommand(new PercentType(1)); + testAction("1020.0"); + executeCommand(PercentType.ZERO); + testAction("1000.0"); + + executeCommand(StopMoveType.MOVE); + testAction(null); + } + + @Test + public void testIncreaseDecreaseCommands() { + changeLoxoneState("min", 123.0); + changeLoxoneState("max", 456.0); + changeLoxoneState("step", 23.0); + changeLoxoneState("value", 400.0); + testChannelState(new PercentType(83)); + executeCommand(IncreaseDecreaseType.INCREASE); + testAction("423.0"); + changeLoxoneState("value", 423.0); + testChannelState(new PercentType(90)); + executeCommand(IncreaseDecreaseType.INCREASE); + testAction("446.0"); + changeLoxoneState("value", 446.0); + testChannelState(new PercentType(96)); + executeCommand(IncreaseDecreaseType.INCREASE); + testAction("456.0"); // trim to max + changeLoxoneState("value", 456.0); + testChannelState(PercentType.HUNDRED); + changeLoxoneState("step", 100.0); + executeCommand(IncreaseDecreaseType.DECREASE); + testAction("356.0"); + changeLoxoneState("value", 356.0); + testChannelState(new PercentType(69)); + executeCommand(IncreaseDecreaseType.DECREASE); + testAction("256.0"); + changeLoxoneState("value", 256.0); + testChannelState(new PercentType(39)); + executeCommand(IncreaseDecreaseType.DECREASE); + testAction("156.0"); + changeLoxoneState("value", 156.0); + testChannelState(new PercentType(9)); + executeCommand(IncreaseDecreaseType.DECREASE); + testAction("123.0"); // trim to min + changeLoxoneState("value", 123.0); + testChannelState(PercentType.ZERO); + } + + @Test + public void testNumberCommands() { + changeLoxoneState("min", 100.0); + changeLoxoneState("max", 300.0); + changeLoxoneState("step", 10.0); + for (Double i = 0.0; i < 100.0; i += 0.35) { + executeCommand(NUMBER_CHANNEL, new DecimalType(i)); + testAction(null); + } + for (Double i = 100.0; i <= 300.0; i += 0.47) { + executeCommand(NUMBER_CHANNEL, new DecimalType(i)); + testAction(i.toString()); + } + for (Double i = 300.01; i < 400.0; i += 0.59) { + executeCommand(NUMBER_CHANNEL, new DecimalType(i)); + testAction(null); + } + } + + @Test + public void testLoxoneNumberChanges() { + testChannelState(null); + changeLoxoneState("min", 100.0); + changeLoxoneState("max", 300.0); + changeLoxoneState("step", 10.0); + for (Double i = 0.0; i < 100.0; i += 0.35) { + changeLoxoneState("value", i); + testChannelState(NUMBER_CHANNEL, null); + } + for (Double i = 100.0; i <= 300.0; i += 0.47) { + changeLoxoneState("value", i); + testChannelState(NUMBER_CHANNEL, new DecimalType(i)); + } + for (Double i = 300.01; i < 400.0; i += 0.59) { + changeLoxoneState("value", i); + testChannelState(NUMBER_CHANNEL, null); + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlWebPageTest.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlWebPageTest.java new file mode 100644 index 0000000000000..d75e78a0af9b5 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxControlWebPageTest.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import org.eclipse.smarthome.core.library.types.StringType; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for (@link LxControlWebPage} + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxControlWebPageTest extends LxControlTest { + @Before + public void setup() { + setupControl("132d2791-00f8-d532-ffff403fb0c34b9e", "0b734138-037d-034e-ffff403fb0c34b9e", + "0fe650c2-0004-d446-ffff504f9410790f", "Webpage 1"); + } + + @Test + public void testControlCreation() { + testControlCreation(LxControlWebPage.class, 1, 0, 2, 2, 0); + } + + @Test + public void testChannels() { + testChannel("String", " / URL", null, null, null, null, true, null); + testChannel("String", " / URL HD", null, null, null, null, true, null); + } + + @Test + public void testChannelStates() { + testChannelState(" / URL", new StringType("http://low.res")); + testChannelState(" / URL HD", new StringType("http://hi.res")); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxServerHandlerDummy.java b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxServerHandlerDummy.java new file mode 100644 index 0000000000000..55f07016b0931 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/java/org/openhab/binding/loxone/internal/controls/LxServerHandlerDummy.java @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.loxone.internal.controls; + +import static org.junit.Assert.assertNotNull; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.stream.Collectors; + +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.StateDescription; +import org.openhab.binding.loxone.internal.LxServerHandlerApi; +import org.openhab.binding.loxone.internal.types.LxConfig; +import org.openhab.binding.loxone.internal.types.LxUuid; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * Dummy implementation of thing handler and its API towards controls. + * + * @author Pawel Pieczul - initial contribution + * + */ +public class LxServerHandlerDummy implements LxServerHandlerApi { + + Gson gson; + LxConfig config; + + Queue actionQueue = new LinkedList<>(); + + Map controls; + Map extraControls = new HashMap<>(); + Map stateDescriptions = new HashMap<>(); + + public LxServerHandlerDummy() { + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(LxUuid.class, LxUuid.DESERIALIZER); + builder.registerTypeAdapter(LxControl.class, LxControl.DESERIALIZER); + gson = builder.create(); + } + + void loadConfiguration() { + InputStream stream = LxServerHandlerDummy.class.getResourceAsStream("LoxAPP3.json"); + assertNotNull(stream); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + assertNotNull(reader); + String msg = reader.lines().collect(Collectors.joining(System.lineSeparator())); + assertNotNull(msg); + + stateDescriptions.clear(); + + LxConfig config = gson.fromJson(msg, LxConfig.class); + config.finalize(this); + controls = config.controls; + assertNotNull(controls); + } + + @Override + public void sendAction(LxUuid id, String operation) throws IOException { + actionQueue.add(id + "/" + operation); + } + + @Override + public void addControl(LxControl control) { + extraControls.put(control.getUuid(), control); + } + + @Override + public void removeControl(LxControl control) { + LxControl ctrl = extraControls.remove(control.getUuid()); + assertNotNull(ctrl); + } + + @Override + public void setChannelState(ChannelUID channelId, State state) { + // TODO Auto-generated method stub + + } + + @Override + public void setChannelStateDescription(ChannelUID channelId, StateDescription description) { + assertNotNull(channelId); + assertNotNull(description); + stateDescriptions.put(channelId, description); + } + + @Override + public String getSetting(String name) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setSettings(Map properties) { + // TODO Auto-generated method stub + + } + + @Override + public Gson getGson() { + return gson; + } + + @Override + public ThingUID getThingId() { + return new ThingUID("loxone:miniserver:12345678"); + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/resources/org/openhab/binding/loxone/internal/controls/LoxAPP3.json b/bundles/org.openhab.binding.loxone/src/test/resources/org/openhab/binding/loxone/internal/controls/LoxAPP3.json new file mode 100644 index 0000000000000..32538ffaa604a --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/resources/org/openhab/binding/loxone/internal/controls/LoxAPP3.json @@ -0,0 +1,1057 @@ +{ + "lastModified": "2019-03-13 16:39:54", + "msInfo": { + "serialNr": "123456789ABC", + "msName": "Jupiter Mining Corporation", + "projectName": "Red Dwarf", + "localUrl": "192.168.0.100", + "remoteUrl": "dns.loxonecloud.com/123456789ABC", + "tempUnit": 0, + "currency": "€", + "squareMeasure": "m²", + "location": "Moon", + "languageCode": "ENG", + "heatPeriodStart": "10-01", + "heatPeriodEnd": "04-01", + "coolPeriodStart": "05-01", + "coolPeriodEnd": "09-30", + "catTitle": "Category", + "roomTitle": "Room", + "miniserverType": 0, + "deviceMonitor": "1fb2b98d-0341-055a-ffff104fe410790f", + "currentUser": { + "name": "kryten", + "uuid": "3e40da88-0262-8434-ffef403fb0c34b9e", + "isAdmin": true, + "changePassword": true, + "userRights": 2047 + } + }, + "globalStates": { + "operatingMode": "b64cbbfe-f905-4111-8bf1e2111bb0ede9", + "sunrise": "cfe0a836-f1a0-495e-afc91a6e3c0f2263", + "sunset": "4131ac4d-21cb-4553-b9bf8902b6349d5c", + "pastTasks": "d7401b6e-10e8-4a52-a888b0be59b67379", + "plannedTasks": "af924d7b-5a56-4621-851f8d20adae928b", + "notifications": "41672eff-c2ef-4b8d-adc8d2965ce4ec99", + "modifications": "cf69318c-496f-46fb-9e2d32b97b7210bc", + "favColorSequences": "e3742442-ceff-47e3-9adea014e3ef461a", + "favColors": "184c0cb0-b2cc-494f-b56cca806dd5bf84", + "miniserverTime": "267b132e-3ee8-4ec8-bf8e7cc3ce591166" + }, + "operatingModes": { + "0": "Feiertag", + "1": "Urlaub", + "2": "Freier Tag/Schulferien", + "3": "Montag", + "4": "Dienstag", + "5": "Mittwoch", + "6": "Donnerstag", + "7": "Freitag", + "8": "Samstag", + "9": "Sonntag", + "-1": "Alarm", + "-2": "Anwesenheitssimulation", + "-3": "Haus im Tiefschlaf", + "-4": "Erhöhter Wärmebedarf", + "-5": "Party", + "-6": "Alle abwesend" + }, + "rooms": { + "0b734138-03ac-03f0-ffff403fb0c34b9e": { + "uuid": "0b734138-03ac-03f0-ffff403fb0c34b9e", + "name": "Room 1", + "image": "00000000-0000-0031-2100000000000000.svg", + "defaultRating": 1, + "isFavorite": true, + "type": 4 + }, + "0b734138-038c-03aa-ffff403fb0c34b9e": { + "uuid": "0b734138-038c-03aa-ffff403fb0c34b9e", + "name": "Room 2", + "image": "00000000-0000-0018-2100000000000000.svg", + "defaultRating": 10, + "isFavorite": false, + "type": 2 + }, + "0b734138-038c-0386-ffff403fb0c34b9e": { + "uuid": "0b734138-038c-0386-ffff403fb0c34b9e", + "name": "Room 3", + "image": "00000000-0000-000f-2100000000000000.svg", + "defaultRating": 1, + "isFavorite": true, + "type": 5 + }, + "0b734138-037d-034e-ffff403fb0c34b9e": { + "uuid": "0b734138-037d-034e-ffff403fb0c34b9e", + "name": "Room 4", + "image": "00000000-0000-0001-2100000000000000.svg", + "defaultRating": 1, + "isFavorite": true, + "type": 2 + }, + "0fe3a451-0283-2afa-ffff403fb0c34b9e": { + "uuid": "0fe3a451-0283-2afa-ffff403fb0c34b9e", + "name": "Room 5", + "image": "00000000-0000-000c-2100000000000000.svg", + "defaultRating": 0, + "isFavorite": false, + "type": 3 + }, + "11d68cf4-0080-7697-ffff403fb0c34b9e": { + "uuid": "11d68cf4-0080-7697-ffff403fb0c34b9e", + "name": "Room 6", + "image": "00000000-0000-000b-2100000000000000.svg", + "defaultRating": 0, + "isFavorite": false, + "type": 5 + }, + "0b734138-039c-03c8-ffff403fb0c34b9e": { + "uuid": "0b734138-039c-03c8-ffff403fb0c34b9e", + "name": "Room 7", + "image": "00000000-0000-0023-2100000000000000.svg", + "defaultRating": 1, + "isFavorite": true, + "type": 2 + }, + "0b734138-038c-035e-ffff403fb0c34b9e": { + "uuid": "0b734138-038c-035e-ffff403fb0c34b9e", + "name": "Room 8", + "image": "00000000-0000-0005-2100000000000000.svg", + "defaultRating": 1, + "isFavorite": true, + "type": 2 + }, + "0e368d32-014f-4604-ffff403fb0c34b9e": { + "uuid": "0e368d32-014f-4604-ffff403fb0c34b9e", + "name": "Room 9", + "image": "00000000-0000-0004-2100000000000000.svg", + "defaultRating": 1, + "isFavorite": true, + "type": 2 + }, + "0b734138-038c-0382-ffff403fb0c34b9e": { + "uuid": "0b734138-038c-0382-ffff403fb0c34b9e", + "name": "Room 10", + "image": "00000000-0000-000e-2100000000000000.svg", + "defaultRating": 1, + "isFavorite": true, + "type": 3 + } + }, + "cats": { + "0b734138-033e-02d8-ffff403fb0c34b9e": { + "uuid": "0b734138-033e-02d8-ffff403fb0c34b9e", + "name": "Blinds", + "image": "00000000-0000-0003-2000000000000000.svg", + "defaultRating": 10, + "isFavorite": true, + "type": "shading", + "color": "#69C350" + }, + "0fe665f4-0161-4773-ffff403fb0c34b9e": { + "uuid": "0fe665f4-0161-4773-ffff403fb0c34b9e", + "name": "Doors", + "image": "00000000-0000-0010-2000000000000000.svg", + "defaultRating": 0, + "isFavorite": false, + "type": "undefined", + "color": "#69C350" + }, + "0b734138-033e-02d4-ffff403fb0c34b9e": { + "uuid": "0b734138-033e-02d4-ffff403fb0c34b9e", + "name": "Lighting", + "image": "00000000-0000-0002-2000000000000000.svg", + "defaultRating": 10, + "isFavorite": true, + "type": "lights", + "color": "#69C350" + }, + "101b563d-0302-78bd-ffff403fb0c34b9e": { + "uuid": "101b563d-0302-78bd-ffff403fb0c34b9e", + "name": "Motion", + "image": "00000000-0000-000f-2000000000000000.svg", + "defaultRating": 0, + "isFavorite": false, + "type": "undefined", + "color": "#69C350" + }, + "0fe650c2-0004-d446-ffff504f9410790f": { + "uuid": "0fe650c2-0004-d446-ffff504f9410790f", + "name": "Not Assigned", + "image": "", + "defaultRating": 0, + "isFavorite": false, + "type": "undefined", + "color": "#69C350" + }, + "0fb99a98-02df-46f1-ffff403fb0c34b9e": { + "uuid": "0fb99a98-02df-46f1-ffff403fb0c34b9e", + "name": "Temperature", + "image": "00000000-0000-0015-2000000000000000.svg", + "defaultRating": 0, + "isFavorite": false, + "type": "indoortemperature", + "color": "#69C350" + } + }, + "controls": { + "4f2f6b5d-0349-83b1-ffff403fb0c34b9e": { + "name": "Secure Switch Button - should be skipped", + "type": "Switch", + "uuidAction": "4f2f6b5d-0349-83b1-ffff403fb0c34b9e", + "room": "0b734138-038c-0382-ffff403fb0c34b9e", + "cat": "0b734138-033e-02d4-ffff403fb0c34b9e", + "defaultRating": 0, + "isFavorite": false, + "isSecured": true, + "defaultIcon": null, + "states": { + "active": "4f2f6b5d-0349-83b0-ffff25eb4cff8f56" + } + }, + "132d2791-00f8-d532-ffff403fb0c34b9e": { + "name": "Webpage 1", + "type": "Webpage", + "uuidAction": "132d2791-00f8-d532-ffff403fb0c34b9e", + "room": "0b734138-037d-034e-ffff403fb0c34b9e", + "cat": "0fe650c2-0004-d446-ffff504f9410790f", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "details": { + "url": "http://low.res", + "urlHd": "http://hi.res", + "image": "00000000-0000-0019-2200000000000000" + } + }, + "0fec5dc3-003e-8800-ffff403fb0c34b9e": { + "name": "Info Only Analog", + "type": "InfoOnlyAnalog", + "uuidAction": "0fec5dc3-003e-8800-ffff403fb0c34b9e", + "room": "0fe3a451-0283-2afa-ffff403fb0c34b9e", + "cat": "0fe665f4-0161-4773-ffff403fb0c34b9e", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null, + "details": { + "format": "%.2f" + }, + "states": { + "value": "0fec5dc3-003e-8800-ffff403fb0c34b9e" + } + }, + "101b50f7-0306-98fb-ffff403fb0c34b9e": { + "name": "Info Only Digital", + "type": "InfoOnlyDigital", + "uuidAction": "101b50f7-0306-98fb-ffff403fb0c34b9e", + "room": "0e368d32-014f-4604-ffff403fb0c34b9e", + "cat": "101b563d-0302-78bd-ffff403fb0c34b9e", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null, + "details": { + "text": { + "off": "Off", + "on": "On" + }, + "color": { + "off": "#E73246", + "on": "#69C350" + } + }, + "states": { + "active": "101b50f7-0306-98fb-ffff403fb0c34b9e" + } + }, + "0f2f6b5d-0349-83b1-ffff403fb0c34b9e": { + "name": "Switch Button", + "type": "Switch", + "uuidAction": "0f2f6b5d-0349-83b1-ffff403fb0c34b9e", + "room": "0b734138-038c-0382-ffff403fb0c34b9e", + "cat": "0b734138-033e-02d4-ffff403fb0c34b9e", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null, + "states": { + "active": "0f2f6b5d-0349-83b0-ffff25eb4cff8f56" + } + }, + "131b19cd-03c0-640f-ffff403fb0c34b9e": { + "name": "Dimmer Control", + "type": "Dimmer", + "uuidAction": "131b19cd-03c0-640f-ffff403fb0c34b9e", + "room": "0b734138-037d-034e-ffff403fb0c34b9e", + "cat": "0fe650c2-0004-d446-ffff504f9410790f", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "states": { + "position": "131b19cd-03c0-640d-ffffd2fd15b703b6", + "min": "131b19cd-03c0-6409-ffffd2fd15b703b6", + "max": "131b19cd-03c0-640a-ffffd2fd15b703b6", + "step": "131b19cd-03c0-6407-ffffd2fd15b703b6" + } + }, + "0e367c09-0161-e2c1-ffff403fb0c34b9e": { + "name": "Window Blinds", + "type": "Jalousie", + "uuidAction": "0e367c09-0161-e2c1-ffff403fb0c34b9e", + "room": "0e368d32-014f-4604-ffff403fb0c34b9e", + "cat": "0b734138-033e-02d8-ffff403fb0c34b9e", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "details": { + "isAutomatic": true, + "animation": 1 + }, + "states": { + "infoText": "131b0b1e-022c-06e0-ffff504f9410790f", + "up": "0e367c09-0161-e2b9-ffff29379da805e5", + "down": "0e367c09-0161-e2ba-ffff29379da805e5", + "position": "0e367c09-0161-e2bb-ffff29379da805e5", + "shadePosition": "0e367c09-0161-e2bc-ffff29379da805e5", + "safetyActive": "0e367c09-0161-e2be-ffff29379da805e5", + "autoAllowed": "0e367c09-0161-e29c-ffff29379da805e5", + "autoActive": "0e367c09-0161-e2bd-ffff29379da805e5", + "locked": "0e367c09-0161-e2bf-ffff29379da805e5", + "autoInfoText": "131b0b1e-022c-06e1-ffff504f9410790f", + "autoState": "131b0b1e-022c-06e2-ffff504f9410790f" + } + }, + "4255054f-0355-af47-ffff403fb0c34b9e": { + "name": "Sprinkler 1", + "type": "Radio", + "uuidAction": "4255054f-0355-af47-ffff403fb0c34b9e", + "room": "11d68cf4-0080-7697-ffff403fb0c34b9e", + "cat": "0fe650c2-0004-d446-ffff504f9410790f", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null, + "details": { + "allOff": "", + "outputs": { + "1": "Sprinkler 1", + "2": "Sprinkler 2", + "3": "Sprinkler 3", + "4": "Sprinkler 4", + "5": "Sprinkler 5", + "6": "Sprinkler 6" + } + }, + "states": { + "activeOutput": "4255054f-0355-af46-ffff8e584788cd57" + } + }, + "1255054f-0355-af47-ffff403fb0c34b9e": { + "name": "Sprinkler 2", + "type": "Radio", + "uuidAction": "1255054f-0355-af47-ffff403fb0c34b9e", + "room": "11d68cf4-0080-7697-ffff403fb0c34b9e", + "cat": "0fe650c2-0004-d446-ffff504f9410790f", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null, + "details": { + "allOff": "All Off", + "outputs": { + "1": "Sprinkler 1", + "2": "Sprinkler 2", + "3": "Sprinkler 3", + "4": "Sprinkler 4", + "5": "Sprinkler 5", + "6": "Sprinkler 6" + } + }, + "states": { + "activeOutput": "1255054f-0355-af46-ffff8e584788cd57" + } + }, + "106bed36-016d-6dd8-ffffffe6109fb656": { + "name": "Gate", + "type": "TextState", + "uuidAction": "106bed36-016d-6dd8-ffffffe6109fb656", + "room": "0b734138-038c-0386-ffff403fb0c34b9e", + "cat": "0fe665f4-0161-4773-ffff403fb0c34b9e", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null, + "states": { + "textAndIcon": "106bed36-016d-6dd8-ffffffe6109fb656" + } + }, + "132aa43b-01d4-56ea-ffff403fb0c34b9e": { + "name": "Tracker Control", + "type": "Tracker", + "uuidAction": "132aa43b-01d4-56ea-ffff403fb0c34b9e", + "room": "0b734138-037d-034e-ffff403fb0c34b9e", + "cat": "0fe650c2-0004-d446-ffff504f9410790f", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "details": { + "maxEntries": 5 + }, + "states": { + "entries": "132aa43b-01d4-56ea-ffff403fb0c34b9e" + } + }, + "0e3684cc-026e-28e0-ffff403fb0c34b9e": { + "name": "Kitchen All Blinds Up", + "type": "Pushbutton", + "uuidAction": "0e3684cc-026e-28e0-ffff403fb0c34b9e", + "room": "0b734138-038c-035e-ffff403fb0c34b9e", + "cat": "0b734138-033e-02d8-ffff403fb0c34b9e", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null, + "states": { + "active": "0e3684cc-026e-28e0-ffff403fb0c34b9e" + } + }, + "131b1a96-02b9-f6e9-eeff403fb0c34b9e": { + "name": "Up Down Analog Input", + "type": "UpDownAnalog", + "uuidAction": "131b1a96-02b9-f6e9-eeff403fb0c34b9e", + "room": "0b734138-037d-034e-ffff403fb0c34b9e", + "cat": "0fe650c2-0004-d446-ffff504f9410790f", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null, + "details": { + "format": "%.1f", + "min": 50.000, + "max": 150.000, + "step": 10.000 + }, + "states": { + "value": "131b1a96-02b9-f6e9-eeff403fb0c34b9e", + "error": "131b1a96-02b9-f6e8-eefffdf1ae1f802f" + } + }, + "131b1a96-02b9-f6e9-ffff403fb0c34b9e": { + "name": "Left Right Analog Input", + "type": "LeftRightAnalog", + "uuidAction": "131b1a96-02b9-f6e9-ffff403fb0c34b9e", + "room": "0b734138-037d-034e-ffff403fb0c34b9e", + "cat": "0fe650c2-0004-d446-ffff504f9410790f", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null, + "details": { + "format": "value: %.2f", + "min": 1072.123, + "max": 1123.458, + "step": 23.987 + }, + "states": { + "value": "131b1a96-02b9-f6e9-ffff403fb0c34b9e", + "error": "131b1a96-02b9-f6e8-fffffdf1ae1f802f" + } + }, + "0fd08ca6-01a6-d72a-ffff403fb0c34b9e": { + "name": "First Floor Scene", + "type": "UpDownDigital", + "uuidAction": "0fd08ca6-01a6-d72a-ffff403fb0c34b9e", + "room": "0b734138-037d-034e-ffff403fb0c34b9e", + "cat": "0b734138-033e-02d4-ffff403fb0c34b9e", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null + }, + "0fd08ca6-01a6-d72a-efff403fb0c34b9e": { + "name": "Second Floor Scene", + "type": "LeftRightDigital", + "uuidAction": "0fd08ca6-01a6-d72a-efff403fb0c34b9e", + "room": "0b734138-037d-034e-ffff403fb0c34b9e", + "cat": "0b734138-033e-02d4-ffff403fb0c34b9e", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null + }, + "1326771c-030e-3a7c-ffff403fb0c34b9e": { + "name": "Stairwell Light Switch", + "type": "TimedSwitch", + "uuidAction": "1326771c-030e-3a7c-ffff403fb0c34b9e", + "room": "0b734138-037d-034e-ffff403fb0c34b9e", + "cat": "0fe650c2-0004-d446-ffff504f9410790f", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null, + "details": { + "isStairwayLs": true + }, + "states": { + "deactivationDelay": "1326771c-030e-3a7c-ffff403fb0c34b9e", + "deactivationDelayTotal": "1326771c-030e-3a78-ffff0a5e97ff8000" + } + }, + "1076668f-0101-7076-ffff403fb0c34b9e": { + "name": "Lighting controller", + "type": "LightControllerV2", + "uuidAction": "1076668f-0101-7076-ffff403fb0c34b9e", + "room": "0b734138-03ac-03f0-ffff403fb0c34b9e", + "cat": "0b734138-033e-02d4-ffff403fb0c34b9e", + "defaultRating": 1, + "isFavorite": false, + "isSecured": false, + "details": { + "movementScene": 5, + "masterValue": "1076668f-0101-7076-ffff403fb0c34b9e/masterValue" + }, + "states": { + "activeMoods": "11d66fdf-01ad-0a8b-ffff504f9410790f", + "moodList": "11d66fdf-01ad-0a8c-ffff504f9410790f", + "favoriteMoods": "11d66fdf-01ad-0a8d-ffff504f9410790f", + "additionalMoods": "11d66fdf-01ad-0a8e-ffff504f9410790f" + }, + "subControls": { + "1076668f-0101-7076-ffff403fb0c34b9e/masterValue": { + "name": "Overall Switch", + "type": "Switch", + "uuidAction": "1076668f-0101-7076-ffff403fb0c34b9e/masterValue", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "states": { + "active": "1076668f-0101-7072-ffffe94fed106136" + } + }, + "1076668f-0101-7076-ffff403fb0c34b9e/AI1": { + "name": "Hall Study Lights", + "type": "Switch", + "uuidAction": "1076668f-0101-7076-ffff403fb0c34b9e/AI1", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "states": { + "active": "1076668f-0101-705f-ffffe94fed106136" + } + }, + "1076668f-0101-7076-ffff403fb0c34b9e/AI2": { + "name": "Roof Lights", + "type": "Switch", + "uuidAction": "1076668f-0101-7076-ffff403fb0c34b9e/AI2", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "states": { + "active": "1076668f-0101-7060-ffffe94fed106136" + } + }, + "1076668f-0101-7076-ffff403fb0c34b9e/AI3": { + "name": "Hall Left Lights", + "type": "Switch", + "uuidAction": "1076668f-0101-7076-ffff403fb0c34b9e/AI3", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "states": { + "active": "1076668f-0101-7061-ffffe94fed106136" + } + }, + "1076668f-0101-7076-ffff403fb0c34b9e/AI4": { + "name": "Hall Right Lights", + "type": "Switch", + "uuidAction": "1076668f-0101-7076-ffff403fb0c34b9e/AI4", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "states": { + "active": "1076668f-0101-7062-ffffe94fed106136" + } + } + } + }, + "133d5db0-0333-5865-ffff403fb0c34b9e": { + "name": "Burglar Alarm With Presence", + "type": "Alarm", + "uuidAction": "133d5db0-0333-5865-ffff403fb0c34b9e", + "room": "0b734138-037d-034e-ffff403fb0c34b9e", + "cat": "0fe650c2-0004-d446-ffff504f9410790f", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "details": { + "alert": true, + "presenceConnected": true + }, + "states": { + "armed": "133d5db0-0333-5858-ffff7a9f2275d9c1", + "nextLevel": "133d5db0-0333-5832-ffff7a9f2275d9c1", + "nextLevelDelay": "133d5db0-0333-5833-ffff7a9f2275d9c1", + "nextLevelDelayTotal": "133d5db0-0333-5834-ffff7a9f2275d9c1", + "level": "133d5db0-0333-5835-ffff7a9f2275d9c1", + "startTime": "133d5db0-0333-5836-ffff7a9f2275d9c1", + "armedDelay": "133d5db0-0333-5861-ffff7a9f2275d9c1", + "armedDelayTotal": "133d5db0-0333-584b-ffff7a9f2275d9c1", + "sensors": "133d5db0-0333-5865-ffff403fb0c34b9e", + "disabledMove": "133d5db0-0333-5837-ffff7a9f2275d9c1" + }, + "subControls": { + "133d5db0-0333-5865-ffff403fb0c34b9e/sensors": { + "name": "sensors", + "type": "Tracker", + "uuidAction": "133d5db0-0333-5865-ffff403fb0c34b9e/sensors", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "details": { + "maxEntries": 40 + }, + "states": { + "entries": "133d5db0-0333-5865-ffff403fb0c34b9e" + } + } + } + }, + "233d5db0-0333-5865-ffff403fb0c34b9e": { + "name": "Burglar Alarm No Presence", + "type": "Alarm", + "uuidAction": "233d5db0-0333-5865-ffff403fb0c34b9e", + "room": "0b734138-037d-034e-ffff403fb0c34b9e", + "cat": "0fe650c2-0004-d446-ffff504f9410790f", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "details": { + "alert": true, + "presenceConnected": false + }, + "states": { + "armed": "233d5db0-0333-5858-ffff7a9f2275d9c1", + "nextLevel": "233d5db0-0333-5832-ffff7a9f2275d9c1", + "nextLevelDelay": "233d5db0-0333-5833-ffff7a9f2275d9c1", + "nextLevelDelayTotal": "233d5db0-0333-5834-ffff7a9f2275d9c1", + "level": "233d5db0-0333-5835-ffff7a9f2275d9c1", + "startTime": "233d5db0-0333-5836-ffff7a9f2275d9c1", + "armedDelay": "233d5db0-0333-5861-ffff7a9f2275d9c1", + "armedDelayTotal": "233d5db0-0333-584b-ffff7a9f2275d9c1", + "sensors": "233d5db0-0333-5865-ffff403fb0c34b9e", + "disabledMove": "233d5db0-0333-5837-ffff7a9f2275d9c1" + }, + "subControls": { + "233d5db0-0333-5865-ffff403fb0c34b9e/sensors": { + "name": "sensors", + "type": "Tracker", + "uuidAction": "233d5db0-0333-5865-ffff403fb0c34b9e/sensors", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "details": { + "maxEntries": 40 + }, + "states": { + "entries": "233d5db0-0333-5865-ffff403fb0c34b9e" + } + } + } + }, + "432a7b7e-0022-3aac-ffff403fb0c34b9e": { + "name": "Selection Switch", + "type": "ValueSelector", + "uuidAction": "432a7b7e-0022-3aac-ffff403fb0c34b9e", + "room": "0b734138-037d-034e-ffff403fb0c34b9e", + "cat": "0fe650c2-0004-d446-ffff504f9410790f", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null, + "details": { + "increaseOnly": false, + "format": "%.0f" + }, + "states": { + "min": "432a7b7e-0022-3aa6-ffffd4d79adca7ff", + "max": "432a7b7e-0022-3aa7-ffffd4d79adca7ff", + "step": "432a7b7e-0022-3aa8-ffffd4d79adca7ff", + "value": "432a7b7e-0022-3aab-ffffd4d79adca7ff" + } + }, + "132a7b7e-0022-3aac-ffff403fb0c34b9e": { + "name": "Selection Switch Increase Only", + "type": "ValueSelector", + "uuidAction": "132a7b7e-0022-3aac-ffff403fb0c34b9e", + "room": "0b734138-037d-034e-ffff403fb0c34b9e", + "cat": "0fe650c2-0004-d446-ffff504f9410790f", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null, + "details": { + "increaseOnly": true, + "format": "%.0f" + }, + "states": { + "min": "132a7b7e-0022-3aa6-ffffd4d79adca7ff", + "max": "132a7b7e-0022-3aa7-ffffd4d79adca7ff", + "step": "132a7b7e-0022-3aa8-ffffd4d79adca7ff", + "value": "132a7b7e-0022-3aab-ffffd4d79adca7ff" + } + }, + "131fb314-0370-c93c-ffff403fb0c34b9e": { + "name": "Slider Virtual Input", + "type": "Slider", + "uuidAction": "131fb314-0370-c93c-ffff403fb0c34b9e", + "room": "0b734138-037d-034e-ffff403fb0c34b9e", + "cat": "0fe650c2-0004-d446-ffff504f9410790f", + "defaultRating": 0, + "isFavorite": false, + "isSecured": false, + "defaultIcon": null, + "details": { + "format": "%.1f", + "min": 120.000, + "max": 450.000, + "step": 3.333 + }, + "states": { + "value": "131fb314-0370-c93c-ffff403fb0c34b9e", + "error": "131fb314-0370-c93b-ffff6a7be057af65" + } + } + }, + "weatherServer": { + "states": { + "actual": "0b734139-0012-04f4-ffff403fb0c34b9e", + "forecast": "0b734139-0012-04f3-ffffa29fe842c4f8" + }, + "format": { + "relativeHumidity": "%.0f%%", + "temperature": "%.1f°", + "windSpeed": "%.0fkm/h", + "precipitation": "%.1fl/m²/h", + "barometricPressure": "%.0fhPa" + }, + "weatherTypeTexts": { + "1": "Clear Sky", + "2": "Clear", + "3": "Partly Cloudy", + "4": "Heavy Cloud Cover", + "5": "Cloudy", + "6": "Fog", + "7": "Overcast", + "8": "not in use", + "9": "not in use", + "10": "Light rain", + "11": "Rain", + "12": "Heavy rain", + "13": "Drizzle", + "14": "Sleet", + "15": "Heavy freezing rain", + "16": "Light shower", + "17": "Heavy rain showers", + "18": "Thunderstorms", + "19": "Strong thunderstorms", + "20": "Light snow", + "21": "Snow", + "22": "Heavy snow", + "23": "Light snow showers", + "24": "Heavy snow showers", + "25": "Light sleet", + "26": "Sleet", + "27": "Heavy Sleet", + "28": "Light sleet showers", + "29": "Heavy sleet showers" + }, + "weatherFieldTypes": { + "1": { + "id": 1, + "name": "Temperature", + "analog": true, + "unit": "°C", + "format": "%.1f" + }, + "2": { + "id": 2, + "name": "Dewpoint", + "analog": true, + "unit": "°C", + "format": "%.1f" + }, + "3": { + "id": 3, + "name": "Rel. humidity", + "analog": true, + "format": "%.0f%%" + }, + "4": { + "id": 4, + "name": "Wind Speed", + "analog": true, + "unit": "km/h", + "format": "%.0f" + }, + "5": { + "id": 5, + "name": "Wind Direction", + "analog": true + }, + "6": { + "id": 6, + "name": "Gusts", + "analog": true + }, + "7": { + "id": 7, + "name": "Radiation (absolute)", + "analog": true + }, + "8": { + "id": 8, + "name": "Radiation (relative)", + "analog": true + }, + "9": { + "id": 9, + "name": "Precipitation", + "analog": true, + "unit": "l/m²/h", + "format": "%.1f" + }, + "10": { + "id": 10, + "name": "Weather type", + "analog": true + }, + "11": { + "id": 11, + "name": "Pressure", + "analog": true, + "unit": "hPa", + "format": "%.0f" + }, + "26": { + "id": 26, + "name": "Perceived temperature", + "analog": true, + "unit": "°C", + "format": "%.1f" + }, + "27": { + "id": 27, + "name": "Solar Radiation", + "analog": true + }, + "28": { + "id": 28, + "name": "Chance of precipitation", + "analog": true + }, + "29": { + "id": 29, + "name": "Snow percentage", + "analog": true + }, + "30": { + "id": 30, + "name": "CAPE", + "analog": true + }, + "31": { + "id": 31, + "name": "Low clouds", + "analog": true + }, + "32": { + "id": 32, + "name": "Medium cloud cover", + "analog": true + }, + "33": { + "id": 33, + "name": "High cloud cover", + "analog": true + }, + "34": { + "id": 34, + "name": "Sunshine", + "analog": false + } + } + }, + "times": { + "266": { + "id": 266, + "name": "Days since 2009", + "analog": true + }, + "267": { + "id": 267, + "name": "Year", + "analog": true + }, + "268": { + "id": 268, + "name": "Month", + "analog": true + }, + "269": { + "id": 269, + "name": "Day", + "analog": true + }, + "270": { + "id": 270, + "name": "Weekday", + "analog": true + }, + "271": { + "id": 271, + "name": "Active operating time", + "analog": true + }, + "272": { + "id": 272, + "name": "Minutes past midnight", + "analog": true + }, + "273": { + "id": 273, + "name": "Hour", + "analog": true + }, + "274": { + "id": 274, + "name": "Minutes", + "analog": true + }, + "275": { + "id": 275, + "name": "Seconds", + "analog": true + }, + "276": { + "id": 276, + "name": "Seconds since boot", + "analog": true + }, + "277": { + "id": 277, + "name": "Time of sunrise", + "analog": true + }, + "278": { + "id": 278, + "name": "Time of sunset", + "analog": true + }, + "281": { + "id": 281, + "name": "Daylight", + "analog": false + }, + "282": { + "id": 282, + "name": "Daylight 30min", + "analog": false + }, + "283": { + "id": 283, + "name": "Time of dawn", + "analog": true + }, + "284": { + "id": 284, + "name": "Time of dusk", + "analog": true + }, + "285": { + "id": 285, + "name": "Sun elevation", + "analog": true + }, + "286": { + "id": 286, + "name": "Sun direction", + "analog": true + }, + "287": { + "id": 287, + "name": "Start pulse", + "analog": false + }, + "288": { + "id": 288, + "name": "Annual pulse", + "analog": false + }, + "289": { + "id": 289, + "name": "Pulse for month", + "analog": false + }, + "290": { + "id": 290, + "name": "Pulse for day", + "analog": false + }, + "291": { + "id": 291, + "name": "Pulse for hour", + "analog": false + }, + "292": { + "id": 292, + "name": "Pulse for minute", + "analog": false + }, + "293": { + "id": 293, + "name": "Pulse for second", + "analog": false + }, + "294": { + "id": 294, + "name": "Pulse for sunrise", + "analog": false + }, + "295": { + "id": 295, + "name": "Pulse for sunset", + "analog": false + }, + "298": { + "id": 298, + "name": "Pulse for dawn", + "analog": false + }, + "299": { + "id": 299, + "name": "Pulse for dusk", + "analog": false + } + }, + "autopilot": { + "0e3673b3-00f4-19dd-ffff403fb0c34b9e": { + "name": "Autopilots", + "uuidAction": "0e3673b3-00f4-19dd-ffff403fb0c34b9e", + "states": { + "changed": "0f2f1277-0304-0556-ffff504f9410790f", + "history": "0f2f1277-0304-0557-ffff504f9410790f" + } + } + }, + "messageCenter": { + "131b0b1e-03b4-0baa-ffff504f9410790f": { + "name": "System Status", + "uuidAction": "131b0b1e-03b4-0baa-ffff504f9410790f", + "states": { + "changed": "131b0b1e-03b4-0ba9-ffff504f9410790f" + } + } + } +} diff --git a/bundles/org.openhab.binding.loxone/src/test/resources/org/openhab/binding/loxone/internal/controls/MoodList1.json b/bundles/org.openhab.binding.loxone/src/test/resources/org/openhab/binding/loxone/internal/controls/MoodList1.json new file mode 100644 index 0000000000000..e08d2cf13bd06 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/resources/org/openhab/binding/loxone/internal/controls/MoodList1.json @@ -0,0 +1,33 @@ +[ + { + "name": "Low Lights", + "id": 5, + "static": false + }, + { + "name": "Side Lights", + "id": 2, + "static": false + }, + { + "name": "Play Lights", + "id": 3, + "static": false + }, + { + "name": "Study Only", + "id": 4, + "static": false + }, + { + "name": "Bright", + "id": 777, + "static": false, + "used": 1 + }, + { + "name": "Off", + "id": 778, + "static": true + } +] diff --git a/bundles/org.openhab.binding.loxone/src/test/resources/org/openhab/binding/loxone/internal/controls/MoodList2.json b/bundles/org.openhab.binding.loxone/src/test/resources/org/openhab/binding/loxone/internal/controls/MoodList2.json new file mode 100644 index 0000000000000..cd5900c8a3952 --- /dev/null +++ b/bundles/org.openhab.binding.loxone/src/test/resources/org/openhab/binding/loxone/internal/controls/MoodList2.json @@ -0,0 +1,43 @@ +[ + { + "name": "Low Lights", + "id": 5, + "static": false + }, + { + "name": "Play Lights", + "id": 6, + "static": false + }, + { + "name": "Study Only Changed Name", + "id": 4, + "static": false + }, + { + "name": "New Mood 1", + "id": 7, + "static": false + }, + { + "name": "New Mood 2", + "id": 8, + "static": false + }, + { + "name": "Bright", + "id": 777, + "static": false, + "used": 1 + }, + { + "name": "Off", + "id": 778, + "static": false + }, + { + "name": "New Off", + "id": 779, + "static": true + } +] diff --git a/bundles/org.openhab.binding.mail/.classpath b/bundles/org.openhab.binding.mail/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.mail/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.mail/.project b/bundles/org.openhab.binding.mail/.project new file mode 100644 index 0000000000000..bd16e5d36cf45 --- /dev/null +++ b/bundles/org.openhab.binding.mail/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.mail + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/bundles/org.openhab.binding.mail/NOTICE b/bundles/org.openhab.binding.mail/NOTICE new file mode 100644 index 0000000000000..f14d05e90ebda --- /dev/null +++ b/bundles/org.openhab.binding.mail/NOTICE @@ -0,0 +1,24 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab2-addons + + +commons-email: +* License: Apache 2.0 License +* Project: https://commons.apache.org/proper/commons-email +* Source: https://commons.apache.org/proper/commons-email + +javax.mail: +* License: Common Development and Distribution License (CDDL) v1.1 and GNU General Public License (GPL) v2 with Classpath Exception +* Project: https://javaee.github.io/javamail/ +* Source: https://github.com/javaee/javamail diff --git a/bundles/org.openhab.binding.mail/README.md b/bundles/org.openhab.binding.mail/README.md new file mode 100644 index 0000000000000..2dcdd41b7db36 --- /dev/null +++ b/bundles/org.openhab.binding.mail/README.md @@ -0,0 +1,122 @@ +# Mail Binding + +The Mail binding provides support for sending emails from rules. + +## Supported Things + +There are three things: `smtp`, `imap` and `pop3` which represents respective servers. + +## Thing Configuration + +### SMTP server (`smtp`) + +There are two mandatory parameters `hostname` and `sender`. + +The `hostname` may contain an IP address or a FQDN like `smtp.gmail.com`. +The `sender` must be a valid email address used as sender address for mails. + +The `security`, `port`, `username` and `password` parameters are optional. + +The `security` parameter defines the transport security and can be set to `PLAIN` (default), `SSL` or `TLS`. +The `port` parameter is used to change the default ports for the SMTP server. +Default ports are `25` (for `PLAIN` and `TLS`) and `465` (for `SSL`). +For authentication, `username` and `password` can be supplied. +If one or both are empty, no authentication data is provided to the SMTP server during connect. + +### IMAP server (`imap`) / POP3 server (`pop3`) + +There is one mandatory parameter: `hostname`, `username`, `password`. +The `hostname` may contain an IP address or a FQDN like `mail.gmail.com`. +For authentication `username` and `password` need to be supplied. + +The `refresh`, `security`, `port`, `username` and `password` parameters are optional. + +The `refresh` parameter is the time in seconds between two refreshes of the thing`s channels. +If omitted, the default of 60s is used. +The `security` parameter defines the transport security and can be set to `PLAIN` (default), `SSL` or `TLS`. +The `port` parameter is used to change the default ports for the SMTP server. +Default ports are `143` (for `PLAIN` and `TLS`) and `993` (for `SSL`) in the case of `imap` or `110` (for `PLAIN` and `TLS`) and `995` (for `SSL`) in the case of `pop3`. + +## Channels + +There are no channels for the `smtp` thing. +The `imap` and `pop3` things can be extended with `mailcount`-type channels. + +### Type `mailcount` + +Each channel has two parameters: `name` and `type`. +The `name` is mandatory and denotes the folder name on the given account. +The `type` parameter can be `UNREAD` or `TOTAL` (default). +Channels with type `UNREAD` give the number on unread mails in that folder. + + +## Full Example + +mail.things: + +``` +Thing mail:smtp:samplesmtp [ hostname="smtp.example.com", sender="foo@example.com", security="TLS", username="user", password="pass" ] + +Thing mail:imap:sampleimap [ hostname="imap.example.com", security="SSL", username="user", password="pass" ] { + Channels: + Type mailcount : inbox_total [ folder="INBOX", type="TOTAL" ] + Type mailcount : inbox_unread [ folder="INBOX", type="UNREAD" ] +} +``` + + +mail.items: + +``` +Number InboxTotal "INBOX [%d]" { channel="mail:imap:sampleimap:inbox_total" } +Number InboxUnread "INBOX Unread [%d]" { channel="mail:imap:sampleimap:inbox_unread" } +``` + +mail.sitemap: + +``` +sitemap demo label="Main Menu" +{ + Frame { + Text item=InboxTotal + Text item=InboxUnread + } +} +``` + +## Rule Action + +This binding includes rule actions for sending mail. +Six different actions available: + +* `sendMail(String recipient, String subject, String text)` +* `sendMail(String recipient, String subject, String text, String URL)` +* `sendMail(String recipient, String subject, String text, List URL)` +* `sendHtmlMail(String recipient, String subject, String htmlContent)` +* `sendHtmlMail(String recipient, String subject, String htmlContent, String URL)` +* `sendHtmlMail(String recipient, String subject, String htmlContent, List URL)` + +The `sendMail(...)` send a plain text mail (with attachments if supplied). +The `sendHtmlMail(...)` send a HTML mail (with attachments if supplied). + +Since there is a separate rule action instance for each `smtp` thing, this needs to be retrieved through `getActions(scope, thingUID)`. +The first parameter always has to be `mail` and the second is the full Thing UID of the SMTP server that should be used. +Once this action instance is retrieved, you can invoke the action method on it. + +Examples: + +``` +val mailActions = getActions("mail","mail:smtp:sampleserver") +mailActions.sendMail("recipient@foo.bar", "Test subject", "This is the mail content.") +``` + +``` +import java.util.List + +val List attachmentUrlList = newArrayList( + "http://some.web/site/snap.jpg¶m=value", + "file:///tmp/201601011031.jpg") +val mailActions = getActions("mail","mail:smtp:sampleserver") +mailActions.sendHtmlMail("recipient@foo.bar", "Test subject", "

Header

This is the mail content.", attachmentUrlList) +``` + diff --git a/bundles/org.openhab.binding.mail/pom.xml b/bundles/org.openhab.binding.mail/pom.xml new file mode 100644 index 0000000000000..c87ca4a20da52 --- /dev/null +++ b/bundles/org.openhab.binding.mail/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.mail + + openHAB Add-ons :: Bundles :: Mail Binding + + + + org.apache.commons + commons-email + 1.5 + provided + + + com.sun.mail + javax.mail + 1.6.2 + provided + + + diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/action/SendMailActions.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/action/SendMailActions.java new file mode 100644 index 0000000000000..a08c9467141a6 --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/action/SendMailActions.java @@ -0,0 +1,230 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mail.action; + +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.List; + +import javax.mail.internet.AddressException; + +import org.apache.commons.mail.EmailException; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.binding.ThingActions; +import org.eclipse.smarthome.core.thing.binding.ThingActionsScope; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.openhab.binding.mail.internal.MailBuilder; +import org.openhab.binding.mail.internal.SMTPHandler; +import org.openhab.core.automation.annotation.ActionInput; +import org.openhab.core.automation.annotation.ActionOutput; +import org.openhab.core.automation.annotation.RuleAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link SendMailActions} class defines rule actions for sending mail + * + * @author Jan N. Klug - Initial contribution + */ +@ThingActionsScope(name = "mail") +@NonNullByDefault +public class SendMailActions implements ThingActions { + private final Logger logger = LoggerFactory.getLogger(SendMailActions.class); + + private @Nullable SMTPHandler handler; + + @RuleAction(label = "Send Text Mail", description = "sends a text mail") + public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendMail( + @ActionInput(name = "recipient") @Nullable String recipient, + @ActionInput(name = "subject") @Nullable String subject, + @ActionInput(name = "text") @Nullable String text) { + return sendMail(recipient, subject, text, new ArrayList()); + } + + @RuleAction(label = "Send Text Mail", description = "sends a text mail with URL attachment") + public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendMail( + @ActionInput(name = "recipient") @Nullable String recipient, + @ActionInput(name = "subject") @Nullable String subject, @ActionInput(name = "text") @Nullable String text, + @ActionInput(name = "url") @Nullable String urlString) { + List urlList = new ArrayList<>(); + if (urlString != null) { + urlList.add(urlString); + } + return sendMail(recipient, subject, text, urlList); + } + + @RuleAction(label = "Send Text Mail", description = "sends a text mail with several URL attachments") + public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendMail( + @ActionInput(name = "recipient") @Nullable String recipient, + @ActionInput(name = "subject") @Nullable String subject, @ActionInput(name = "text") @Nullable String text, + @ActionInput(name = "urlList") @Nullable List urlStringList) { + if (recipient == null) { + logger.info("can't send to missing recipient"); + return false; + } + + try { + MailBuilder builder = new MailBuilder(recipient); + + if (subject != null && !subject.isEmpty()) { + builder.withSubject(subject); + } + if (text != null && !text.isEmpty()) { + builder.withText(text); + } + if (urlStringList != null) { + for (String urlString : urlStringList) { + builder.withURLAttachment(urlString); + } + } + + if (handler == null) { + logger.info("handler is null, can't send mail"); + return false; + } else { + return handler.sendMail(builder.build()); + } + } catch (AddressException e) { + logger.info("could not send mail: {}", e.getMessage()); + return false; + } catch (MalformedURLException e) { + logger.info("could not send mail: {}", e.getMessage()); + return false; + } catch (EmailException e) { + logger.info("could not send mail: {}", e.getMessage()); + return false; + } + } + + public static boolean sendMail(@Nullable ThingActions actions, @Nullable String recipient, @Nullable String subject, + @Nullable String text) { + return SendMailActions.sendMail(actions, recipient, subject, text, new ArrayList()); + } + + public static boolean sendMail(@Nullable ThingActions actions, @Nullable String recipient, @Nullable String subject, + @Nullable String text, @Nullable String urlString) { + List urlList = new ArrayList<>(); + if (urlString != null) { + urlList.add(urlString); + } + return SendMailActions.sendMail(actions, recipient, subject, text, urlList); + } + + public static boolean sendMail(@Nullable ThingActions actions, @Nullable String recipient, @Nullable String subject, + @Nullable String text, @Nullable List urlStringList) { + if (actions instanceof SendMailActions) { + return ((SendMailActions) actions).sendMail(recipient, subject, text, urlStringList); + } else { + throw new IllegalArgumentException("Instance is not SendMailActions class."); + } + } + + @RuleAction(label = "Send HTML Mail", description = "sends a HTML mail") + public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendHtmlMail( + @ActionInput(name = "recipient") @Nullable String recipient, + @ActionInput(name = "subject") @Nullable String subject, + @ActionInput(name = "html") @Nullable String html) { + return sendHtmlMail(recipient, subject, html, new ArrayList()); + } + + @RuleAction(label = "Send HTML Mail", description = "sends a HTML mail with URL attachment") + public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendHtmlMail( + @ActionInput(name = "recipient") @Nullable String recipient, + @ActionInput(name = "subject") @Nullable String subject, @ActionInput(name = "html") @Nullable String html, + @ActionInput(name = "url") @Nullable String urlString) { + List urlList = new ArrayList<>(); + if (urlString != null) { + urlList.add(urlString); + } + return sendHtmlMail(recipient, subject, html, urlList); + } + + @RuleAction(label = "Send HTML Mail", description = "sends a HTML mail with several URL attachments") + public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendHtmlMail( + @ActionInput(name = "recipient") @Nullable String recipient, + @ActionInput(name = "subject") @Nullable String subject, @ActionInput(name = "html") @Nullable String html, + @ActionInput(name = "urlList") @Nullable List urlStringList) { + if (recipient == null) { + logger.info("can't send to missing recipient"); + return false; + } + + try { + MailBuilder builder = new MailBuilder(recipient); + + if (subject != null && !subject.isEmpty()) { + builder.withSubject(subject); + } + if (html != null && !html.isEmpty()) { + builder.withHtml(html); + } + if (urlStringList != null) { + for (String urlString : urlStringList) { + builder.withURLAttachment(urlString); + } + } + + if (handler == null) { + logger.info("handler is null, can't send mail"); + return false; + } else { + return handler.sendMail(builder.build()); + } + } catch (AddressException e) { + logger.info("could not send mail: {}", e.getMessage()); + return false; + } catch (MalformedURLException e) { + logger.info("could not send mail: {}", e.getMessage()); + return false; + } catch (EmailException e) { + logger.info("could not send mail: {}", e.getMessage()); + return false; + } + } + + public static boolean sendHtmlMail(@Nullable ThingActions actions, @Nullable String recipient, + @Nullable String subject, @Nullable String html) { + return SendMailActions.sendHtmlMail(actions, recipient, subject, html, new ArrayList()); + } + + public static boolean sendHtmMail(@Nullable ThingActions actions, @Nullable String recipient, + @Nullable String subject, @Nullable String html, @Nullable String urlString) { + List urlList = new ArrayList<>(); + if (urlString != null) { + urlList.add(urlString); + } + return SendMailActions.sendHtmlMail(actions, recipient, subject, html, urlList); + } + + public static boolean sendHtmlMail(@Nullable ThingActions actions, @Nullable String recipient, + @Nullable String subject, @Nullable String html, @Nullable List urlStringList) { + if (actions instanceof SendMailActions) { + return ((SendMailActions) actions).sendHtmlMail(recipient, subject, html, urlStringList); + } else { + throw new IllegalArgumentException("Instance is not SendMailActions class."); + } + } + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + if (handler instanceof SMTPHandler) { + this.handler = (SMTPHandler) handler; + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return this.handler; + } +} diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/MailBindingConstants.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/MailBindingConstants.java new file mode 100644 index 0000000000000..57da9e1f78b1c --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/MailBindingConstants.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mail.internal; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; + +/** + * The {@link MailBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Jan N. Klug - Initial contribution + */ +@NonNullByDefault +public class MailBindingConstants { + private static final String BINDING_ID = "mail"; + + public static final ThingTypeUID THING_TYPE_SMTPSERVER = new ThingTypeUID(BINDING_ID, "smtp"); + public static final ThingTypeUID THING_TYPE_IMAPSERVER = new ThingTypeUID(BINDING_ID, "imap"); + public static final ThingTypeUID THING_TYPE_POP3SERVER = new ThingTypeUID(BINDING_ID, "pop3"); + + public static final Set SUPPORTED_THING_TYPES = new HashSet<>( + Arrays.asList(THING_TYPE_SMTPSERVER, THING_TYPE_IMAPSERVER, THING_TYPE_POP3SERVER)); + + public static final ChannelTypeUID CHANNEL_TYPE_UID_FOLDER_MAILCOUNT = new ChannelTypeUID(BINDING_ID, "mailcount"); +} diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/MailBuilder.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/MailBuilder.java new file mode 100644 index 0000000000000..1247186d3a22c --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/MailBuilder.java @@ -0,0 +1,201 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mail.internal; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.activation.FileDataSource; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; + +import org.apache.commons.mail.Email; +import org.apache.commons.mail.EmailAttachment; +import org.apache.commons.mail.EmailException; +import org.apache.commons.mail.HtmlEmail; +import org.apache.commons.mail.MultiPartEmail; +import org.apache.commons.mail.SimpleEmail; +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link MailBuilder} class provides a builder for an mail. + * + * @author Jan N. Klug - Initial contribution + */ + +@NonNullByDefault +public class MailBuilder { + private String sender = ""; + private List recipients = new ArrayList<>(); + private List attachmentURLs = new ArrayList<>(); + private List attachmentFiles = new ArrayList<>(); + private String subject = "(no subject)"; + private String text = ""; + private String html = ""; + + /** + * Create a new MailBuilder + * + * @param recipients comma separated sequence of addresses (must follow RFC822 syntax) + * @throws AddressException on invalid recipient address + */ + public MailBuilder(String recipients) throws AddressException { + this.recipients.addAll(Arrays.asList(InternetAddress.parse(recipients))); + } + + /** + * Add one or more recipients + * + * @param recipients comma separated sequence of addresses (must follow RFC822 syntax) + * @return a MailBuilder + * @throws AddressException on invalid recipient address + */ + public MailBuilder withRecipients(String recipients) throws AddressException { + this.recipients.addAll(Arrays.asList(InternetAddress.parse(recipients))); + return this; + } + + /** + * Set the sender address + * + * @param sender address (must follow RFC822 syntax) + * @return a MailBuilder + */ + public MailBuilder withSender(String sender) { + this.sender = sender; + return this; + } + + /** + * Set the mail subject + * + * @param subject String containing the subject + * @return a MailBuilder + */ + public MailBuilder withSubject(String subject) { + this.subject = subject; + return this; + } + + /** + * Set the plain text content + * + * @param text String containing the text + * @return a MailBuilder + */ + public MailBuilder withText(String text) { + this.text = text; + return this; + } + + /** + * Set the HTML content + * + * @param html a String containing HTML (syntax not checked) + * @return a MailBuilder + */ + public MailBuilder withHtml(String html) { + this.html = html; + return this; + } + + /** + * Attach an URL + * + * @param urlString the URL as String + * @return a MailBuilder + * @throws MalformedURLException if url has invalid format + */ + public MailBuilder withURLAttachment(String urlString) throws MalformedURLException { + attachmentURLs.add(new URL(urlString)); + return this; + } + + /** + * Attach a file + * + * @param path String with path to local file + * @return a MailBuilder + */ + public MailBuilder withFileAttachment(String path) { + attachmentFiles.add(new File(path)); + return this; + } + + /** + * Build the Mail + * + * @return instance of Email + * @throws EmailException if something goes wrong + */ + public Email build() throws EmailException { + Email mail; + + if (attachmentURLs.isEmpty() && attachmentFiles.isEmpty() && html.isEmpty()) { + // text mail without attachments + mail = new SimpleEmail(); + if (!text.isEmpty()) { + mail.setMsg(text); + } + } else if (html.isEmpty()) { + // text mail with attachments + MultiPartEmail multipartMail = new MultiPartEmail(); + if (!text.isEmpty()) { + multipartMail.setMsg(text); + } + for (File file : attachmentFiles) { + multipartMail.attach(file); + } + for (URL url : attachmentURLs) { + EmailAttachment attachment = new EmailAttachment(); + attachment.setURL(url); + attachment.setDisposition(EmailAttachment.ATTACHMENT); + multipartMail.attach(attachment); + } + mail = multipartMail; + } else { + // html email + HtmlEmail htmlMail = new HtmlEmail(); + if (!text.isEmpty()) { + // alternate text supplied + htmlMail.setTextMsg(text); + htmlMail.setHtmlMsg(html); + } else { + htmlMail.setMsg(html); + } + for (File file : attachmentFiles) { + htmlMail.attach(new FileDataSource(file), "", ""); + } + for (URL url : attachmentURLs) { + EmailAttachment attachment = new EmailAttachment(); + attachment.setURL(url); + attachment.setDisposition(EmailAttachment.ATTACHMENT); + htmlMail.attach(attachment); + } + mail = htmlMail; + } + + mail.setTo(recipients); + mail.setSubject(subject); + + if (!sender.isEmpty()) { + mail.setFrom(sender); + } + + return mail; + } +} diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/MailCountChannelType.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/MailCountChannelType.java new file mode 100644 index 0000000000000..013f299d24769 --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/MailCountChannelType.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mail.internal; + +/** + * The {@link MailCountChannelType} enum for folder mail count type + * + * @author Jan N. Klug - Initial contribution + */ + +public enum MailCountChannelType { + UNREAD, + TOTAL +} diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/MailHandlerFactory.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/MailHandlerFactory.java new file mode 100644 index 0000000000000..3dc37de42fd63 --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/MailHandlerFactory.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mail.internal; + +import static org.openhab.binding.mail.internal.MailBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link MailHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Jan N. Klug - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.mail", service = ThingHandlerFactory.class) +public class MailHandlerFactory extends BaseThingHandlerFactory { + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (THING_TYPE_SMTPSERVER.equals(thingTypeUID)) { + return new SMTPHandler(thing); + } else if (THING_TYPE_IMAPSERVER.equals(thingTypeUID) || THING_TYPE_POP3SERVER.equals(thingTypeUID)) { + return new POP3IMAPHandler(thing); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/POP3IMAPHandler.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/POP3IMAPHandler.java new file mode 100644 index 0000000000000..34c696da04aee --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/POP3IMAPHandler.java @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mail.internal; + +import static org.openhab.binding.mail.internal.MailBindingConstants.CHANNEL_TYPE_UID_FOLDER_MAILCOUNT; + +import java.util.Properties; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import javax.mail.Flags; +import javax.mail.Folder; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.Store; +import javax.mail.search.FlagTerm; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.mail.internal.config.POP3IMAPChannelConfig; +import org.openhab.binding.mail.internal.config.POP3IMAPConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link POP3IMAPHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Jan N. Klug - Initial contribution + */ +@NonNullByDefault +public class POP3IMAPHandler extends BaseThingHandler { + private final Logger logger = LoggerFactory.getLogger(POP3IMAPHandler.class); + + private @NonNullByDefault({}) POP3IMAPConfig config; + private @Nullable ScheduledFuture refreshTask; + private final String baseProtocol; + private String protocol = "imap"; + + public POP3IMAPHandler(Thing thing) { + super(thing); + baseProtocol = thing.getThingTypeUID().getId(); // pop3 or imap + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + } + + @Override + public void initialize() { + config = getConfigAs(POP3IMAPConfig.class); + + protocol = baseProtocol; + + if (config.security == ServerSecurity.SSL) { + protocol.concat("s"); + } + + if (config.port == 0) { + switch (protocol) { + case "imap": + config.port = 143; + break; + case "imaps": + config.port = 993; + break; + case "pop3": + config.port = 110; + break; + case "pop3s": + config.port = 995; + break; + default: + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR); + return; + } + } + + refreshTask = scheduler.scheduleWithFixedDelay(this::refresh, 0, config.refresh, TimeUnit.SECONDS); + updateStatus(ThingStatus.ONLINE); + } + + @SuppressWarnings("null") + @Override + public void dispose() { + if (refreshTask != null) { + if (!refreshTask.isCancelled()) { + refreshTask.cancel(true); + } + } + } + + private void refresh() { + Properties props = new Properties(); + props.setProperty("mail." + baseProtocol + ".starttls.enable", "true"); + props.setProperty("mail.store.protocol", protocol); + Session session = Session.getInstance(props); + + try (Store store = session.getStore()) { + store.connect(config.hostname, config.port, config.username, config.password); + + for (Channel channel : thing.getChannels()) { + if (CHANNEL_TYPE_UID_FOLDER_MAILCOUNT.equals(channel.getChannelTypeUID())) { + final POP3IMAPChannelConfig channelConfig = channel.getConfiguration() + .as(POP3IMAPChannelConfig.class); + final String folderName = channelConfig.folder; + if (folderName == null || folderName.isEmpty()) { + logger.info("missing or empty folder name in channel {}", channel.getUID()); + } else { + try (Folder mailbox = store.getFolder(folderName)) { + mailbox.open(Folder.READ_ONLY); + if (channelConfig.type == MailCountChannelType.TOTAL) { + updateState(channel.getUID(), new DecimalType(mailbox.getMessageCount())); + } else { + updateState(channel.getUID(), new DecimalType( + mailbox.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false)).length)); + } + } catch (MessagingException e) { + throw e; + } + } + } + } + } catch (MessagingException e) { + logger.info("error when trying to refresh IMAP: {}", e.getMessage()); + } + } +} diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/SMTPHandler.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/SMTPHandler.java new file mode 100644 index 0000000000000..10d52d372215e --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/SMTPHandler.java @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mail.internal; + +import java.util.Collection; +import java.util.Collections; + +import org.apache.commons.mail.DefaultAuthenticator; +import org.apache.commons.mail.Email; +import org.apache.commons.mail.EmailException; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.mail.action.SendMailActions; +import org.openhab.binding.mail.internal.config.SMTPConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link SMTPHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Jan N. Klug - Initial contribution + */ +@NonNullByDefault +public class SMTPHandler extends BaseThingHandler { + private final Logger logger = LoggerFactory.getLogger(SMTPHandler.class); + + private @NonNullByDefault({}) SMTPConfig config; + + public SMTPHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + } + + @Override + public void initialize() { + config = getConfigAs(SMTPConfig.class); + + if (config.port == 0) { + if (config.security == ServerSecurity.SSL) { + config.port = 465; + } else { + config.port = 25; + } + } + + updateStatus(ThingStatus.ONLINE); + } + + /** + * use this server to send a mail + * + * @param mail the Email that needs to be sent + * @return true if successful, false if failed + */ + public boolean sendMail(Email mail) { + try { + if (mail.getFromAddress() == null) { + mail.setFrom(config.sender); + } + mail.setHostName(config.hostname); + switch (config.security) { + case SSL: + mail.setSSLOnConnect(true); + mail.setSslSmtpPort(config.port.toString()); + break; + case TLS: + mail.setStartTLSEnabled(true); + mail.setStartTLSRequired(true); + mail.setSmtpPort(config.port); + break; + case PLAIN: + mail.setSmtpPort(config.port); + } + if (!config.username.isEmpty() && !config.password.isEmpty()) { + mail.setAuthenticator(new DefaultAuthenticator(config.username, config.password)); + } + mail.send(); + } catch (EmailException e) { + logger.warn("Trying to send mail but exception occured: {} ", e.getMessage()); + return false; + } + return true; + } + + @Override + public Collection> getServices() { + return Collections.singletonList(SendMailActions.class); + } +} diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/ServerSecurity.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/ServerSecurity.java new file mode 100644 index 0000000000000..061ff384f7b10 --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/ServerSecurity.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mail.internal; + +/** + * The {@link ServerSecurity} enum contains security configuration options + * + * @author Jan N. Klug - Initial contribution + */ + +public enum ServerSecurity { + PLAIN, + SSL, + TLS +} diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/config/BaseConfig.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/config/BaseConfig.java new file mode 100644 index 0000000000000..fff7425c29306 --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/config/BaseConfig.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mail.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.mail.internal.ServerSecurity; + +/** + * The {@link BaseConfig} class contains fields mapping thing configuration parameters. + * + * @author Jan N. Klug - Initial contribution + */ + +@NonNullByDefault +public class BaseConfig { + public @Nullable String hostname; + public Integer port = 0; + public String username = ""; + public String password = ""; + public ServerSecurity security = ServerSecurity.PLAIN; +} diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/config/POP3IMAPChannelConfig.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/config/POP3IMAPChannelConfig.java new file mode 100644 index 0000000000000..a47ab11425e8e --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/config/POP3IMAPChannelConfig.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mail.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.mail.internal.MailCountChannelType; + +/** + * The {@link POP3IMAPChannelConfig} class contains fields mapping thing configuration parameters. + * + * @author Jan N. Klug - Initial contribution + */ + +@NonNullByDefault +public class POP3IMAPChannelConfig { + public @Nullable String folder; + public MailCountChannelType type = MailCountChannelType.TOTAL; +} diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/config/POP3IMAPConfig.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/config/POP3IMAPConfig.java new file mode 100644 index 0000000000000..f491623857e5a --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/config/POP3IMAPConfig.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mail.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link POP3IMAPConfig} class contains fields mapping thing configuration parameters. + * + * @author Jan N. Klug - Initial contribution + */ + +@NonNullByDefault +public class POP3IMAPConfig extends BaseConfig { + public int refresh = 60; +} diff --git a/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/config/SMTPConfig.java b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/config/SMTPConfig.java new file mode 100644 index 0000000000000..35467ab9c6306 --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/java/org/openhab/binding/mail/internal/config/SMTPConfig.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mail.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link SMTPConfig} class contains fields mapping thing configuration parameters. + * + * @author Jan N. Klug - Initial contribution + */ + +@NonNullByDefault +public class SMTPConfig extends BaseConfig { + public @Nullable String sender; +} diff --git a/bundles/org.openhab.binding.mail/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.mail/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..8ee1480e5f6f2 --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,10 @@ + + + + Mail Binding + Email support + Jan N. Klug + + diff --git a/bundles/org.openhab.binding.mail/src/main/resources/ESH-INF/i18n/mail_de_DE.properties b/bundles/org.openhab.binding.mail/src/main/resources/ESH-INF/i18n/mail_de_DE.properties new file mode 100644 index 0000000000000..b67289c1e28b4 --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/resources/ESH-INF/i18n/mail_de_DE.properties @@ -0,0 +1,39 @@ +# binding +binding.mail.name = Mail Binding +binding.mail.description = Email Unterstützung + +# thing types +thing-type.mail.smtp.label = SMTP Server +thing-type.mail.smtp.description = Wird zum Versenden von Mail verwendet +thing-type.config.mail.smtp.sender.label = Absender +thing-type.config.mail.smtp.sender.description = Standard-Absendeadresse für Mails +thing-type.config.mail.smtp.port.description = Standard Ports sind 25 für PLAIN/TLS und 465 für SSL +thing-type.config.mail.smtp.security.label = Server Sicherheits-Protokoll +thing-type.config.mail.smtp.username.label = Benutzername +thing-type.config.mail.smtp.password.label = Passwort + +thing-type.mail.imap.label = IMAP Server +thing-type.mail.imap.description = IMAP Postfach Überwachung +thing-type.config.mail.imap.refresh.label = Refresh +thing-type.config.mail.imap.refresh.description = Zeit zwischen zwei Abfragen (in s, Standard ist 60s) +thing-type.config.mail.imap.port.description = Standard Ports sind 143 für PLAIN/TLS und 993 für SSL +thing-type.config.mail.imap.security.label = Server Sicherheits-Protokoll +thing-type.config.mail.imap.username.label = Benutzername +thing-type.config.mail.imap.password.label = Passwort + +thing-type.mail.imap.label = POP3 Server +thing-type.mail.imap.description = POP3 Postfach Überwachung +thing-type.config.mail.imap.refresh.label = Refresh +thing-type.config.mail.imap.refresh.description = Zeit zwischen zwei Abfragen (in s, Standard ist 60s) +thing-type.config.mail.imap.port.description = Standard Ports sind 110 für PLAIN/TLS und 995 für SSL +thing-type.config.mail.imap.security.label = Server Sicherheits-Protokoll +thing-type.config.mail.imap.username.label = Benutzername +thing-type.config.mail.imap.password.label = Passwort + +channel-type.mail.mailcount.label = Mail Anzahl +channel-type.mail.mailcount.description = Anzahl Mails in Postfach +channel-type.config.mail.folder.label = Postfach +channel-type.config.mail.folder.description = Name des Postfachs auf dem Server +channel-type.config.mail.type.label = Typ +channel-type.config.mail.type.option.TOTAL = Gesamt +channel-type.config.mail.type.option.UNREAD = Ungelesen diff --git a/bundles/org.openhab.binding.mail/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.mail/src/main/resources/ESH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..6557635206612 --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/main/resources/ESH-INF/thing/thing-types.xml @@ -0,0 +1,133 @@ + + + + + + Used for sending mail via rule-actions + + + + Default sender address for mail + + + + + + + Default values are 25 for plain/TLS and 465 for SSL + true + + + + + + + + + true + PLAIN + + + + + + + password + + + + + + Used for receiving mail + + + + + + + Default values are 143 for plain/TLS and 993 for SSL + true + + + + + + + + + true + PLAIN + + + + + + + password + + + + 60 + + + + + + Used for receiving mail + + + + + + + Default values are 110 for plain/TLS and 995 for SSL + true + + + + + + + + + true + PLAIN + + + + + + + password + + + + 60 + + + + + + Number + + Number of mails in folder + + + + + true + + + + + + + true + TOTAL + + + + diff --git a/bundles/org.openhab.binding.mail/src/test/java/org/openhab/binding/mail/MailBuilderTest.java b/bundles/org.openhab.binding.mail/src/test/java/org/openhab/binding/mail/MailBuilderTest.java new file mode 100644 index 0000000000000..e6f660b33a445 --- /dev/null +++ b/bundles/org.openhab.binding.mail/src/test/java/org/openhab/binding/mail/MailBuilderTest.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mail; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.net.MalformedURLException; + +import javax.mail.MessagingException; +import javax.mail.internet.AddressException; + +import org.apache.commons.mail.Email; +import org.apache.commons.mail.EmailException; +import org.apache.commons.mail.HtmlEmail; +import org.apache.commons.mail.MultiPartEmail; +import org.apache.commons.mail.SimpleEmail; +import org.junit.Test; +import org.openhab.binding.mail.internal.MailBuilder; + +/** + * The {@link MailBuilderTest} class defines tests for the {@link MailBuilder} class + * + * @author Jan N. Klug - Initial contribution + */ + +public class MailBuilderTest { + + private static final String TEST_STRING = "test"; + private static final String TEST_EMAIL = "foo@bar.zinga"; + + @Test(expected = AddressException.class) + public void illegalToAddressThrowsException() throws AddressException { + MailBuilder builder = new MailBuilder("foo bar.zinga"); + } + + @Test(expected = EmailException.class) + public void illegalFromAddressThrowsException() throws AddressException, EmailException { + Email mail = new MailBuilder("TEST_EMAIL").withSender("foo bar.zinga").build(); + } + + @Test(expected = MalformedURLException.class) + public void illegalURLThrowsException() throws AddressException, MalformedURLException { + MailBuilder builder = new MailBuilder("TEST_EMAIL").withURLAttachment("foo bar.zinga"); + } + + @Test + public void withTextOnlyReturnsSimpleEmail() throws AddressException, EmailException { + MailBuilder builder = new MailBuilder(TEST_EMAIL); + Email mail = builder.withText("boo").build(); + assertThat(mail, instanceOf(SimpleEmail.class)); + } + + @Test + public void withURLAttachmentReturnsMultiPartEmail() + throws AddressException, EmailException, MalformedURLException { + MailBuilder builder = new MailBuilder(TEST_EMAIL); + Email mail = builder.withText("boo").withURLAttachment("http://www.openhab.org").build(); + assertThat(mail, instanceOf(MultiPartEmail.class)); + } + + @Test + public void withHtmlReturnsHtmlEmail() throws AddressException, EmailException { + MailBuilder builder = new MailBuilder(TEST_EMAIL); + Email mail = builder.withHtml("test").build(); + assertThat(mail, instanceOf(HtmlEmail.class)); + } + + @Test + public void fieldsSetInMail() throws EmailException, MessagingException, IOException { + MailBuilder builder = new MailBuilder(TEST_EMAIL); + + assertEquals("(no subject)", builder.build().getSubject()); + assertEquals(TEST_STRING, builder.withSubject(TEST_STRING).build().getSubject()); + + assertEquals(TEST_EMAIL, builder.withSender(TEST_EMAIL).build().getFromAddress().getAddress()); + + assertEquals(TEST_EMAIL, builder.build().getToAddresses().get(0).getAddress()); + assertEquals(2, builder.withRecipients(TEST_EMAIL).build().getToAddresses().size()); + } + +} diff --git a/bundles/org.openhab.binding.max/MAINTAINERS b/bundles/org.openhab.binding.max/MAINTAINERS deleted file mode 100644 index d5775129ed224..0000000000000 --- a/bundles/org.openhab.binding.max/MAINTAINERS +++ /dev/null @@ -1 +0,0 @@ -Marcel Verpaalen diff --git a/bundles/org.openhab.binding.minecraft/src/main/java/org/openhab/binding/minecraft/internal/handler/MinecraftPlayerHandler.java b/bundles/org.openhab.binding.minecraft/src/main/java/org/openhab/binding/minecraft/internal/handler/MinecraftPlayerHandler.java index a90bbeb3a73cb..4f0edc9b344f9 100644 --- a/bundles/org.openhab.binding.minecraft/src/main/java/org/openhab/binding/minecraft/internal/handler/MinecraftPlayerHandler.java +++ b/bundles/org.openhab.binding.minecraft/src/main/java/org/openhab/binding/minecraft/internal/handler/MinecraftPlayerHandler.java @@ -130,7 +130,7 @@ private String getPlayerName() { private synchronized MinecraftServerHandler getBridgeHandler() { Bridge bridge = getBridge(); if (bridge == null) { - logger.debug("Required bridge not defined for device {}."); + logger.debug("Required bridge not defined for device {}.", getThing().getUID()); return null; } else { return getBridgeHandler(bridge); diff --git a/bundles/org.openhab.binding.minecraft/src/main/java/org/openhab/binding/minecraft/internal/handler/MinecraftSignHandler.java b/bundles/org.openhab.binding.minecraft/src/main/java/org/openhab/binding/minecraft/internal/handler/MinecraftSignHandler.java index e3bbbc650e2ce..538cdf82de440 100644 --- a/bundles/org.openhab.binding.minecraft/src/main/java/org/openhab/binding/minecraft/internal/handler/MinecraftSignHandler.java +++ b/bundles/org.openhab.binding.minecraft/src/main/java/org/openhab/binding/minecraft/internal/handler/MinecraftSignHandler.java @@ -102,7 +102,7 @@ private void updateSignState(SignData sign) { private synchronized MinecraftServerHandler getBridgeHandler() { Bridge bridge = getBridge(); if (bridge == null) { - logger.debug("Required bridge not defined for device {}."); + logger.debug("Required bridge not defined for device {}.", getThing().getUID()); return null; } else { return getBridgeHandler(bridge); diff --git a/bundles/org.openhab.binding.modbus/.classpath b/bundles/org.openhab.binding.modbus/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.modbus/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/binding/org.openhab.binding.modbus.test/.gitignore b/bundles/org.openhab.binding.modbus/.gitignore similarity index 100% rename from addons/binding/org.openhab.binding.modbus.test/.gitignore rename to bundles/org.openhab.binding.modbus/.gitignore diff --git a/bundles/org.openhab.binding.modbus/.project b/bundles/org.openhab.binding.modbus/.project new file mode 100644 index 0000000000000..04b5a91c87b48 --- /dev/null +++ b/bundles/org.openhab.binding.modbus/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.modbus + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.modbus/DEVELOPERS.md b/bundles/org.openhab.binding.modbus/DEVELOPERS.md similarity index 100% rename from addons/binding/org.openhab.binding.modbus/DEVELOPERS.md rename to bundles/org.openhab.binding.modbus/DEVELOPERS.md diff --git a/addons/binding/org.openhab.binding.enocean/NOTICE b/bundles/org.openhab.binding.modbus/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.enocean/NOTICE rename to bundles/org.openhab.binding.modbus/NOTICE diff --git a/addons/binding/org.openhab.binding.modbus/README.md b/bundles/org.openhab.binding.modbus/README.md similarity index 100% rename from addons/binding/org.openhab.binding.modbus/README.md rename to bundles/org.openhab.binding.modbus/README.md diff --git a/addons/binding/org.openhab.binding.modbus/doc/images/DiscoveryProcess.png b/bundles/org.openhab.binding.modbus/doc/images/DiscoveryProcess.png similarity index 100% rename from addons/binding/org.openhab.binding.modbus/doc/images/DiscoveryProcess.png rename to bundles/org.openhab.binding.modbus/doc/images/DiscoveryProcess.png diff --git a/addons/binding/org.openhab.binding.modbus/doc/images/ModbusExtensibleDiscovery.png b/bundles/org.openhab.binding.modbus/doc/images/ModbusExtensibleDiscovery.png similarity index 100% rename from addons/binding/org.openhab.binding.modbus/doc/images/ModbusExtensibleDiscovery.png rename to bundles/org.openhab.binding.modbus/doc/images/ModbusExtensibleDiscovery.png diff --git a/bundles/org.openhab.binding.modbus/pom.xml b/bundles/org.openhab.binding.modbus/pom.xml new file mode 100644 index 0000000000000..7d3f87a522f74 --- /dev/null +++ b/bundles/org.openhab.binding.modbus/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.modbus + + openHAB Add-ons :: Bundles :: Modbus Binding + + + + org.openhab.addons.bundles + org.openhab.io.transport.modbus + ${project.version} + provided + + + net.wimpi + jamod + 1.2.3.OH + provided + + + + diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/ModbusBindingConstants.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/ModbusBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/ModbusBindingConstants.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/ModbusBindingConstants.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/ModbusDiscoveryListener.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/ModbusDiscoveryListener.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/ModbusDiscoveryListener.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/ModbusDiscoveryListener.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/ModbusDiscoveryParticipant.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/ModbusDiscoveryParticipant.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/ModbusDiscoveryParticipant.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/ModbusDiscoveryParticipant.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/internal/ModbusDiscoveryService.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/internal/ModbusDiscoveryService.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/internal/ModbusDiscoveryService.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/internal/ModbusDiscoveryService.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/internal/ModbusEndpointDiscoveryService.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/internal/ModbusEndpointDiscoveryService.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/internal/ModbusEndpointDiscoveryService.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/internal/ModbusEndpointDiscoveryService.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/internal/ModbusThingHandlerDiscoveryService.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/internal/ModbusThingHandlerDiscoveryService.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/internal/ModbusThingHandlerDiscoveryService.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/discovery/internal/ModbusThingHandlerDiscoveryService.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/handler/EndpointNotInitializedException.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/handler/EndpointNotInitializedException.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/handler/EndpointNotInitializedException.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/handler/EndpointNotInitializedException.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/handler/ModbusEndpointThingHandler.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/handler/ModbusEndpointThingHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/handler/ModbusEndpointThingHandler.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/handler/ModbusEndpointThingHandler.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/AtomicStampedKeyValue.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/AtomicStampedKeyValue.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/AtomicStampedKeyValue.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/AtomicStampedKeyValue.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/ModbusBindingConstantsInternal.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/ModbusBindingConstantsInternal.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/ModbusBindingConstantsInternal.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/ModbusBindingConstantsInternal.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/ModbusConfigurationException.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/ModbusConfigurationException.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/ModbusConfigurationException.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/ModbusConfigurationException.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/ModbusHandlerFactory.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/ModbusHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/ModbusHandlerFactory.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/ModbusHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/Transformation.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/Transformation.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/Transformation.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/Transformation.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusDataConfiguration.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusDataConfiguration.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusDataConfiguration.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusDataConfiguration.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusPollerConfiguration.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusPollerConfiguration.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusPollerConfiguration.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusPollerConfiguration.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusSerialConfiguration.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusSerialConfiguration.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusSerialConfiguration.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusSerialConfiguration.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusTcpConfiguration.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusTcpConfiguration.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusTcpConfiguration.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/config/ModbusTcpConfiguration.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/AbstractModbusEndpointThingHandler.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/AbstractModbusEndpointThingHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/AbstractModbusEndpointThingHandler.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/AbstractModbusEndpointThingHandler.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusDataThingHandler.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusDataThingHandler.java similarity index 99% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusDataThingHandler.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusDataThingHandler.java index 27ff18e93ad3b..e5b06518b27ef 100644 --- a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusDataThingHandler.java +++ b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusDataThingHandler.java @@ -53,6 +53,7 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.modbus.handler.EndpointNotInitializedException; import org.openhab.binding.modbus.handler.ModbusEndpointThingHandler; import org.openhab.binding.modbus.internal.ModbusBindingConstantsInternal; @@ -230,7 +231,7 @@ public synchronized void handleCommand(ChannelUID channelUID, Command command) { * In case of JSON as transformation output, the output processed using {@link processJsonTransform}. * * @param channelUID channel UID corresponding to received command - * @param command command to be transformed + * @param command command to be transformed * @return transformed command. Null is returned with JSON transformation outputs and configuration errors * * @see processJsonTransform @@ -653,7 +654,7 @@ public synchronized void onRegisters(ModbusReadRequestBlueprint request, ModbusR if (readValueType == null) { return; } - DecimalType numericState; + State numericState; // extractIndex: // e.g. with bit, extractIndex=4 means 5th bit (from right) ("10.4" -> 5th bit of register 10, "10.4" -> 5th bit @@ -674,7 +675,8 @@ public synchronized void onRegisters(ModbusReadRequestBlueprint request, ModbusR int itemsPerRegister = 16 / readValueType.getBits(); extractIndex = (readIndex.get() - pollStart) * itemsPerRegister + subIndex; } - numericState = ModbusBitUtilities.extractStateFromRegisters(registers, extractIndex, readValueType); + numericState = ModbusBitUtilities.extractStateFromRegisters(registers, extractIndex, readValueType) + .map(state -> (State) state).orElse(UnDefType.UNDEF); boolean boolValue = !numericState.equals(DecimalType.ZERO); Map values = processUpdatedValue(numericState, boolValue); logger.debug( @@ -790,11 +792,11 @@ public synchronized void onWriteResponse(ModbusWriteRequestBlueprint request, Mo /** * Update linked channels * - * @param numericState numeric state corresponding to polled data - * @param boolValue boolean value corresponding to polled data + * @param numericState numeric state corresponding to polled data (or UNDEF with floating point NaN or infinity) + * @param boolValue boolean value corresponding to polled data * @return updated channel data */ - private Map processUpdatedValue(DecimalType numericState, boolean boolValue) { + private Map processUpdatedValue(State numericState, boolean boolValue) { Map<@NonNull ChannelUID, @NonNull State> states = new HashMap<>(); CHANNEL_ID_TO_ACCEPTED_TYPES.keySet().stream().forEach(channelId -> { ChannelUID channelUID = getChannelUID(channelId); diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusPollerThingHandler.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusPollerThingHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusPollerThingHandler.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusPollerThingHandler.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusPollerThingHandlerImpl.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusPollerThingHandlerImpl.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusPollerThingHandlerImpl.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusPollerThingHandlerImpl.java index 2970b3fdcc157..e2bbc9608d6f7 100644 --- a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusPollerThingHandlerImpl.java +++ b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusPollerThingHandlerImpl.java @@ -38,15 +38,15 @@ import org.openhab.binding.modbus.internal.AtomicStampedKeyValue; import org.openhab.binding.modbus.internal.ModbusBindingConstantsInternal; import org.openhab.binding.modbus.internal.config.ModbusPollerConfiguration; +import org.openhab.io.transport.modbus.BasicModbusReadRequestBlueprint; +import org.openhab.io.transport.modbus.BasicPollTaskImpl; import org.openhab.io.transport.modbus.BitArray; import org.openhab.io.transport.modbus.ModbusManager; import org.openhab.io.transport.modbus.ModbusReadCallback; import org.openhab.io.transport.modbus.ModbusReadFunctionCode; import org.openhab.io.transport.modbus.ModbusReadRequestBlueprint; -import org.openhab.io.transport.modbus.BasicModbusReadRequestBlueprint; import org.openhab.io.transport.modbus.ModbusRegisterArray; import org.openhab.io.transport.modbus.PollTask; -import org.openhab.io.transport.modbus.BasicPollTaskImpl; import org.openhab.io.transport.modbus.endpoint.ModbusSlaveEndpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusSerialThingHandler.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusSerialThingHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusSerialThingHandler.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusSerialThingHandler.java diff --git a/addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusTcpThingHandler.java b/bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusTcpThingHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusTcpThingHandler.java rename to bundles/org.openhab.binding.modbus/src/main/java/org/openhab/binding/modbus/internal/handler/ModbusTcpThingHandler.java diff --git a/addons/binding/org.openhab.binding.modbus/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.modbus/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.modbus/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.modbus/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.modbus/ESH-INF/thing/bridge-poller.xml b/bundles/org.openhab.binding.modbus/src/main/resources/ESH-INF/thing/bridge-poller.xml similarity index 100% rename from addons/binding/org.openhab.binding.modbus/ESH-INF/thing/bridge-poller.xml rename to bundles/org.openhab.binding.modbus/src/main/resources/ESH-INF/thing/bridge-poller.xml diff --git a/addons/binding/org.openhab.binding.modbus/ESH-INF/thing/bridge-serial.xml b/bundles/org.openhab.binding.modbus/src/main/resources/ESH-INF/thing/bridge-serial.xml similarity index 100% rename from addons/binding/org.openhab.binding.modbus/ESH-INF/thing/bridge-serial.xml rename to bundles/org.openhab.binding.modbus/src/main/resources/ESH-INF/thing/bridge-serial.xml diff --git a/addons/binding/org.openhab.binding.modbus/ESH-INF/thing/bridge-tcp.xml b/bundles/org.openhab.binding.modbus/src/main/resources/ESH-INF/thing/bridge-tcp.xml similarity index 100% rename from addons/binding/org.openhab.binding.modbus/ESH-INF/thing/bridge-tcp.xml rename to bundles/org.openhab.binding.modbus/src/main/resources/ESH-INF/thing/bridge-tcp.xml diff --git a/addons/binding/org.openhab.binding.modbus/ESH-INF/thing/thing-data.xml b/bundles/org.openhab.binding.modbus/src/main/resources/ESH-INF/thing/thing-data.xml similarity index 100% rename from addons/binding/org.openhab.binding.modbus/ESH-INF/thing/thing-data.xml rename to bundles/org.openhab.binding.modbus/src/main/resources/ESH-INF/thing/thing-data.xml diff --git a/addons/binding/org.openhab.binding.modbus/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.modbus/src/main/resources/ESH-INF/thing/thing-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.modbus/ESH-INF/thing/thing-types.xml rename to bundles/org.openhab.binding.modbus/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/addons/binding/org.openhab.binding.modbus.test/src/test/java/org/openhab/binding/modbus/internal/AtomicStampedKeyValueTest.java b/bundles/org.openhab.binding.modbus/src/test/java/org/openhab/binding/modbus/internal/AtomicStampedKeyValueTest.java similarity index 97% rename from addons/binding/org.openhab.binding.modbus.test/src/test/java/org/openhab/binding/modbus/internal/AtomicStampedKeyValueTest.java rename to bundles/org.openhab.binding.modbus/src/test/java/org/openhab/binding/modbus/internal/AtomicStampedKeyValueTest.java index 2f9d7b59ec7b2..1c32041338d2b 100644 --- a/addons/binding/org.openhab.binding.modbus.test/src/test/java/org/openhab/binding/modbus/internal/AtomicStampedKeyValueTest.java +++ b/bundles/org.openhab.binding.modbus/src/test/java/org/openhab/binding/modbus/internal/AtomicStampedKeyValueTest.java @@ -18,19 +18,18 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; -import org.openhab.binding.modbus.internal.AtomicStampedKeyValue; @RunWith(MockitoJUnitRunner.class) public class AtomicStampedKeyValueTest { @Test(expected = NullPointerException.class) public void testInitWithNullKey() { - new AtomicStampedKeyValue(0, null, new Object()); + new AtomicStampedKeyValue<>(0, null, new Object()); } @Test(expected = NullPointerException.class) public void testInitWithNullValue() { - new AtomicStampedKeyValue(0, new Object(), null); + new AtomicStampedKeyValue<>(0, new Object(), null); } @Test diff --git a/addons/binding/org.openhab.binding.modbus.test/src/test/java/org/openhab/binding/modbus/internal/ModbusDataHandlerTest.java b/bundles/org.openhab.binding.modbus/src/test/java/org/openhab/binding/modbus/internal/ModbusDataHandlerTest.java similarity index 97% rename from addons/binding/org.openhab.binding.modbus.test/src/test/java/org/openhab/binding/modbus/internal/ModbusDataHandlerTest.java rename to bundles/org.openhab.binding.modbus/src/test/java/org/openhab/binding/modbus/internal/ModbusDataHandlerTest.java index 4eb2791d63a5e..e1ae18b358049 100644 --- a/addons/binding/org.openhab.binding.modbus.test/src/test/java/org/openhab/binding/modbus/internal/ModbusDataHandlerTest.java +++ b/bundles/org.openhab.binding.modbus/src/test/java/org/openhab/binding/modbus/internal/ModbusDataHandlerTest.java @@ -55,6 +55,7 @@ import org.eclipse.smarthome.core.library.items.SwitchItem; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.OpenClosedType; import org.eclipse.smarthome.core.library.types.StringType; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.Channel; @@ -78,6 +79,7 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; import org.eclipse.smarthome.test.java.JavaTest; import org.eclipse.smarthome.test.storage.VolatileStorageService; import org.hamcrest.Matcher; @@ -120,7 +122,7 @@ public class ModbusDataHandlerTest extends JavaTest { private class ItemChannelLinkRegistryTestImpl extends ItemChannelLinkRegistry { private final class ManagedItemChannelLinkProviderExtension extends ManagedItemChannelLinkProvider { ManagedItemChannelLinkProviderExtension() { - setStorageService(new VolatileStorageService()); + super(new VolatileStorageService()); } } @@ -140,7 +142,7 @@ private class ItemRegisteryTestImpl extends AbstractRegistry SUPPORTED_THING_TYPES_UIDS = Stream + .of(MqttBindingConstants.GENERIC_MQTT_THING).collect(Collectors.toSet()); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Activate + @Override + protected void activate(ComponentContext componentContext) { + super.activate(componentContext); + } + + @Deactivate + @Override + protected void deactivate(ComponentContext componentContext) { + super.deactivate(componentContext); + } + + @Reference + protected void setStateDescriptionProvider(MqttChannelStateDescriptionProvider stateDescription) { + this.stateDescriptionProvider = stateDescription; + } + + protected void unsetStateDescriptionProvider(MqttChannelStateDescriptionProvider stateDescription) { + this.stateDescriptionProvider = null; + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (thingTypeUID.equals(MqttBindingConstants.GENERIC_MQTT_THING)) { + return new GenericMQTTThingHandler(thing, stateDescriptionProvider, this, 1500); + } + return null; + } + + @Override + public @Nullable TransformationService getTransformationService(String type) { + return TransformationHelper.getTransformationService(bundleContext, type); + } + +} diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/internal/handler/GenericMQTTThingHandler.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/internal/handler/GenericMQTTThingHandler.java similarity index 92% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/internal/handler/GenericMQTTThingHandler.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/internal/handler/GenericMQTTThingHandler.java index 83c37953d46f0..46641f12b2fba 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/internal/handler/GenericMQTTThingHandler.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/internal/handler/GenericMQTTThingHandler.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.handler; +package org.openhab.binding.mqtt.generic.internal.handler; import java.util.ArrayList; import java.util.HashMap; @@ -34,15 +34,15 @@ import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; import org.eclipse.smarthome.core.types.StateDescription; import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; -import org.openhab.binding.mqtt.AbstractMQTTThingHandler; -import org.openhab.binding.mqtt.ChannelConfig; -import org.openhab.binding.mqtt.ChannelState; -import org.openhab.binding.mqtt.ChannelStateTransformation; -import org.openhab.binding.mqtt.ChannelStateUpdateListener; -import org.openhab.binding.mqtt.MqttChannelStateDescriptionProvider; -import org.openhab.binding.mqtt.TransformationServiceProvider; -import org.openhab.binding.mqtt.values.Value; -import org.openhab.binding.mqtt.values.ValueFactory; +import org.openhab.binding.mqtt.generic.AbstractMQTTThingHandler; +import org.openhab.binding.mqtt.generic.ChannelConfig; +import org.openhab.binding.mqtt.generic.ChannelState; +import org.openhab.binding.mqtt.generic.ChannelStateTransformation; +import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener; +import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider; +import org.openhab.binding.mqtt.generic.TransformationServiceProvider; +import org.openhab.binding.mqtt.generic.values.Value; +import org.openhab.binding.mqtt.generic.values.ValueFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/AbstractMqttAttributeClass.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/AbstractMqttAttributeClass.java similarity index 99% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/AbstractMqttAttributeClass.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/AbstractMqttAttributeClass.java index 282dbcfab099c..d109d6d5d7bb0 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/AbstractMqttAttributeClass.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/AbstractMqttAttributeClass.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.mapping; +package org.openhab.binding.mqtt.generic.mapping; import java.lang.ref.WeakReference; import java.lang.reflect.Field; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/MQTTvalueTransform.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/MQTTvalueTransform.java similarity index 96% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/MQTTvalueTransform.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/MQTTvalueTransform.java index 26a2c2eb07f90..bacc4a39eb3d5 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/MQTTvalueTransform.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/MQTTvalueTransform.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.mapping; +package org.openhab.binding.mqtt.generic.mapping; import static java.lang.annotation.ElementType.FIELD; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/MandatoryField.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/MandatoryField.java similarity index 96% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/MandatoryField.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/MandatoryField.java index 07c8d90efc653..11fa5b899d534 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/MandatoryField.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/MandatoryField.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.mapping; +package org.openhab.binding.mqtt.generic.mapping; import static java.lang.annotation.ElementType.FIELD; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/SubscribeFieldToMQTTtopic.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/SubscribeFieldToMQTTtopic.java similarity index 98% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/SubscribeFieldToMQTTtopic.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/SubscribeFieldToMQTTtopic.java index 4ed83b6413701..69f29c3f28307 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/SubscribeFieldToMQTTtopic.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/SubscribeFieldToMQTTtopic.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.mapping; +package org.openhab.binding.mqtt.generic.mapping; import java.lang.reflect.Field; import java.math.BigDecimal; @@ -134,6 +134,12 @@ public void processMessage(@NonNull String topic, byte @NonNull [] payload) { scheduledFuture.cancel(false); this.scheduledFuture = null; } + + if (payload.length == 0) { + logger.debug("NULL payload on topic: {}", topic); + return; + } + String valueStr = new String(payload, StandardCharsets.UTF_8); // Check if there is a manipulation annotation attached to the field diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/TopicPrefix.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/TopicPrefix.java similarity index 95% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/TopicPrefix.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/TopicPrefix.java index 99fb4d803b174..58462f8428b19 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/mapping/TopicPrefix.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/mapping/TopicPrefix.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.mapping; +package org.openhab.binding.mqtt.generic.mapping; import static java.lang.annotation.ElementType.*; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/tools/ChildMap.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/tools/ChildMap.java similarity index 98% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/tools/ChildMap.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/tools/ChildMap.java index 78164b328dd3d..dd4da36fed7bf 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/tools/ChildMap.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/tools/ChildMap.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.tools; +package org.openhab.binding.mqtt.generic.tools; import java.util.Map; import java.util.Set; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/tools/DelayedBatchProcessing.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/tools/DelayedBatchProcessing.java similarity index 98% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/tools/DelayedBatchProcessing.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/tools/DelayedBatchProcessing.java index 848e4589e21d3..a5a3a66e78241 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/tools/DelayedBatchProcessing.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/tools/DelayedBatchProcessing.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.tools; +package org.openhab.binding.mqtt.generic.tools; import java.util.ArrayList; import java.util.Collections; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/tools/JsonReaderDelegate.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/tools/JsonReaderDelegate.java similarity index 98% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/tools/JsonReaderDelegate.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/tools/JsonReaderDelegate.java index e2895c4fcf371..2bee9d967c3f5 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/tools/JsonReaderDelegate.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/tools/JsonReaderDelegate.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.tools; +package org.openhab.binding.mqtt.generic.tools; import java.io.IOException; import java.io.StringReader; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/tools/WaitForTopicValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/tools/WaitForTopicValue.java similarity index 98% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/tools/WaitForTopicValue.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/tools/WaitForTopicValue.java index 13dc385437d3d..4733e3b3a187f 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/tools/WaitForTopicValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/tools/WaitForTopicValue.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.tools; +package org.openhab.binding.mqtt.generic.tools; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/ColorValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ColorValue.java similarity index 99% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/ColorValue.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ColorValue.java index 34c3f253d5beb..713c007cce54a 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/ColorValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ColorValue.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.values; +package org.openhab.binding.mqtt.generic.values; import java.math.BigDecimal; import java.util.stream.Collectors; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/DateTimeValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/DateTimeValue.java similarity index 97% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/DateTimeValue.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/DateTimeValue.java index 4eeef8ca49861..2d9e85f9357bb 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/DateTimeValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/DateTimeValue.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.values; +package org.openhab.binding.mqtt.generic.values; import java.time.format.DateTimeFormatter; import java.util.stream.Collectors; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/ImageValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ImageValue.java similarity index 95% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/ImageValue.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ImageValue.java index 3f1c20ab94fb5..143b5acf80326 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/ImageValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ImageValue.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.values; +package org.openhab.binding.mqtt.generic.values; import java.util.Collections; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/LocationValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/LocationValue.java similarity index 96% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/LocationValue.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/LocationValue.java index 0c9436f607347..a319cc0705232 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/LocationValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/LocationValue.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.values; +package org.openhab.binding.mqtt.generic.values; import java.util.stream.Collectors; import java.util.stream.Stream; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/NumberValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/NumberValue.java similarity index 98% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/NumberValue.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/NumberValue.java index 55fe38c98f661..dc2a9bfb275c5 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/NumberValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/NumberValue.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.values; +package org.openhab.binding.mqtt.generic.values; import java.math.BigDecimal; import java.util.Collections; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/OnOffValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/OnOffValue.java similarity index 98% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/OnOffValue.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/OnOffValue.java index b92e0cd3ebb92..4363ed8aa5456 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/OnOffValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/OnOffValue.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.values; +package org.openhab.binding.mqtt.generic.values; import java.util.stream.Collectors; import java.util.stream.Stream; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/OpenCloseValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/OpenCloseValue.java similarity index 98% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/OpenCloseValue.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/OpenCloseValue.java index 2b62ea01ca46d..c2e5369792a33 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/OpenCloseValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/OpenCloseValue.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.values; +package org.openhab.binding.mqtt.generic.values; import java.util.stream.Collectors; import java.util.stream.Stream; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/PercentageValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/PercentageValue.java similarity index 99% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/PercentageValue.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/PercentageValue.java index 03ee7a7219e7d..993510719f8ac 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/PercentageValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/PercentageValue.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.values; +package org.openhab.binding.mqtt.generic.values; import java.math.BigDecimal; import java.util.Collections; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/RollershutterValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/RollershutterValue.java similarity index 99% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/RollershutterValue.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/RollershutterValue.java index 7dd2ac473bfee..f43bc722e7bc1 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/RollershutterValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/RollershutterValue.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.values; +package org.openhab.binding.mqtt.generic.values; import java.util.stream.Collectors; import java.util.stream.Stream; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/TextValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java similarity index 98% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/TextValue.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java index 9040af5c322d3..6f51f5c9287e1 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/TextValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.values; +package org.openhab.binding.mqtt.generic.values; import java.util.ArrayList; import java.util.Collections; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/Value.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/Value.java similarity index 99% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/Value.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/Value.java index 787900f865443..95218507cfe88 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/Value.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/Value.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.values; +package org.openhab.binding.mqtt.generic.values; import java.io.ByteArrayInputStream; import java.io.IOException; diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/ValueFactory.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ValueFactory.java similarity index 94% rename from bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/ValueFactory.java rename to bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ValueFactory.java index bbe4b7911c07b..843e60a8fa38b 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/values/ValueFactory.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ValueFactory.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.values; +package org.openhab.binding.mqtt.generic.values; import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.mqtt.ChannelConfig; -import org.openhab.binding.mqtt.internal.MqttBindingConstants; +import org.openhab.binding.mqtt.generic.ChannelConfig; +import org.openhab.binding.mqtt.generic.internal.MqttBindingConstants; /** * A factory t diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/internal/MqttBindingConstants.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/internal/MqttBindingConstants.java deleted file mode 100644 index d974397c84dd5..0000000000000 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/internal/MqttBindingConstants.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt.internal; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.thing.ThingTypeUID; - -/** - * The {@link MqttBindingConstants} class defines common constants, which are - * used across the whole binding. - * - * @author David Graeff - Initial contribution - */ -@NonNullByDefault -public class MqttBindingConstants { - - public static final String BINDING_ID = "mqtt"; - - // List of all Thing Type UIDs - public static final ThingTypeUID GENERIC_MQTT_THING = new ThingTypeUID(BINDING_ID, "topic"); - - // Generic thing channel types - public static final String COLOR_RGB = "colorRGB"; - public static final String COLOR_HSB = "colorHSB"; - public static final String CONTACT = "contact"; - public static final String DIMMER = "dimmer"; - public static final String NUMBER = "number"; - public static final String STRING = "string"; - public static final String SWITCH = "switch"; - public static final String IMAGE = "image"; - public static final String LOCATION = "location"; - public static final String DATETIME = "datetime"; - public static final String ROLLERSHUTTER = "rollershutter"; -} diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/internal/MqttThingHandlerFactory.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/internal/MqttThingHandlerFactory.java deleted file mode 100644 index 9815249d792c2..0000000000000 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/internal/MqttThingHandlerFactory.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt.internal; - -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; -import org.eclipse.smarthome.core.thing.binding.ThingHandler; -import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; -import org.eclipse.smarthome.core.transform.TransformationHelper; -import org.eclipse.smarthome.core.transform.TransformationService; -import org.openhab.binding.mqtt.MqttChannelStateDescriptionProvider; -import org.openhab.binding.mqtt.TransformationServiceProvider; -import org.openhab.binding.mqtt.internal.handler.GenericMQTTThingHandler; -import org.osgi.service.component.ComponentContext; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Deactivate; -import org.osgi.service.component.annotations.Reference; - -/** - * The {@link MqttThingHandlerFactory} is responsible for creating things and thing - * handlers. - * - * @author David Graeff - Initial contribution - */ -@Component(service = ThingHandlerFactory.class) -@NonNullByDefault -public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements TransformationServiceProvider { - private @NonNullByDefault({}) MqttChannelStateDescriptionProvider stateDescriptionProvider; - private static final Set SUPPORTED_THING_TYPES_UIDS = Stream - .of(MqttBindingConstants.GENERIC_MQTT_THING).collect(Collectors.toSet()); - - @Override - public boolean supportsThingType(ThingTypeUID thingTypeUID) { - return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); - } - - @Activate - @Override - protected void activate(ComponentContext componentContext) { - super.activate(componentContext); - } - - @Deactivate - @Override - protected void deactivate(ComponentContext componentContext) { - super.deactivate(componentContext); - } - - @Reference - protected void setStateDescriptionProvider(MqttChannelStateDescriptionProvider stateDescription) { - this.stateDescriptionProvider = stateDescription; - } - - protected void unsetStateDescriptionProvider(MqttChannelStateDescriptionProvider stateDescription) { - this.stateDescriptionProvider = null; - } - - @Override - protected @Nullable ThingHandler createHandler(Thing thing) { - ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - - if (thingTypeUID.equals(MqttBindingConstants.GENERIC_MQTT_THING)) { - return new GenericMQTTThingHandler(thing, stateDescriptionProvider, this, 1500); - } - return null; - } - - @Override - public @Nullable TransformationService getTransformationService(String type) { - return TransformationHelper.getTransformationService(bundleContext, type); - } - -} diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/ChannelStateHelper.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/ChannelStateHelper.java deleted file mode 100644 index 85baee003f183..0000000000000 --- a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/ChannelStateHelper.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; -import org.openhab.binding.mqtt.ChannelState; - -@NonNullByDefault -public class ChannelStateHelper { - public static void setConnection(ChannelState cs, MqttBrokerConnection connection) { - cs.setConnection(connection); - } -} diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/ThingHandlerHelper.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/ThingHandlerHelper.java deleted file mode 100644 index 06ececd0a62be..0000000000000 --- a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/ThingHandlerHelper.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; - -@NonNullByDefault -public class ThingHandlerHelper { - public static void setConnection(AbstractMQTTThingHandler h, MqttBrokerConnection connection) { - h.connection = connection; - } -} diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateHelper.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateHelper.java new file mode 100644 index 0000000000000..3fd3f2fafba7c --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateHelper.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.generic; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; +import org.openhab.binding.mqtt.generic.ChannelState; + +@NonNullByDefault +public class ChannelStateHelper { + public static void setConnection(ChannelState cs, MqttBrokerConnection connection) { + cs.setConnection(connection); + } +} diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/ChannelStateTests.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateTests.java similarity index 94% rename from bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/ChannelStateTests.java rename to bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateTests.java index 583ef00e74413..e52cb22539c7c 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/ChannelStateTests.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateTests.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt; +package org.openhab.binding.mqtt.generic; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.assertThat; @@ -41,13 +41,17 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.Spy; -import org.openhab.binding.mqtt.values.ColorValue; -import org.openhab.binding.mqtt.values.DateTimeValue; -import org.openhab.binding.mqtt.values.ImageValue; -import org.openhab.binding.mqtt.values.LocationValue; -import org.openhab.binding.mqtt.values.NumberValue; -import org.openhab.binding.mqtt.values.PercentageValue; -import org.openhab.binding.mqtt.values.TextValue; +import org.openhab.binding.mqtt.generic.ChannelConfig; +import org.openhab.binding.mqtt.generic.ChannelConfigBuilder; +import org.openhab.binding.mqtt.generic.ChannelState; +import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener; +import org.openhab.binding.mqtt.generic.values.ColorValue; +import org.openhab.binding.mqtt.generic.values.DateTimeValue; +import org.openhab.binding.mqtt.generic.values.ImageValue; +import org.openhab.binding.mqtt.generic.values.LocationValue; +import org.openhab.binding.mqtt.generic.values.NumberValue; +import org.openhab.binding.mqtt.generic.values.PercentageValue; +import org.openhab.binding.mqtt.generic.values.TextValue; /** * Tests the {@link ChannelState} class. diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/ChannelStateTransformationTests.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateTransformationTests.java similarity index 91% rename from bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/ChannelStateTransformationTests.java rename to bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateTransformationTests.java index 3e4ee404206f1..9055e9e5be977 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/ChannelStateTransformationTests.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateTransformationTests.java @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt; +package org.openhab.binding.mqtt.generic; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; -import static org.openhab.binding.mqtt.internal.handler.ThingChannelConstants.*; +import static org.openhab.binding.mqtt.generic.internal.handler.ThingChannelConstants.*; import java.util.concurrent.CompletableFuture; @@ -36,12 +36,12 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; -import org.openhab.binding.mqtt.ChannelState; -import org.openhab.binding.mqtt.ChannelStateTransformation; -import org.openhab.binding.mqtt.MqttChannelStateDescriptionProvider; -import org.openhab.binding.mqtt.TransformationServiceProvider; +import org.openhab.binding.mqtt.generic.ChannelState; +import org.openhab.binding.mqtt.generic.ChannelStateTransformation; +import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider; +import org.openhab.binding.mqtt.generic.TransformationServiceProvider; +import org.openhab.binding.mqtt.generic.internal.handler.GenericMQTTThingHandler; import org.openhab.binding.mqtt.handler.AbstractBrokerHandler; -import org.openhab.binding.mqtt.internal.handler.GenericMQTTThingHandler; /** * Tests cases for {@link ThingHandler} to test the json transformation. diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ThingHandlerHelper.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ThingHandlerHelper.java new file mode 100644 index 0000000000000..427deb83cf868 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ThingHandlerHelper.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.generic; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; +import org.openhab.binding.mqtt.generic.AbstractMQTTThingHandler; + +@NonNullByDefault +public class ThingHandlerHelper { + public static void setConnection(AbstractMQTTThingHandler h, MqttBrokerConnection connection) { + h.connection = connection; + } +} diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/internal/handler/GenericThingHandlerTests.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/internal/handler/GenericThingHandlerTests.java similarity index 91% rename from bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/internal/handler/GenericThingHandlerTests.java rename to bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/internal/handler/GenericThingHandlerTests.java index c29ea4bca8e82..5b58f15b995e0 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/internal/handler/GenericThingHandlerTests.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/internal/handler/GenericThingHandlerTests.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.handler; +package org.openhab.binding.mqtt.generic.internal.handler; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import static org.openhab.binding.mqtt.internal.handler.ThingChannelConstants.*; +import static org.openhab.binding.mqtt.generic.internal.handler.ThingChannelConstants.*; import java.util.concurrent.CompletableFuture; @@ -35,16 +35,17 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.openhab.binding.mqtt.ChannelConfig; -import org.openhab.binding.mqtt.ChannelConfigBuilder; -import org.openhab.binding.mqtt.ChannelState; -import org.openhab.binding.mqtt.MqttChannelStateDescriptionProvider; -import org.openhab.binding.mqtt.ThingHandlerHelper; -import org.openhab.binding.mqtt.TransformationServiceProvider; +import org.openhab.binding.mqtt.generic.ChannelConfig; +import org.openhab.binding.mqtt.generic.ChannelConfigBuilder; +import org.openhab.binding.mqtt.generic.ChannelState; +import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider; +import org.openhab.binding.mqtt.generic.ThingHandlerHelper; +import org.openhab.binding.mqtt.generic.TransformationServiceProvider; +import org.openhab.binding.mqtt.generic.internal.handler.GenericMQTTThingHandler; +import org.openhab.binding.mqtt.generic.values.OnOffValue; +import org.openhab.binding.mqtt.generic.values.TextValue; +import org.openhab.binding.mqtt.generic.values.ValueFactory; import org.openhab.binding.mqtt.handler.AbstractBrokerHandler; -import org.openhab.binding.mqtt.values.OnOffValue; -import org.openhab.binding.mqtt.values.TextValue; -import org.openhab.binding.mqtt.values.ValueFactory; /** * Tests cases for {@link GenericMQTTThingHandler}. diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/internal/handler/ThingChannelConstants.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/internal/handler/ThingChannelConstants.java new file mode 100644 index 0000000000000..7a983766d6882 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/internal/handler/ThingChannelConstants.java @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.generic.internal.handler; + +import static org.openhab.binding.mqtt.generic.internal.MqttBindingConstants.*; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.openhab.binding.mqtt.generic.internal.MqttBindingConstants; + +/** + * Static test definitions, like thing, bridge and channel definitions + * + * @author David Graeff - Initial contribution + */ +public class ThingChannelConstants { + // Common ThingUID and ChannelUIDs + final public static ThingUID testGenericThing = new ThingUID(GENERIC_MQTT_THING, "genericthing"); + + final public static ChannelTypeUID textChannel = new ChannelTypeUID(BINDING_ID, MqttBindingConstants.STRING); + final public static ChannelTypeUID textWithJsonChannel = new ChannelTypeUID(BINDING_ID, + MqttBindingConstants.STRING); + final public static ChannelTypeUID onoffChannel = new ChannelTypeUID(BINDING_ID, MqttBindingConstants.SWITCH); + final public static ChannelTypeUID numberChannel = new ChannelTypeUID(BINDING_ID, MqttBindingConstants.NUMBER); + final public static ChannelTypeUID percentageChannel = new ChannelTypeUID(BINDING_ID, MqttBindingConstants.DIMMER); + final public static ChannelTypeUID unknownChannel = new ChannelTypeUID(BINDING_ID, "unknown"); + + final public static ChannelUID textChannelUID = new ChannelUID(testGenericThing, "mytext"); + + final public static String jsonPathJSON = "{ \"device\": { \"status\": { \"temperature\": 23.2 }}}"; + final public static String jsonPathPattern = "$.device.status.temperature"; + + final public static List thingChannelList = new ArrayList<>(); + final public static List thingChannelListWithJson = new ArrayList<>(); + + /** + * Create a channel with exact the parameters we need for the tests + * + * @param id Channel ID + * @param acceptedType Accept type + * @param config The configuration + * @param channelTypeUID ChannelTypeUID provided by the static definitions + * @return + */ + public static Channel cb(String id, String acceptedType, Configuration config, ChannelTypeUID channelTypeUID) { + return ChannelBuilder.create(new ChannelUID(testGenericThing, id), acceptedType).withConfiguration(config) + .withType(channelTypeUID).build(); + } + + static { + thingChannelList.add(cb("mytext", "TextItemType", textConfiguration(), textChannel)); + thingChannelList.add(cb("onoff", "OnOffType", onoffConfiguration(), onoffChannel)); + thingChannelList.add(cb("num", "NumberType", numberConfiguration(), numberChannel)); + thingChannelList.add(cb("percent", "NumberType", percentageConfiguration(), percentageChannel)); + + thingChannelListWithJson.add(cb("mytext", "TextItemType", textConfigurationWithJson(), textWithJsonChannel)); + thingChannelListWithJson.add(cb("onoff", "OnOffType", onoffConfiguration(), onoffChannel)); + thingChannelListWithJson.add(cb("num", "NumberType", numberConfiguration(), numberChannel)); + thingChannelListWithJson.add(cb("percent", "NumberType", percentageConfiguration(), percentageChannel)); + } + + static Configuration textConfiguration() { + Map data = new HashMap<>(); + data.put("stateTopic", "test/state"); + data.put("commandTopic", "test/command"); + return new Configuration(data); + } + + static Configuration textConfigurationWithJson() { + Map data = new HashMap<>(); + data.put("stateTopic", "test/state"); + data.put("commandTopic", "test/command"); + data.put("transformationPattern", "JSONPATH:" + jsonPathPattern); + return new Configuration(data); + } + + private static Configuration numberConfiguration() { + Map data = new HashMap<>(); + data.put("stateTopic", "test/state"); + data.put("commandTopic", "test/command"); + data.put("min", BigDecimal.valueOf(1)); + data.put("max", BigDecimal.valueOf(99)); + data.put("step", BigDecimal.valueOf(2)); + data.put("isDecimal", true); + return new Configuration(data); + } + + private static Configuration percentageConfiguration() { + Map data = new HashMap<>(); + data.put("stateTopic", "test/state"); + data.put("commandTopic", "test/command"); + data.put("on", "ON"); + data.put("off", "OFF"); + return new Configuration(data); + } + + private static Configuration onoffConfiguration() { + Map data = new HashMap<>(); + data.put("stateTopic", "test/state"); + data.put("commandTopic", "test/command"); + data.put("on", "ON"); + data.put("off", "OFF"); + data.put("inverse", true); + return new Configuration(data); + } + +} diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/mapping/MqttTopicClassMapperTests.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/mapping/MqttTopicClassMapperTests.java similarity index 94% rename from bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/mapping/MqttTopicClassMapperTests.java rename to bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/mapping/MqttTopicClassMapperTests.java index e6f0c8177cfb7..dbadad2562935 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/mapping/MqttTopicClassMapperTests.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/mapping/MqttTopicClassMapperTests.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.mapping; +package org.openhab.binding.mqtt.generic.mapping; import static java.lang.annotation.ElementType.FIELD; import static org.hamcrest.CoreMatchers.is; @@ -36,7 +36,11 @@ import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.mockito.invocation.InvocationOnMock; -import org.openhab.binding.mqtt.mapping.AbstractMqttAttributeClass.AttributeChanged; +import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass; +import org.openhab.binding.mqtt.generic.mapping.MQTTvalueTransform; +import org.openhab.binding.mqtt.generic.mapping.SubscribeFieldToMQTTtopic; +import org.openhab.binding.mqtt.generic.mapping.TopicPrefix; +import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass.AttributeChanged; /** * Tests cases for {@link AbstractMqttAttributeClass}. diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/mapping/SubscribeFieldToMQTTtopicTests.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/mapping/SubscribeFieldToMQTTtopicTests.java similarity index 92% rename from bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/mapping/SubscribeFieldToMQTTtopicTests.java rename to bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/mapping/SubscribeFieldToMQTTtopicTests.java index b555b912408a4..967529eaf9c7f 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/mapping/SubscribeFieldToMQTTtopicTests.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/mapping/SubscribeFieldToMQTTtopicTests.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.mapping; +package org.openhab.binding.mqtt.generic.mapping; import static java.lang.annotation.ElementType.FIELD; import static org.hamcrest.CoreMatchers.is; @@ -37,11 +37,11 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.openhab.binding.mqtt.mapping.AbstractMqttAttributeClass; -import org.openhab.binding.mqtt.mapping.MQTTvalueTransform; -import org.openhab.binding.mqtt.mapping.SubscribeFieldToMQTTtopic; -import org.openhab.binding.mqtt.mapping.TopicPrefix; -import org.openhab.binding.mqtt.mapping.SubscribeFieldToMQTTtopic.FieldChanged; +import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass; +import org.openhab.binding.mqtt.generic.mapping.MQTTvalueTransform; +import org.openhab.binding.mqtt.generic.mapping.SubscribeFieldToMQTTtopic; +import org.openhab.binding.mqtt.generic.mapping.TopicPrefix; +import org.openhab.binding.mqtt.generic.mapping.SubscribeFieldToMQTTtopic.FieldChanged; /** * Tests cases for {@link SubscribeFieldToMQTTtopic}. diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/values/ValueTests.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/values/ValueTests.java similarity index 94% rename from bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/values/ValueTests.java rename to bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/values/ValueTests.java index 7fab52b396e89..5c4013c12b0d5 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/values/ValueTests.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/values/ValueTests.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.values; +package org.openhab.binding.mqtt.generic.values; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; @@ -27,6 +27,14 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.TypeParser; import org.junit.Test; +import org.openhab.binding.mqtt.generic.values.ColorValue; +import org.openhab.binding.mqtt.generic.values.NumberValue; +import org.openhab.binding.mqtt.generic.values.OnOffValue; +import org.openhab.binding.mqtt.generic.values.OpenCloseValue; +import org.openhab.binding.mqtt.generic.values.PercentageValue; +import org.openhab.binding.mqtt.generic.values.RollershutterValue; +import org.openhab.binding.mqtt.generic.values.TextValue; +import org.openhab.binding.mqtt.generic.values.Value; /** * Test cases for the value classes. They should throw exceptions if the wrong command type is used diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/internal/handler/ThingChannelConstants.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/internal/handler/ThingChannelConstants.java deleted file mode 100644 index 403631ded395d..0000000000000 --- a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/internal/handler/ThingChannelConstants.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt.internal.handler; - -import static org.openhab.binding.mqtt.internal.MqttBindingConstants.*; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.eclipse.smarthome.config.core.Configuration; -import org.eclipse.smarthome.core.thing.Channel; -import org.eclipse.smarthome.core.thing.ChannelUID; -import org.eclipse.smarthome.core.thing.ThingUID; -import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; -import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; -import org.openhab.binding.mqtt.internal.MqttBindingConstants; - -/** - * Static test definitions, like thing, bridge and channel definitions - * - * @author David Graeff - Initial contribution - */ -public class ThingChannelConstants { - // Common ThingUID and ChannelUIDs - final public static ThingUID testGenericThing = new ThingUID(GENERIC_MQTT_THING, "genericthing"); - - final public static ChannelTypeUID textChannel = new ChannelTypeUID(BINDING_ID, MqttBindingConstants.STRING); - final public static ChannelTypeUID textWithJsonChannel = new ChannelTypeUID(BINDING_ID, - MqttBindingConstants.STRING); - final public static ChannelTypeUID onoffChannel = new ChannelTypeUID(BINDING_ID, MqttBindingConstants.SWITCH); - final public static ChannelTypeUID numberChannel = new ChannelTypeUID(BINDING_ID, MqttBindingConstants.NUMBER); - final public static ChannelTypeUID percentageChannel = new ChannelTypeUID(BINDING_ID, MqttBindingConstants.DIMMER); - final public static ChannelTypeUID unknownChannel = new ChannelTypeUID(BINDING_ID, "unknown"); - - final public static ChannelUID textChannelUID = new ChannelUID(testGenericThing, "mytext"); - - final public static String jsonPathJSON = "{ \"device\": { \"status\": { \"temperature\": 23.2 }}}"; - final public static String jsonPathPattern = "$.device.status.temperature"; - - final public static List thingChannelList = new ArrayList<>(); - final public static List thingChannelListWithJson = new ArrayList<>(); - - /** - * Create a channel with exact the parameters we need for the tests - * - * @param id Channel ID - * @param acceptedType Accept type - * @param config The configuration - * @param channelTypeUID ChannelTypeUID provided by the static definitions - * @return - */ - public static Channel cb(String id, String acceptedType, Configuration config, ChannelTypeUID channelTypeUID) { - return ChannelBuilder.create(new ChannelUID(testGenericThing, id), acceptedType).withConfiguration(config) - .withType(channelTypeUID).build(); - } - - static { - thingChannelList.add(cb("mytext", "TextItemType", textConfiguration(), textChannel)); - thingChannelList.add(cb("onoff", "OnOffType", onoffConfiguration(), onoffChannel)); - thingChannelList.add(cb("num", "NumberType", numberConfiguration(), numberChannel)); - thingChannelList.add(cb("percent", "NumberType", percentageConfiguration(), percentageChannel)); - - thingChannelListWithJson.add(cb("mytext", "TextItemType", textConfigurationWithJson(), textWithJsonChannel)); - thingChannelListWithJson.add(cb("onoff", "OnOffType", onoffConfiguration(), onoffChannel)); - thingChannelListWithJson.add(cb("num", "NumberType", numberConfiguration(), numberChannel)); - thingChannelListWithJson.add(cb("percent", "NumberType", percentageConfiguration(), percentageChannel)); - } - - static Configuration textConfiguration() { - Map data = new HashMap<>(); - data.put("stateTopic", "test/state"); - data.put("commandTopic", "test/command"); - return new Configuration(data); - } - - static Configuration textConfigurationWithJson() { - Map data = new HashMap<>(); - data.put("stateTopic", "test/state"); - data.put("commandTopic", "test/command"); - data.put("transformationPattern", "JSONPATH:" + jsonPathPattern); - return new Configuration(data); - } - - private static Configuration numberConfiguration() { - Map data = new HashMap<>(); - data.put("stateTopic", "test/state"); - data.put("commandTopic", "test/command"); - data.put("min", BigDecimal.valueOf(1)); - data.put("max", BigDecimal.valueOf(99)); - data.put("step", BigDecimal.valueOf(2)); - data.put("isDecimal", true); - return new Configuration(data); - } - - private static Configuration percentageConfiguration() { - Map data = new HashMap<>(); - data.put("stateTopic", "test/state"); - data.put("commandTopic", "test/command"); - data.put("on", "ON"); - data.put("off", "OFF"); - return new Configuration(data); - } - - private static Configuration onoffConfiguration() { - Map data = new HashMap<>(); - data.put("stateTopic", "test/state"); - data.put("commandTopic", "test/command"); - data.put("on", "ON"); - data.put("off", "OFF"); - data.put("inverse", true); - return new Configuration(data); - } - -} diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/generic/internal/MqttBindingConstants.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/generic/internal/MqttBindingConstants.java deleted file mode 100644 index e5d725ab3c61b..0000000000000 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/generic/internal/MqttBindingConstants.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt.generic.internal; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.thing.ThingTypeUID; - -/** - * The {@link MqttBindingConstants} class defines common constants, which are - * used across the whole binding. - * - * @author David Graeff - Initial contribution - */ -@NonNullByDefault -public class MqttBindingConstants { - - public static final String BINDING_ID = "mqtt"; - - // List of all Thing Type UIDs - public static final ThingTypeUID HOMEASSISTANT_MQTT_THING = new ThingTypeUID(BINDING_ID, "homeassistant"); - - public static final String CONFIG_HA_CHANNEL = "mqtt:ha_channel"; -} diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/generic/internal/MqttThingHandlerFactory.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/generic/internal/MqttThingHandlerFactory.java deleted file mode 100644 index 31a4633e5bb07..0000000000000 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/generic/internal/MqttThingHandlerFactory.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt.generic.internal; - -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; -import org.eclipse.smarthome.core.thing.binding.ThingHandler; -import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; -import org.eclipse.smarthome.core.transform.TransformationHelper; -import org.eclipse.smarthome.core.transform.TransformationService; -import org.openhab.binding.mqtt.MqttChannelTypeProvider; -import org.openhab.binding.mqtt.TransformationServiceProvider; -import org.openhab.binding.mqtt.internal.handler.HomeAssistantThingHandler; -import org.osgi.service.component.ComponentContext; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Deactivate; -import org.osgi.service.component.annotations.Reference; - -/** - * The {@link MqttThingHandlerFactory} is responsible for creating things and thing - * handlers. - * - * @author David Graeff - Initial contribution - */ -@Component(service = ThingHandlerFactory.class) -@NonNullByDefault -public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements TransformationServiceProvider { - private @NonNullByDefault({}) MqttChannelTypeProvider typeProvider; - private static final Set SUPPORTED_THING_TYPES_UIDS = Stream - .of(MqttBindingConstants.HOMEASSISTANT_MQTT_THING).collect(Collectors.toSet()); - - @Override - public boolean supportsThingType(ThingTypeUID thingTypeUID) { - return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); - } - - @Activate - @Override - protected void activate(ComponentContext componentContext) { - super.activate(componentContext); - } - - @Deactivate - @Override - protected void deactivate(ComponentContext componentContext) { - super.deactivate(componentContext); - } - - @Reference - protected void setChannelProvider(MqttChannelTypeProvider provider) { - this.typeProvider = provider; - } - - protected void unsetChannelProvider(MqttChannelTypeProvider provider) { - this.typeProvider = null; - } - - @Override - protected @Nullable ThingHandler createHandler(Thing thing) { - ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - - if (thingTypeUID.equals(MqttBindingConstants.HOMEASSISTANT_MQTT_THING)) { - return new HomeAssistantThingHandler(thing, typeProvider, this, 10000, 2000); - } - return null; - } - - @Override - public @Nullable TransformationService getTransformationService(String type) { - return TransformationHelper.getTransformationService(bundleContext, type); - } - -} diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/generic/internal/MqttBindingConstants.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/generic/internal/MqttBindingConstants.java new file mode 100644 index 0000000000000..1ebd042e0c20f --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/generic/internal/MqttBindingConstants.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.homeassistant.generic.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link MqttBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class MqttBindingConstants { + + public static final String BINDING_ID = "mqtt"; + + // List of all Thing Type UIDs + public static final ThingTypeUID HOMEASSISTANT_MQTT_THING = new ThingTypeUID(BINDING_ID, "homeassistant"); + + public static final String CONFIG_HA_CHANNEL = "mqtt:ha_channel"; +} diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/generic/internal/MqttThingHandlerFactory.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/generic/internal/MqttThingHandlerFactory.java new file mode 100644 index 0000000000000..5d4efc943906c --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/generic/internal/MqttThingHandlerFactory.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.homeassistant.generic.internal; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.eclipse.smarthome.core.transform.TransformationHelper; +import org.eclipse.smarthome.core.transform.TransformationService; +import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider; +import org.openhab.binding.mqtt.generic.TransformationServiceProvider; +import org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants; +import org.openhab.binding.mqtt.homeassistant.internal.handler.HomeAssistantThingHandler; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; + +/** + * The {@link MqttThingHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author David Graeff - Initial contribution + */ +@Component(service = ThingHandlerFactory.class) +@NonNullByDefault +public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements TransformationServiceProvider { + private @NonNullByDefault({}) MqttChannelTypeProvider typeProvider; + private static final Set SUPPORTED_THING_TYPES_UIDS = Stream + .of(MqttBindingConstants.HOMEASSISTANT_MQTT_THING).collect(Collectors.toSet()); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Activate + @Override + protected void activate(ComponentContext componentContext) { + super.activate(componentContext); + } + + @Deactivate + @Override + protected void deactivate(ComponentContext componentContext) { + super.deactivate(componentContext); + } + + @Reference + protected void setChannelProvider(MqttChannelTypeProvider provider) { + this.typeProvider = provider; + } + + protected void unsetChannelProvider(MqttChannelTypeProvider provider) { + this.typeProvider = null; + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (thingTypeUID.equals(MqttBindingConstants.HOMEASSISTANT_MQTT_THING)) { + return new HomeAssistantThingHandler(thing, typeProvider, this, 10000, 2000); + } + return null; + } + + @Override + public @Nullable TransformationService getTransformationService(String type) { + return TransformationHelper.getTransformationService(bundleContext, type); + } + +} diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/AbstractComponent.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/AbstractComponent.java similarity index 95% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/AbstractComponent.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/AbstractComponent.java index 78f7287b33e3e..34caf5e9e3a93 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/AbstractComponent.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/AbstractComponent.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import java.util.List; import java.util.Map; @@ -28,10 +28,10 @@ import org.eclipse.smarthome.core.thing.type.ChannelGroupTypeBuilder; import org.eclipse.smarthome.core.thing.type.ChannelGroupTypeUID; import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; -import org.openhab.binding.mqtt.MqttChannelTypeProvider; -import org.openhab.binding.mqtt.generic.internal.MqttBindingConstants; -import org.openhab.binding.mqtt.internal.homeassistant.CFactory.ComponentConfiguration; -import org.openhab.binding.mqtt.values.Value; +import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider; +import org.openhab.binding.mqtt.generic.values.Value; +import org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants; +import org.openhab.binding.mqtt.homeassistant.internal.CFactory.ComponentConfiguration; /** * A HomeAssistant component is comparable to an ESH channel group. diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/BaseChannelConfiguration.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/BaseChannelConfiguration.java similarity index 98% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/BaseChannelConfiguration.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/BaseChannelConfiguration.java index adc133dd9bb8b..5379b24e21eef 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/BaseChannelConfiguration.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/BaseChannelConfiguration.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import java.util.List; import java.util.Map; diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/CChannel.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/CChannel.java similarity index 92% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/CChannel.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/CChannel.java index 974b5e97d6a8c..a4cc5b99a5bac 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/CChannel.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/CChannel.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import java.net.URI; import java.util.concurrent.CompletableFuture; @@ -29,15 +29,15 @@ import org.eclipse.smarthome.core.thing.type.ChannelTypeBuilder; import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; -import org.openhab.binding.mqtt.ChannelConfigBuilder; -import org.openhab.binding.mqtt.ChannelState; -import org.openhab.binding.mqtt.ChannelStateTransformation; -import org.openhab.binding.mqtt.ChannelStateUpdateListener; -import org.openhab.binding.mqtt.MqttChannelTypeProvider; -import org.openhab.binding.mqtt.TransformationServiceProvider; -import org.openhab.binding.mqtt.generic.internal.MqttBindingConstants; -import org.openhab.binding.mqtt.internal.homeassistant.CFactory.ComponentConfiguration; -import org.openhab.binding.mqtt.values.Value; +import org.openhab.binding.mqtt.generic.ChannelConfigBuilder; +import org.openhab.binding.mqtt.generic.ChannelState; +import org.openhab.binding.mqtt.generic.ChannelStateTransformation; +import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener; +import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider; +import org.openhab.binding.mqtt.generic.TransformationServiceProvider; +import org.openhab.binding.mqtt.generic.values.Value; +import org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants; +import org.openhab.binding.mqtt.homeassistant.internal.CFactory.ComponentConfiguration; /** * An {@link AbstractComponent}s derived class consists of one or multiple channels. diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/CFactory.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/CFactory.java similarity index 96% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/CFactory.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/CFactory.java index 1c334efa98f03..de1b2a96034aa 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/CFactory.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/CFactory.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.thing.ThingUID; -import org.openhab.binding.mqtt.ChannelStateUpdateListener; -import org.openhab.binding.mqtt.TransformationServiceProvider; +import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener; +import org.openhab.binding.mqtt.generic.TransformationServiceProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ChannelConfigurationTypeAdapterFactory.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ChannelConfigurationTypeAdapterFactory.java similarity index 98% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ChannelConfigurationTypeAdapterFactory.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ChannelConfigurationTypeAdapterFactory.java index 019ee895edf8c..13446b2868340 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ChannelConfigurationTypeAdapterFactory.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ChannelConfigurationTypeAdapterFactory.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import java.io.IOException; import java.lang.reflect.Field; diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentAlarmControlPanel.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentAlarmControlPanel.java similarity index 97% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentAlarmControlPanel.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentAlarmControlPanel.java index 6634c4c1b8e16..cd58d3daa59a1 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentAlarmControlPanel.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentAlarmControlPanel.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.mqtt.values.TextValue; +import org.openhab.binding.mqtt.generic.values.TextValue; /** * A MQTT alarm control panel, following the https://www.home-assistant.io/components/alarm_control_panel.mqtt/ diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentBinarySensor.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentBinarySensor.java similarity index 94% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentBinarySensor.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentBinarySensor.java index e63aca3ad48f2..216c6f17cad58 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentBinarySensor.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentBinarySensor.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.mqtt.values.OnOffValue; +import org.openhab.binding.mqtt.generic.values.OnOffValue; /** * A MQTT BinarySensor, following the https://www.home-assistant.io/components/binary_sensor.mqtt/ specification. diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentCamera.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentCamera.java similarity index 93% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentCamera.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentCamera.java index e30fe22a1c7bb..72dd4b5223081 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentCamera.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentCamera.java @@ -10,10 +10,10 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.mqtt.values.ImageValue; +import org.openhab.binding.mqtt.generic.values.ImageValue; /** * A MQTT camera, following the https://www.home-assistant.io/components/camera.mqtt/ specification. diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentClimate.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentClimate.java similarity index 95% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentClimate.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentClimate.java index ba794bca2d615..e761caa95a291 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentClimate.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentClimate.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentCover.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentCover.java similarity index 94% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentCover.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentCover.java index 0e917a618219c..807c2126908b7 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentCover.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentCover.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.mqtt.values.RollershutterValue; +import org.openhab.binding.mqtt.generic.values.RollershutterValue; /** * A MQTT Cover component, following the https://www.home-assistant.io/components/cover.mqtt/ specification. diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentFan.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentFan.java similarity index 94% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentFan.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentFan.java index b7a2097d7f47c..bbd5d9b431606 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentFan.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentFan.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.mqtt.values.OnOffValue; +import org.openhab.binding.mqtt.generic.values.OnOffValue; /** * A MQTT Fan component, following the https://www.home-assistant.io/components/fan.mqtt/ specification. diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentLight.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentLight.java similarity index 97% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentLight.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentLight.java index 04e84eb2fc494..f3ce3ebae0efb 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentLight.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentLight.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -23,8 +23,8 @@ import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; -import org.openhab.binding.mqtt.ChannelStateUpdateListener; -import org.openhab.binding.mqtt.values.ColorValue; +import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener; +import org.openhab.binding.mqtt.generic.values.ColorValue; /** * A MQTT light, following the https://www.home-assistant.io/components/light.mqtt/ specification. diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentLock.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentLock.java similarity index 95% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentLock.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentLock.java index 19c40a6a7d192..b6127c1c43d56 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentLock.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentLock.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.mqtt.values.OnOffValue; +import org.openhab.binding.mqtt.generic.values.OnOffValue; /** * A MQTT lock, following the https://www.home-assistant.io/components/lock.mqtt/ specification. diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentSensor.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSensor.java similarity index 94% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentSensor.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSensor.java index fc4445f3c3f23..f0343555f8a88 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentSensor.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSensor.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.mqtt.values.TextValue; +import org.openhab.binding.mqtt.generic.values.TextValue; /** * A MQTT sensor, following the https://www.home-assistant.io/components/sensor.mqtt/ specification. diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentSwitch.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSwitch.java similarity index 95% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentSwitch.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSwitch.java index 386159208195c..799aca0b91d49 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/ComponentSwitch.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/ComponentSwitch.java @@ -10,12 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.mqtt.values.OnOffValue; +import org.openhab.binding.mqtt.generic.values.OnOffValue; /** * A MQTT switch, following the https://www.home-assistant.io/components/switch.mqtt/ specification. diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/DiscoverComponents.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/DiscoverComponents.java similarity index 97% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/DiscoverComponents.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/DiscoverComponents.java index 11ab1e551e9a6..08f839f4e432f 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/DiscoverComponents.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/DiscoverComponents.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import java.lang.ref.WeakReference; import java.util.concurrent.CompletableFuture; @@ -23,8 +23,8 @@ import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; import org.eclipse.smarthome.io.transport.mqtt.MqttMessageSubscriber; -import org.openhab.binding.mqtt.ChannelStateUpdateListener; -import org.openhab.binding.mqtt.TransformationServiceProvider; +import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener; +import org.openhab.binding.mqtt.generic.TransformationServiceProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/HaID.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HaID.java similarity index 99% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/HaID.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HaID.java index 1499cc173620a..73cd506562837 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/HaID.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HaID.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HandlerConfiguration.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HandlerConfiguration.java new file mode 100644 index 0000000000000..5a5afcec200bd --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/HandlerConfiguration.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.homeassistant.internal; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.mqtt.homeassistant.internal.handler.HomeAssistantThingHandler; + +/** + * The {@link HomeAssistantThingHandler} manages Things that are responsible for + * HomeAssistant MQTT components. + * This class contains the necessary configuration for such a Thing handler. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HandlerConfiguration { + /** + * hint: cannot be final, or getConfigAs will not work. + * The MQTT prefix topic + */ + public String basetopic; + /** + * hint: cannot be final, or getConfigAs will not work. + * The object id. This is comparable to a Homie Device. + */ + public String objectid; + + public HandlerConfiguration() { + this("homeassistant", ""); + } + + public HandlerConfiguration(String basetopic, String objectid) { + super(); + this.basetopic = basetopic; + this.objectid = objectid; + } + + /** + * Add the basetopic and objectid to the properties. + * + * @param properties + * @return the modified properties + */ + public > T appendToProperties(T properties) { + properties.put("basetopic", basetopic); + properties.put("objectid", objectid); + return properties; + } +} diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/MappingJsonReader.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/MappingJsonReader.java similarity index 98% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/MappingJsonReader.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/MappingJsonReader.java index 26020b0722cdb..c2b2dd2d3c28d 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/MappingJsonReader.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/MappingJsonReader.java @@ -10,14 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.mqtt.tools.JsonReaderDelegate; +import org.openhab.binding.mqtt.generic.tools.JsonReaderDelegate; import com.google.gson.stream.JsonReader; diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/discovery/HomeAssistantDiscovery.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/discovery/HomeAssistantDiscovery.java similarity index 95% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/discovery/HomeAssistantDiscovery.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/discovery/HomeAssistantDiscovery.java index 959c54a3fdd55..9b13c874f139b 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/discovery/HomeAssistantDiscovery.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/discovery/HomeAssistantDiscovery.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.discovery; +package org.openhab.binding.mqtt.homeassistant.internal.discovery; import java.nio.charset.StandardCharsets; import java.util.HashMap; @@ -31,11 +31,11 @@ import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; import org.openhab.binding.mqtt.discovery.AbstractMQTTDiscovery; import org.openhab.binding.mqtt.discovery.MQTTTopicDiscoveryService; -import org.openhab.binding.mqtt.generic.internal.MqttBindingConstants; -import org.openhab.binding.mqtt.internal.homeassistant.BaseChannelConfiguration; -import org.openhab.binding.mqtt.internal.homeassistant.ChannelConfigurationTypeAdapterFactory; -import org.openhab.binding.mqtt.internal.homeassistant.HaID; -import org.openhab.binding.mqtt.internal.homeassistant.HandlerConfiguration; +import org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants; +import org.openhab.binding.mqtt.homeassistant.internal.BaseChannelConfiguration; +import org.openhab.binding.mqtt.homeassistant.internal.ChannelConfigurationTypeAdapterFactory; +import org.openhab.binding.mqtt.homeassistant.internal.HaID; +import org.openhab.binding.mqtt.homeassistant.internal.HandlerConfiguration; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/handler/HomeAssistantThingHandler.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/handler/HomeAssistantThingHandler.java similarity index 92% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/handler/HomeAssistantThingHandler.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/handler/HomeAssistantThingHandler.java index eea7be5c49726..baa7dff5394df 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/handler/HomeAssistantThingHandler.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/handler/HomeAssistantThingHandler.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.handler; +package org.openhab.binding.mqtt.homeassistant.internal.handler; import java.util.ArrayList; import java.util.HashMap; @@ -32,19 +32,19 @@ import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; -import org.openhab.binding.mqtt.AbstractMQTTThingHandler; -import org.openhab.binding.mqtt.ChannelState; -import org.openhab.binding.mqtt.MqttChannelTypeProvider; -import org.openhab.binding.mqtt.TransformationServiceProvider; -import org.openhab.binding.mqtt.internal.homeassistant.AbstractComponent; -import org.openhab.binding.mqtt.internal.homeassistant.CChannel; -import org.openhab.binding.mqtt.internal.homeassistant.CFactory; -import org.openhab.binding.mqtt.internal.homeassistant.ChannelConfigurationTypeAdapterFactory; -import org.openhab.binding.mqtt.internal.homeassistant.DiscoverComponents; -import org.openhab.binding.mqtt.internal.homeassistant.DiscoverComponents.ComponentDiscovered; -import org.openhab.binding.mqtt.internal.homeassistant.HaID; -import org.openhab.binding.mqtt.internal.homeassistant.HandlerConfiguration; -import org.openhab.binding.mqtt.tools.DelayedBatchProcessing; +import org.openhab.binding.mqtt.generic.AbstractMQTTThingHandler; +import org.openhab.binding.mqtt.generic.ChannelState; +import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider; +import org.openhab.binding.mqtt.generic.TransformationServiceProvider; +import org.openhab.binding.mqtt.generic.tools.DelayedBatchProcessing; +import org.openhab.binding.mqtt.homeassistant.internal.AbstractComponent; +import org.openhab.binding.mqtt.homeassistant.internal.CChannel; +import org.openhab.binding.mqtt.homeassistant.internal.CFactory; +import org.openhab.binding.mqtt.homeassistant.internal.ChannelConfigurationTypeAdapterFactory; +import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents; +import org.openhab.binding.mqtt.homeassistant.internal.HaID; +import org.openhab.binding.mqtt.homeassistant.internal.HandlerConfiguration; +import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents.ComponentDiscovered; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/HandlerConfiguration.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/HandlerConfiguration.java deleted file mode 100644 index 19997a1aa6fc2..0000000000000 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/internal/homeassistant/HandlerConfiguration.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt.internal.homeassistant; - -import java.util.Map; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.mqtt.internal.handler.HomeAssistantThingHandler; - -/** - * The {@link HomeAssistantThingHandler} manages Things that are responsible for - * HomeAssistant MQTT components. - * This class contains the necessary configuration for such a Thing handler. - * - * @author David Graeff - Initial contribution - */ -@NonNullByDefault -public class HandlerConfiguration { - /** - * hint: cannot be final, or getConfigAs will not work. - * The MQTT prefix topic - */ - public String basetopic; - /** - * hint: cannot be final, or getConfigAs will not work. - * The object id. This is comparable to a Homie Device. - */ - public String objectid; - - public HandlerConfiguration() { - this("homeassistant", ""); - } - - public HandlerConfiguration(String basetopic, String objectid) { - super(); - this.basetopic = basetopic; - this.objectid = objectid; - } - - /** - * Add the basetopic and objectid to the properties. - * - * @param properties - * @return the modified properties - */ - public > T appendToProperties(T properties) { - properties.put("basetopic", basetopic); - properties.put("objectid", objectid); - return properties; - } -} diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/internal/homeassistant/HAConfigurationTests.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/HAConfigurationTests.java similarity index 95% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/internal/homeassistant/HAConfigurationTests.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/HAConfigurationTests.java index d80d4c16fc95b..be2c9bb2d213b 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/internal/homeassistant/HAConfigurationTests.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/HAConfigurationTests.java @@ -10,17 +10,17 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.junit.Assert.assertThat; import org.junit.Test; -import org.openhab.binding.mqtt.internal.homeassistant.BaseChannelConfiguration; -import org.openhab.binding.mqtt.internal.homeassistant.ChannelConfigurationTypeAdapterFactory; -import org.openhab.binding.mqtt.internal.homeassistant.ComponentFan; -import org.openhab.binding.mqtt.internal.homeassistant.ComponentSwitch; +import org.openhab.binding.mqtt.homeassistant.internal.BaseChannelConfiguration; +import org.openhab.binding.mqtt.homeassistant.internal.ChannelConfigurationTypeAdapterFactory; +import org.openhab.binding.mqtt.homeassistant.internal.ComponentFan; +import org.openhab.binding.mqtt.homeassistant.internal.ComponentSwitch; import com.google.gson.Gson; import com.google.gson.GsonBuilder; diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/internal/homeassistant/HaIDTests.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/HaIDTests.java similarity index 91% rename from bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/internal/homeassistant/HaIDTests.java rename to bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/HaIDTests.java index fd41b8499fb9e..d3079b5f4147a 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/internal/homeassistant/HaIDTests.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/HaIDTests.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homeassistant; +package org.openhab.binding.mqtt.homeassistant.internal; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import org.eclipse.smarthome.config.core.Configuration; import org.junit.Test; -import org.openhab.binding.mqtt.internal.homeassistant.HaID; -import org.openhab.binding.mqtt.internal.homeassistant.HandlerConfiguration; +import org.openhab.binding.mqtt.homeassistant.internal.HaID; +import org.openhab.binding.mqtt.homeassistant.internal.HandlerConfiguration; public class HaIDTests { diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/handler/ThingChannelConstants.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/handler/ThingChannelConstants.java new file mode 100644 index 0000000000000..98046b91f6e99 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/handler/ThingChannelConstants.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.homeassistant.internal.handler; + +import static org.openhab.binding.mqtt.homeassistant.generic.internal.MqttBindingConstants.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; + +/** + * Static test definitions, like thing, bridge and channel definitions + * + * @author David Graeff - Initial contribution + */ +public class ThingChannelConstants { + // Common ThingUID and ChannelUIDs + final public static ThingUID testHomeAssistantThing = new ThingUID(HOMEASSISTANT_MQTT_THING, "device234"); + + final public static ChannelTypeUID unknownChannel = new ChannelTypeUID(BINDING_ID, "unknown"); + + final public static String jsonPathJSON = "{ \"device\": { \"status\": { \"temperature\": 23.2 }}}"; + final public static String jsonPathPattern = "$.device.status.temperature"; + + final public static List thingChannelList = new ArrayList<>(); + final public static List thingChannelListWithJson = new ArrayList<>(); + + static Configuration textConfiguration() { + Map data = new HashMap<>(); + data.put("stateTopic", "test/state"); + data.put("commandTopic", "test/command"); + return new Configuration(data); + } + + static Configuration textConfigurationWithJson() { + Map data = new HashMap<>(); + data.put("stateTopic", "test/state"); + data.put("commandTopic", "test/command"); + data.put("transformationPattern", "JSONPATH:" + jsonPathPattern); + return new Configuration(data); + } + +} diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/internal/handler/ThingChannelConstants.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/internal/handler/ThingChannelConstants.java deleted file mode 100644 index c5b7d8ae04f08..0000000000000 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/internal/handler/ThingChannelConstants.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt.internal.handler; - -import static org.openhab.binding.mqtt.generic.internal.MqttBindingConstants.*; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.eclipse.smarthome.config.core.Configuration; -import org.eclipse.smarthome.core.thing.Channel; -import org.eclipse.smarthome.core.thing.ThingUID; -import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; - -/** - * Static test definitions, like thing, bridge and channel definitions - * - * @author David Graeff - Initial contribution - */ -public class ThingChannelConstants { - // Common ThingUID and ChannelUIDs - final public static ThingUID testHomeAssistantThing = new ThingUID(HOMEASSISTANT_MQTT_THING, "device234"); - - final public static ChannelTypeUID unknownChannel = new ChannelTypeUID(BINDING_ID, "unknown"); - - final public static String jsonPathJSON = "{ \"device\": { \"status\": { \"temperature\": 23.2 }}}"; - final public static String jsonPathPattern = "$.device.status.temperature"; - - final public static List thingChannelList = new ArrayList<>(); - final public static List thingChannelListWithJson = new ArrayList<>(); - - static Configuration textConfiguration() { - Map data = new HashMap<>(); - data.put("stateTopic", "test/state"); - data.put("commandTopic", "test/command"); - return new Configuration(data); - } - - static Configuration textConfigurationWithJson() { - Map data = new HashMap<>(); - data.put("stateTopic", "test/state"); - data.put("commandTopic", "test/command"); - data.put("transformationPattern", "JSONPATH:" + jsonPathPattern); - return new Configuration(data); - } - -} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/generic/internal/MqttBindingConstants.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/generic/internal/MqttBindingConstants.java deleted file mode 100644 index f5bd41962bafc..0000000000000 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/generic/internal/MqttBindingConstants.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt.generic.internal; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.core.thing.ThingTypeUID; - -/** - * The {@link MqttBindingConstants} class defines common constants, which are - * used across the whole binding. - * - * @author David Graeff - Initial contribution - */ -@NonNullByDefault -public class MqttBindingConstants { - - public static final String BINDING_ID = "mqtt"; - - // List of all Thing Type UIDs - public static final ThingTypeUID HOMIE300_MQTT_THING = new ThingTypeUID(BINDING_ID, "homie300"); - - public static final String CONFIG_HOMIE_CHANNEL = "mqtt:homie_channel"; - - public static final String HOMIE_PROPERTY_VERSION = "homieversion"; - public static final String HOMIE_PROPERTY_HEARTBEAT_INTERVAL = "heartbeat_interval"; -} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/generic/internal/MqttThingHandlerFactory.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/generic/internal/MqttThingHandlerFactory.java deleted file mode 100644 index 950d5bbfc16fb..0000000000000 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/generic/internal/MqttThingHandlerFactory.java +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt.generic.internal; - -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.thing.Thing; -import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; -import org.eclipse.smarthome.core.thing.binding.ThingHandler; -import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; -import org.eclipse.smarthome.core.transform.TransformationHelper; -import org.eclipse.smarthome.core.transform.TransformationService; -import org.openhab.binding.mqtt.MqttChannelStateDescriptionProvider; -import org.openhab.binding.mqtt.MqttChannelTypeProvider; -import org.openhab.binding.mqtt.TransformationServiceProvider; -import org.openhab.binding.mqtt.internal.handler.HomieThingHandler; -import org.osgi.service.component.ComponentContext; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Deactivate; -import org.osgi.service.component.annotations.Reference; - -/** - * The {@link MqttThingHandlerFactory} is responsible for creating things and thing - * handlers. - * - * @author David Graeff - Initial contribution - */ -@Component(service = ThingHandlerFactory.class) -@NonNullByDefault -public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements TransformationServiceProvider { - private @NonNullByDefault({}) MqttChannelTypeProvider typeProvider; - private @NonNullByDefault({}) MqttChannelStateDescriptionProvider stateDescriptionProvider; - private static final Set SUPPORTED_THING_TYPES_UIDS = Stream - .of(MqttBindingConstants.HOMIE300_MQTT_THING).collect(Collectors.toSet()); - - @Override - public boolean supportsThingType(ThingTypeUID thingTypeUID) { - return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); - } - - @Activate - @Override - protected void activate(ComponentContext componentContext) { - super.activate(componentContext); - } - - @Deactivate - @Override - protected void deactivate(ComponentContext componentContext) { - super.deactivate(componentContext); - } - - @Reference - protected void setStateDescriptionProvider(MqttChannelStateDescriptionProvider stateDescription) { - this.stateDescriptionProvider = stateDescription; - } - - protected void unsetStateDescriptionProvider(MqttChannelStateDescriptionProvider stateDescription) { - this.stateDescriptionProvider = null; - } - - @Reference - protected void setChannelProvider(MqttChannelTypeProvider provider) { - this.typeProvider = provider; - } - - protected void unsetChannelProvider(MqttChannelTypeProvider provider) { - this.typeProvider = null; - } - - @Override - protected @Nullable ThingHandler createHandler(Thing thing) { - ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - - if (thingTypeUID.equals(MqttBindingConstants.HOMIE300_MQTT_THING)) { - return new HomieThingHandler(thing, typeProvider, 15000, 2000); - } - return null; - } - - @Override - public @Nullable TransformationService getTransformationService(String type) { - return TransformationHelper.getTransformationService(bundleContext, type); - } - -} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/generic/internal/MqttBindingConstants.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/generic/internal/MqttBindingConstants.java new file mode 100644 index 0000000000000..56a1f40735ca2 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/generic/internal/MqttBindingConstants.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.homie.generic.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link MqttBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class MqttBindingConstants { + + public static final String BINDING_ID = "mqtt"; + + // List of all Thing Type UIDs + public static final ThingTypeUID HOMIE300_MQTT_THING = new ThingTypeUID(BINDING_ID, "homie300"); + + public static final String CONFIG_HOMIE_CHANNEL = "mqtt:homie_channel"; + + public static final String HOMIE_PROPERTY_VERSION = "homieversion"; + public static final String HOMIE_PROPERTY_HEARTBEAT_INTERVAL = "heartbeat_interval"; +} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/generic/internal/MqttThingHandlerFactory.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/generic/internal/MqttThingHandlerFactory.java new file mode 100644 index 0000000000000..0a694c877a0e4 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/generic/internal/MqttThingHandlerFactory.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.homie.generic.internal; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.eclipse.smarthome.core.transform.TransformationHelper; +import org.eclipse.smarthome.core.transform.TransformationService; +import org.openhab.binding.mqtt.generic.MqttChannelStateDescriptionProvider; +import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider; +import org.openhab.binding.mqtt.generic.TransformationServiceProvider; +import org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants; +import org.openhab.binding.mqtt.homie.internal.handler.HomieThingHandler; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; + +/** + * The {@link MqttThingHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author David Graeff - Initial contribution + */ +@Component(service = ThingHandlerFactory.class) +@NonNullByDefault +public class MqttThingHandlerFactory extends BaseThingHandlerFactory implements TransformationServiceProvider { + private @NonNullByDefault({}) MqttChannelTypeProvider typeProvider; + private @NonNullByDefault({}) MqttChannelStateDescriptionProvider stateDescriptionProvider; + private static final Set SUPPORTED_THING_TYPES_UIDS = Stream + .of(MqttBindingConstants.HOMIE300_MQTT_THING).collect(Collectors.toSet()); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Activate + @Override + protected void activate(ComponentContext componentContext) { + super.activate(componentContext); + } + + @Deactivate + @Override + protected void deactivate(ComponentContext componentContext) { + super.deactivate(componentContext); + } + + @Reference + protected void setStateDescriptionProvider(MqttChannelStateDescriptionProvider stateDescription) { + this.stateDescriptionProvider = stateDescription; + } + + protected void unsetStateDescriptionProvider(MqttChannelStateDescriptionProvider stateDescription) { + this.stateDescriptionProvider = null; + } + + @Reference + protected void setChannelProvider(MqttChannelTypeProvider provider) { + this.typeProvider = provider; + } + + protected void unsetChannelProvider(MqttChannelTypeProvider provider) { + this.typeProvider = null; + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (thingTypeUID.equals(MqttBindingConstants.HOMIE300_MQTT_THING)) { + return new HomieThingHandler(thing, typeProvider, 15000, 2000); + } + return null; + } + + @Override + public @Nullable TransformationService getTransformationService(String type) { + return TransformationHelper.getTransformationService(bundleContext, type); + } + +} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/discovery/Homie300Discovery.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/discovery/Homie300Discovery.java similarity index 96% rename from bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/discovery/Homie300Discovery.java rename to bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/discovery/Homie300Discovery.java index 12636e180179c..1c9e542871a37 100644 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/discovery/Homie300Discovery.java +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/discovery/Homie300Discovery.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.discovery; +package org.openhab.binding.mqtt.homie.internal.discovery; import java.nio.charset.StandardCharsets; import java.util.HashMap; @@ -27,8 +27,8 @@ import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; import org.openhab.binding.mqtt.discovery.AbstractMQTTDiscovery; import org.openhab.binding.mqtt.discovery.MQTTTopicDiscoveryService; -import org.openhab.binding.mqtt.generic.internal.MqttBindingConstants; -import org.openhab.binding.mqtt.tools.WaitForTopicValue; +import org.openhab.binding.mqtt.generic.tools.WaitForTopicValue; +import org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/handler/HomieThingHandler.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/handler/HomieThingHandler.java similarity index 85% rename from bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/handler/HomieThingHandler.java rename to bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/handler/HomieThingHandler.java index a919928976260..8ee2c76704662 100644 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/handler/HomieThingHandler.java +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/handler/HomieThingHandler.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.handler; +package org.openhab.binding.mqtt.homie.internal.handler; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -26,18 +26,18 @@ import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; -import org.openhab.binding.mqtt.AbstractMQTTThingHandler; -import org.openhab.binding.mqtt.ChannelState; -import org.openhab.binding.mqtt.MqttChannelTypeProvider; -import org.openhab.binding.mqtt.generic.internal.MqttBindingConstants; -import org.openhab.binding.mqtt.internal.homie300.Device; -import org.openhab.binding.mqtt.internal.homie300.DeviceAttributes; -import org.openhab.binding.mqtt.internal.homie300.DeviceAttributes.ReadyState; -import org.openhab.binding.mqtt.internal.homie300.DeviceCallback; -import org.openhab.binding.mqtt.internal.homie300.HandlerConfiguration; -import org.openhab.binding.mqtt.internal.homie300.Node; -import org.openhab.binding.mqtt.internal.homie300.Property; -import org.openhab.binding.mqtt.tools.DelayedBatchProcessing; +import org.openhab.binding.mqtt.generic.AbstractMQTTThingHandler; +import org.openhab.binding.mqtt.generic.ChannelState; +import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider; +import org.openhab.binding.mqtt.generic.tools.DelayedBatchProcessing; +import org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants; +import org.openhab.binding.mqtt.homie.internal.homie300.Device; +import org.openhab.binding.mqtt.homie.internal.homie300.DeviceAttributes; +import org.openhab.binding.mqtt.homie.internal.homie300.DeviceCallback; +import org.openhab.binding.mqtt.homie.internal.homie300.HandlerConfiguration; +import org.openhab.binding.mqtt.homie.internal.homie300.Node; +import org.openhab.binding.mqtt.homie.internal.homie300.Property; +import org.openhab.binding.mqtt.homie.internal.homie300.DeviceAttributes.ReadyState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -104,6 +104,14 @@ public void initialize() { super.initialize(); } + @Override + public void handleRemoval() { + this.stop(); + if(config.removetopics) + this.removeRetainedTopics(); + super.handleRemoval(); + } + @Override protected CompletableFuture<@Nullable Void> start(MqttBrokerConnection connection) { logger.debug("About to start Homie device {}", device.attributes.name); @@ -208,4 +216,12 @@ public void accept(@Nullable List t) { }); } } + /** + * Removes all retained topics related to the device + */ + private void removeRetainedTopics() { + device.getRetainedTopics().stream().map( + d -> {return String.format("%s/%s", config.basetopic, d);}).collect(Collectors.toList()).forEach( + t -> this.connection.publish(t, new byte[0], 1, true)); + } } diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/Device.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/Device.java similarity index 91% rename from bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/Device.java rename to bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/Device.java index a166933b1d861..1c3395b52109f 100644 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/Device.java +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/Device.java @@ -10,11 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homie300; +package org.openhab.binding.mqtt.homie.internal.homie300; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledExecutorService; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -22,9 +25,9 @@ import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; -import org.openhab.binding.mqtt.internal.handler.HomieThingHandler; -import org.openhab.binding.mqtt.mapping.AbstractMqttAttributeClass; -import org.openhab.binding.mqtt.tools.ChildMap; +import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass; +import org.openhab.binding.mqtt.generic.tools.ChildMap; +import org.openhab.binding.mqtt.homie.internal.handler.HomieThingHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -208,7 +211,7 @@ public void initialize(String baseTopic, String deviceID, List channels // Restores the properties attribute object via the channels configuration. Property property = node.createProperty(propertyID, channel.getConfiguration().as(PropertyAttributes.class)); - property.createChannelFromAttribute(); + property.attributesReceived(); node.properties.put(propertyID, property); } @@ -289,4 +292,21 @@ public void attributeChanged(String name, Object value, MqttBrokerConnection con } } } + + /** + * Creates a list of retained topics related to the device + * @return Returns a list of relative topics + */ + public ArrayList getRetainedTopics() { + ArrayList topics = new ArrayList(); + + topics.addAll(Stream.of(this.attributes.getClass().getDeclaredFields()).map( + f -> {return String.format("%s/$%s", this.deviceID, f.getName());}).collect(Collectors.toList())); + + this.nodes.stream().map( + n -> n.getRetainedTopics().stream().map( + a -> {return String.format("%s/%s", this.deviceID, a);}).collect(Collectors.toList())).collect(Collectors.toList()).forEach(topics::addAll); + + return topics; + } } diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/DeviceAttributes.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/DeviceAttributes.java similarity index 78% rename from bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/DeviceAttributes.java rename to bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/DeviceAttributes.java index 90eae5d1488fd..9ec0e38d24344 100644 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/DeviceAttributes.java +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/DeviceAttributes.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homie300; +package org.openhab.binding.mqtt.homie.internal.homie300; import org.eclipse.jdt.annotation.NonNull; -import org.openhab.binding.mqtt.mapping.AbstractMqttAttributeClass; -import org.openhab.binding.mqtt.mapping.MQTTvalueTransform; -import org.openhab.binding.mqtt.mapping.MandatoryField; -import org.openhab.binding.mqtt.mapping.TopicPrefix; +import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass; +import org.openhab.binding.mqtt.generic.mapping.MQTTvalueTransform; +import org.openhab.binding.mqtt.generic.mapping.MandatoryField; +import org.openhab.binding.mqtt.generic.mapping.TopicPrefix; /** * Homie 3.x Device attributes diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/DeviceCallback.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/DeviceCallback.java similarity index 88% rename from bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/DeviceCallback.java rename to bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/DeviceCallback.java index 0758d086401fd..754121d07c3c4 100644 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/DeviceCallback.java +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/DeviceCallback.java @@ -10,11 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homie300; +package org.openhab.binding.mqtt.homie.internal.homie300; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.mqtt.ChannelStateUpdateListener; -import org.openhab.binding.mqtt.internal.homie300.DeviceAttributes.ReadyState; +import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener; +import org.openhab.binding.mqtt.homie.internal.homie300.DeviceAttributes.ReadyState; /** * Callbacks to inform about the Homie Device state, statistics changes, node layout changes. diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/HandlerConfiguration.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/HandlerConfiguration.java new file mode 100644 index 0000000000000..c6db62072260f --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/HandlerConfiguration.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.homie.internal.homie300; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.mqtt.homie.internal.handler.HomieThingHandler; + +/** + * The {@link HomieThingHandler} manages Things that are responsible for + * Homie MQTT devices. + * This class contains the necessary configuration for such a Thing handler. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HandlerConfiguration { + /** + * The MQTT prefix topic + */ + public String basetopic = "homie"; + /** + * The device id. + */ + public String deviceid = ""; + /** + * Indicates if retained topics should be removed when the Thing is deleted. + */ + public boolean removetopics = false; +} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/Node.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/Node.java similarity index 88% rename from bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/Node.java rename to bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/Node.java index f74039e7a19fb..e54847e9370d5 100644 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/Node.java +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/Node.java @@ -10,12 +10,14 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homie300; +package org.openhab.binding.mqtt.homie.internal.homie300; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledExecutorService; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -27,9 +29,9 @@ import org.eclipse.smarthome.core.thing.type.ChannelGroupTypeBuilder; import org.eclipse.smarthome.core.thing.type.ChannelGroupTypeUID; import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; -import org.openhab.binding.mqtt.generic.internal.MqttBindingConstants; -import org.openhab.binding.mqtt.mapping.AbstractMqttAttributeClass; -import org.openhab.binding.mqtt.tools.ChildMap; +import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass; +import org.openhab.binding.mqtt.generic.tools.ChildMap; +import org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -200,4 +202,21 @@ public void attributeChanged(String name, Object value, MqttBrokerConnection con public String toString() { return channelGroupUID.toString(); } + + /** + * Creates a list of retained topics related to the node + * @return Returns a list of relative topics + */ + public ArrayList getRetainedTopics() { + ArrayList topics = new ArrayList(); + + topics.addAll(Stream.of(this.attributes.getClass().getDeclaredFields()).map( + f -> {return String.format("%s/$%s", this.nodeID, f.getName());}).collect(Collectors.toList())); + + this.properties.stream().map( + p -> p.getRetainedTopics().stream().map( + a -> {return String.format("%s/%s", this.nodeID, a);}).collect(Collectors.toList())).collect(Collectors.toList()).forEach(topics::addAll); + + return topics; + } } diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/NodeAttributes.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/NodeAttributes.java new file mode 100644 index 0000000000000..573d206ce5f73 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/NodeAttributes.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.homie.internal.homie300; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass; +import org.openhab.binding.mqtt.generic.mapping.MQTTvalueTransform; +import org.openhab.binding.mqtt.generic.mapping.MandatoryField; +import org.openhab.binding.mqtt.generic.mapping.TopicPrefix; + +/** + * Homie 3.x Node attributes + * + * @author David Graeff - Initial contribution + */ +@TopicPrefix +public class NodeAttributes extends AbstractMqttAttributeClass { + public @MandatoryField String name; + public @MandatoryField @MQTTvalueTransform(splitCharacter = ",") String[] properties; + // Type has no meaning for ESH yet and is currently purely of textual, descriptive nature + public String type; + + @Override + public @NonNull Object getFieldsOf() { + return this; + } +} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/Property.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/Property.java similarity index 86% rename from bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/Property.java rename to bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/Property.java index 5cb2b5aa46763..d3b8d44501aa5 100644 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/Property.java +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/Property.java @@ -10,13 +10,16 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homie300; +package org.openhab.binding.mqtt.homie.internal.homie300; import java.math.BigDecimal; import java.math.MathContext; import java.net.URI; +import java.util.ArrayList; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledExecutorService; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -29,17 +32,17 @@ import org.eclipse.smarthome.core.thing.type.ChannelTypeBuilder; import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; -import org.openhab.binding.mqtt.ChannelConfigBuilder; -import org.openhab.binding.mqtt.ChannelState; -import org.openhab.binding.mqtt.generic.internal.MqttBindingConstants; -import org.openhab.binding.mqtt.internal.homie300.PropertyAttributes.DataTypeEnum; -import org.openhab.binding.mqtt.mapping.AbstractMqttAttributeClass; -import org.openhab.binding.mqtt.mapping.AbstractMqttAttributeClass.AttributeChanged; -import org.openhab.binding.mqtt.values.ColorValue; -import org.openhab.binding.mqtt.values.NumberValue; -import org.openhab.binding.mqtt.values.OnOffValue; -import org.openhab.binding.mqtt.values.TextValue; -import org.openhab.binding.mqtt.values.Value; +import org.openhab.binding.mqtt.generic.ChannelConfigBuilder; +import org.openhab.binding.mqtt.generic.ChannelState; +import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass; +import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass.AttributeChanged; +import org.openhab.binding.mqtt.generic.values.ColorValue; +import org.openhab.binding.mqtt.generic.values.NumberValue; +import org.openhab.binding.mqtt.generic.values.OnOffValue; +import org.openhab.binding.mqtt.generic.values.TextValue; +import org.openhab.binding.mqtt.generic.values.Value; +import org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants; +import org.openhab.binding.mqtt.homie.internal.homie300.PropertyAttributes.DataTypeEnum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -297,4 +300,28 @@ public void attributeChanged(String name, Object value, MqttBrokerConnection con } attributesReceived(); } + + /** + * Creates a list of retained topics related to the property + * @return Returns a list of relative topics + */ + public ArrayList getRetainedTopics() { + ArrayList topics = new ArrayList(); + + topics.addAll(Stream.of(this.attributes.getClass().getDeclaredFields()).map( + f -> {return String.format("%s/$%s", this.propertyID, f.getName());}).collect(Collectors.toList())); + + // All exceptions can be ignored because the 'retained' attribute of the PropertyAttributes class + // is public, is a boolean variable and has a default value (true) + try { + if(attributes.getClass().getDeclaredField("retained").getBoolean(attributes)) + topics.add(this.propertyID); + } catch (NoSuchFieldException ignored) { + } catch (SecurityException ignored) { + } catch (IllegalArgumentException ignored) { + } catch (IllegalAccessException ignored) { + } + + return topics; + } } diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/PropertyAttributes.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/PropertyAttributes.java similarity index 89% rename from bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/PropertyAttributes.java rename to bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/PropertyAttributes.java index acc83f9b089d4..d418e36de12f6 100644 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/PropertyAttributes.java +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/PropertyAttributes.java @@ -10,15 +10,15 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homie300; +package org.openhab.binding.mqtt.homie.internal.homie300; import java.util.Map; import java.util.TreeMap; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.mqtt.mapping.AbstractMqttAttributeClass; -import org.openhab.binding.mqtt.mapping.MQTTvalueTransform; -import org.openhab.binding.mqtt.mapping.TopicPrefix; +import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass; +import org.openhab.binding.mqtt.generic.mapping.MQTTvalueTransform; +import org.openhab.binding.mqtt.generic.mapping.TopicPrefix; /** * Homie 3.x Property attributes diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/HandlerConfiguration.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/HandlerConfiguration.java deleted file mode 100644 index a5690848e72d6..0000000000000 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/HandlerConfiguration.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt.internal.homie300; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.mqtt.internal.handler.HomieThingHandler; - -/** - * The {@link HomieThingHandler} manages Things that are responsible for - * Homie MQTT devices. - * This class contains the necessary configuration for such a Thing handler. - * - * @author David Graeff - Initial contribution - */ -@NonNullByDefault -public class HandlerConfiguration { - /** - * The MQTT prefix topic - */ - public String basetopic = "homie"; - /** - * The device id. - */ - public String deviceid = ""; -} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/NodeAttributes.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/NodeAttributes.java deleted file mode 100644 index 208666c06252d..0000000000000 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/internal/homie300/NodeAttributes.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt.internal.homie300; - -import org.eclipse.jdt.annotation.NonNull; -import org.openhab.binding.mqtt.mapping.AbstractMqttAttributeClass; -import org.openhab.binding.mqtt.mapping.MQTTvalueTransform; -import org.openhab.binding.mqtt.mapping.MandatoryField; -import org.openhab.binding.mqtt.mapping.TopicPrefix; - -/** - * Homie 3.x Node attributes - * - * @author David Graeff - Initial contribution - */ -@TopicPrefix -public class NodeAttributes extends AbstractMqttAttributeClass { - public @MandatoryField String name; - public @MandatoryField @MQTTvalueTransform(splitCharacter = ",") String[] properties; - // Type has no meaning for ESH yet and is currently purely of textual, descriptive nature - public String type; - - @Override - public @NonNull Object getFieldsOf() { - return this; - } -} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/resources/ESH-INF/thing/homie-thing.xml b/bundles/org.openhab.binding.mqtt.homie/src/main/resources/ESH-INF/thing/homie-thing.xml index 167f9eb8e4059..5815d80e1128c 100644 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/resources/ESH-INF/thing/homie-thing.xml +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/resources/ESH-INF/thing/homie-thing.xml @@ -24,6 +24,11 @@ MQTT base prefix homie + + + Remove retained topics when thing is deleted + false + diff --git a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/ChannelStateHelper.java b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/ChannelStateHelper.java deleted file mode 100644 index 85baee003f183..0000000000000 --- a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/ChannelStateHelper.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; -import org.openhab.binding.mqtt.ChannelState; - -@NonNullByDefault -public class ChannelStateHelper { - public static void setConnection(ChannelState cs, MqttBrokerConnection connection) { - cs.setConnection(connection); - } -} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/ThingHandlerHelper.java b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/ThingHandlerHelper.java deleted file mode 100644 index 06ececd0a62be..0000000000000 --- a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/ThingHandlerHelper.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; - -@NonNullByDefault -public class ThingHandlerHelper { - public static void setConnection(AbstractMQTTThingHandler h, MqttBrokerConnection connection) { - h.connection = connection; - } -} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/ChannelStateHelper.java b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/ChannelStateHelper.java new file mode 100644 index 0000000000000..fd30efafd8af0 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/ChannelStateHelper.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.homie; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; +import org.openhab.binding.mqtt.generic.ChannelState; + +@NonNullByDefault +public class ChannelStateHelper { + public static void setConnection(ChannelState cs, MqttBrokerConnection connection) { + cs.setConnection(connection); + } +} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/ThingHandlerHelper.java b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/ThingHandlerHelper.java new file mode 100644 index 0000000000000..e94ac888ed264 --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/ThingHandlerHelper.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.homie; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; +import org.openhab.binding.mqtt.generic.AbstractMQTTThingHandler; + +@NonNullByDefault +public class ThingHandlerHelper { + public static void setConnection(AbstractMQTTThingHandler h, MqttBrokerConnection connection) { + h.setConnection(connection); + } +} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/generic/internal/mapping/HomieChildMapTests.java b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/generic/internal/mapping/HomieChildMapTests.java similarity index 87% rename from bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/generic/internal/mapping/HomieChildMapTests.java rename to bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/generic/internal/mapping/HomieChildMapTests.java index 8ed4006528758..99a8096444203 100644 --- a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/generic/internal/mapping/HomieChildMapTests.java +++ b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/generic/internal/mapping/HomieChildMapTests.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.generic.internal.mapping; +package org.openhab.binding.mqtt.homie.generic.internal.mapping; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @@ -25,11 +25,11 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; -import org.openhab.binding.mqtt.internal.handler.ThingChannelConstants; -import org.openhab.binding.mqtt.internal.homie300.DeviceCallback; -import org.openhab.binding.mqtt.internal.homie300.Node; -import org.openhab.binding.mqtt.internal.homie300.NodeAttributes; -import org.openhab.binding.mqtt.tools.ChildMap; +import org.openhab.binding.mqtt.homie.internal.handler.ThingChannelConstants; +import org.openhab.binding.mqtt.generic.tools.ChildMap; +import org.openhab.binding.mqtt.homie.internal.homie300.DeviceCallback; +import org.openhab.binding.mqtt.homie.internal.homie300.Node; +import org.openhab.binding.mqtt.homie.internal.homie300.NodeAttributes; /** * Tests cases for {@link HomieChildMap}. diff --git a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/internal/handler/HomieThingHandlerTests.java b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/internal/handler/HomieThingHandlerTests.java similarity index 91% rename from bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/internal/handler/HomieThingHandlerTests.java rename to bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/internal/handler/HomieThingHandlerTests.java index 4dce96014f582..258eee65b011d 100644 --- a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/internal/handler/HomieThingHandlerTests.java +++ b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/internal/handler/HomieThingHandlerTests.java @@ -10,13 +10,13 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.handler; +package org.openhab.binding.mqtt.homie.internal.handler; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import static org.openhab.binding.mqtt.internal.handler.ThingChannelConstants.testHomieThing; +import static org.openhab.binding.mqtt.homie.internal.handler.ThingChannelConstants.testHomieThing; import java.lang.reflect.Field; import java.util.ArrayList; @@ -49,25 +49,27 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; -import org.openhab.binding.mqtt.ChannelState; -import org.openhab.binding.mqtt.ChannelStateHelper; -import org.openhab.binding.mqtt.MqttChannelTypeProvider; -import org.openhab.binding.mqtt.ThingHandlerHelper; -import org.openhab.binding.mqtt.generic.internal.MqttBindingConstants; +import org.openhab.binding.mqtt.generic.ChannelState; +import org.openhab.binding.mqtt.homie.ChannelStateHelper; +import org.openhab.binding.mqtt.generic.MqttChannelTypeProvider; +import org.openhab.binding.mqtt.homie.ThingHandlerHelper; +import org.openhab.binding.mqtt.homie.internal.handler.ThingChannelConstants; +import org.openhab.binding.mqtt.generic.mapping.AbstractMqttAttributeClass; +import org.openhab.binding.mqtt.generic.mapping.SubscribeFieldToMQTTtopic; +import org.openhab.binding.mqtt.generic.tools.ChildMap; +import org.openhab.binding.mqtt.generic.tools.DelayedBatchProcessing; +import org.openhab.binding.mqtt.generic.values.Value; import org.openhab.binding.mqtt.handler.AbstractBrokerHandler; -import org.openhab.binding.mqtt.internal.homie300.Device; -import org.openhab.binding.mqtt.internal.homie300.DeviceAttributes; -import org.openhab.binding.mqtt.internal.homie300.DeviceAttributes.ReadyState; -import org.openhab.binding.mqtt.internal.homie300.Node; -import org.openhab.binding.mqtt.internal.homie300.NodeAttributes; -import org.openhab.binding.mqtt.internal.homie300.Property; -import org.openhab.binding.mqtt.internal.homie300.PropertyAttributes; -import org.openhab.binding.mqtt.internal.homie300.PropertyAttributes.DataTypeEnum; -import org.openhab.binding.mqtt.mapping.AbstractMqttAttributeClass; -import org.openhab.binding.mqtt.mapping.SubscribeFieldToMQTTtopic; -import org.openhab.binding.mqtt.tools.ChildMap; -import org.openhab.binding.mqtt.tools.DelayedBatchProcessing; -import org.openhab.binding.mqtt.values.Value; +import org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants; +import org.openhab.binding.mqtt.homie.internal.handler.HomieThingHandler; +import org.openhab.binding.mqtt.homie.internal.homie300.Device; +import org.openhab.binding.mqtt.homie.internal.homie300.DeviceAttributes; +import org.openhab.binding.mqtt.homie.internal.homie300.Node; +import org.openhab.binding.mqtt.homie.internal.homie300.NodeAttributes; +import org.openhab.binding.mqtt.homie.internal.homie300.Property; +import org.openhab.binding.mqtt.homie.internal.homie300.PropertyAttributes; +import org.openhab.binding.mqtt.homie.internal.homie300.DeviceAttributes.ReadyState; +import org.openhab.binding.mqtt.homie.internal.homie300.PropertyAttributes.DataTypeEnum; /** * Tests cases for {@link HomieThingHandler}. diff --git a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/internal/handler/ThingChannelConstants.java b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/internal/handler/ThingChannelConstants.java new file mode 100644 index 0000000000000..dee5282882a6d --- /dev/null +++ b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/internal/handler/ThingChannelConstants.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.mqtt.homie.internal.handler; + +import static org.openhab.binding.mqtt.homie.generic.internal.MqttBindingConstants.*; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; + +/** + * Static test definitions, like thing, bridge and channel definitions + * + * @author David Graeff - Initial contribution + */ +public class ThingChannelConstants { + // Common ThingUID and ChannelUIDs + final public static ThingUID testHomieThing = new ThingUID(HOMIE300_MQTT_THING, "device123"); + + final public static ChannelTypeUID unknownChannel = new ChannelTypeUID(BINDING_ID, "unknown"); + + final public static String jsonPathJSON = "{ \"device\": { \"status\": { \"temperature\": 23.2 }}}"; + final public static String jsonPathPattern = "$.device.status.temperature"; + + final public static List thingChannelList = new ArrayList<>(); + final public static List thingChannelListWithJson = new ArrayList<>(); + + static Configuration textConfiguration() { + Map data = new HashMap<>(); + data.put("stateTopic", "test/state"); + data.put("commandTopic", "test/command"); + return new Configuration(data); + } + + static Configuration textConfigurationWithJson() { + Map data = new HashMap<>(); + data.put("stateTopic", "test/state"); + data.put("commandTopic", "test/command"); + data.put("transformationPattern", "JSONPATH:" + jsonPathPattern); + return new Configuration(data); + } + + private static Configuration numberConfiguration() { + Map data = new HashMap<>(); + data.put("stateTopic", "test/state"); + data.put("commandTopic", "test/command"); + data.put("min", BigDecimal.valueOf(1)); + data.put("max", BigDecimal.valueOf(99)); + data.put("step", BigDecimal.valueOf(2)); + data.put("isDecimal", true); + return new Configuration(data); + } + + private static Configuration percentageConfiguration() { + Map data = new HashMap<>(); + data.put("stateTopic", "test/state"); + data.put("commandTopic", "test/command"); + data.put("on", "ON"); + data.put("off", "OFF"); + return new Configuration(data); + } + + private static Configuration onoffConfiguration() { + Map data = new HashMap<>(); + data.put("stateTopic", "test/state"); + data.put("commandTopic", "test/command"); + data.put("on", "ON"); + data.put("off", "OFF"); + data.put("inverse", true); + return new Configuration(data); + } + +} diff --git a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/internal/homie300/PropertyHelper.java b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/internal/homie300/PropertyHelper.java similarity index 81% rename from bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/internal/homie300/PropertyHelper.java rename to bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/internal/homie300/PropertyHelper.java index 284ca5459275a..2d345c8f31957 100644 --- a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/internal/homie300/PropertyHelper.java +++ b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/homie/internal/homie300/PropertyHelper.java @@ -10,11 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.mqtt.internal.homie300; +package org.openhab.binding.mqtt.homie.internal.homie300; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.mqtt.ChannelState; +import org.openhab.binding.mqtt.generic.ChannelState; +import org.openhab.binding.mqtt.homie.internal.homie300.Property; /** * Helper to access {@link Property} internals. diff --git a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/internal/handler/ThingChannelConstants.java b/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/internal/handler/ThingChannelConstants.java deleted file mode 100644 index dec149f158a7f..0000000000000 --- a/bundles/org.openhab.binding.mqtt.homie/src/test/java/org/openhab/binding/mqtt/internal/handler/ThingChannelConstants.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.mqtt.internal.handler; - -import static org.openhab.binding.mqtt.generic.internal.MqttBindingConstants.*; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.eclipse.smarthome.config.core.Configuration; -import org.eclipse.smarthome.core.thing.Channel; -import org.eclipse.smarthome.core.thing.ThingUID; -import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; - -/** - * Static test definitions, like thing, bridge and channel definitions - * - * @author David Graeff - Initial contribution - */ -public class ThingChannelConstants { - // Common ThingUID and ChannelUIDs - final public static ThingUID testHomieThing = new ThingUID(HOMIE300_MQTT_THING, "device123"); - - final public static ChannelTypeUID unknownChannel = new ChannelTypeUID(BINDING_ID, "unknown"); - - final public static String jsonPathJSON = "{ \"device\": { \"status\": { \"temperature\": 23.2 }}}"; - final public static String jsonPathPattern = "$.device.status.temperature"; - - final public static List thingChannelList = new ArrayList<>(); - final public static List thingChannelListWithJson = new ArrayList<>(); - - static Configuration textConfiguration() { - Map data = new HashMap<>(); - data.put("stateTopic", "test/state"); - data.put("commandTopic", "test/command"); - return new Configuration(data); - } - - static Configuration textConfigurationWithJson() { - Map data = new HashMap<>(); - data.put("stateTopic", "test/state"); - data.put("commandTopic", "test/command"); - data.put("transformationPattern", "JSONPATH:" + jsonPathPattern); - return new Configuration(data); - } - - private static Configuration numberConfiguration() { - Map data = new HashMap<>(); - data.put("stateTopic", "test/state"); - data.put("commandTopic", "test/command"); - data.put("min", BigDecimal.valueOf(1)); - data.put("max", BigDecimal.valueOf(99)); - data.put("step", BigDecimal.valueOf(2)); - data.put("isDecimal", true); - return new Configuration(data); - } - - private static Configuration percentageConfiguration() { - Map data = new HashMap<>(); - data.put("stateTopic", "test/state"); - data.put("commandTopic", "test/command"); - data.put("on", "ON"); - data.put("off", "OFF"); - return new Configuration(data); - } - - private static Configuration onoffConfiguration() { - Map data = new HashMap<>(); - data.put("stateTopic", "test/state"); - data.put("commandTopic", "test/command"); - data.put("on", "ON"); - data.put("off", "OFF"); - data.put("inverse", true); - return new Configuration(data); - } - -} diff --git a/bundles/org.openhab.binding.nest/src/main/java/org/openhab/binding/nest/internal/handler/NestBaseHandler.java b/bundles/org.openhab.binding.nest/src/main/java/org/openhab/binding/nest/internal/handler/NestBaseHandler.java index ea331c77957d9..717da9919d605 100644 --- a/bundles/org.openhab.binding.nest/src/main/java/org/openhab/binding/nest/internal/handler/NestBaseHandler.java +++ b/bundles/org.openhab.binding.nest/src/main/java/org/openhab/binding/nest/internal/handler/NestBaseHandler.java @@ -77,7 +77,8 @@ public void initialize() { logger.debug("Adding {} with ID '{}' as device data listener, result: {}", getClass().getSimpleName(), getId(), success); } else { - logger.debug("Unable to add {} with ID '{}' as device data listener because bridge is null"); + logger.debug("Unable to add {} with ID '{}' as device data listener because bridge is null", + getClass().getSimpleName(), getId()); } updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Waiting for refresh"); diff --git a/bundles/org.openhab.binding.netatmo/pom.xml b/bundles/org.openhab.binding.netatmo/pom.xml index 031fa7394a926..88a5e4d90e147 100644 --- a/bundles/org.openhab.binding.netatmo/pom.xml +++ b/bundles/org.openhab.binding.netatmo/pom.xml @@ -1,49 +1,55 @@ - 4.0.0 + 4.0.0 - - org.openhab.addons.bundles - org.openhab.addons.reactor.bundles - 2.5.0-SNAPSHOT - + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + - org.openhab.binding.netatmo + org.openhab.binding.netatmo - openHAB Add-ons :: Bundles :: Netatmo Binding + openHAB Add-ons :: Bundles :: Netatmo Binding - - - org.json - json - 20131018 - - - com.squareup.okhttp - okhttp - 2.3.0 - - - com.squareup.okio - okio - 1.3.0 - - - com.squareup.retrofit - retrofit - 1.9.0 - - - org.apache.oltu.oauth2 - org.apache.oltu.oauth2.client - 1.0.0 - - - org.apache.oltu.oauth2 - org.apache.oltu.oauth2.common - 1.0.0 - - + + + org.json + json + 20131018 + provided + + + com.squareup.okhttp + okhttp + 2.3.0 + provided + + + com.squareup.okio + okio + 1.3.0 + provided + + + com.squareup.retrofit + retrofit + 1.9.0 + provided + + + org.apache.oltu.oauth2 + org.apache.oltu.oauth2.client + 1.0.0 + provided + + + org.apache.oltu.oauth2 + org.apache.oltu.oauth2.common + 1.0.0 + provided + + diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java index f85057b3e22e4..1606020d6cc51 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java @@ -317,7 +317,7 @@ public boolean nativeARPPing(@Nullable ArpPingUtilEnum arpingTool, @Nullable Str proc = new ProcessBuilder(arpUtilPath, "-w", String.valueOf(timeoutInMS), "-x", ipV4address).start(); } else { - proc = new ProcessBuilder(arpUtilPath, "-w", String.valueOf(timeoutInMS / 1000), "-C", "1", "-I", + proc = new ProcessBuilder(arpUtilPath, "-w", String.valueOf(timeoutInMS / 1000), "-c", "1", "-I", interfaceName, ipV4address).start(); } diff --git a/bundles/org.openhab.binding.nikohomecontrol/.classpath b/bundles/org.openhab.binding.nikohomecontrol/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.nikohomecontrol/.project b/bundles/org.openhab.binding.nikohomecontrol/.project new file mode 100644 index 0000000000000..af0b4fdd9c008 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.nikohomecontrol + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.exec/NOTICE b/bundles/org.openhab.binding.nikohomecontrol/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.exec/NOTICE rename to bundles/org.openhab.binding.nikohomecontrol/NOTICE diff --git a/bundles/org.openhab.binding.nikohomecontrol/README.md b/bundles/org.openhab.binding.nikohomecontrol/README.md new file mode 100644 index 0000000000000..aac4b0753071d --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/README.md @@ -0,0 +1,287 @@ +# Niko Home Control Binding + +The Niko Home Control binding integrates with a [Niko Home Control](http://www.nikohomecontrol.be/) system through a Niko Home Control IP-interface or Niko Home Control Connected Controller. + +The binding supports both Niko Home Control I and Niko Home Control II. + +For Niko Home Control I, the binding has been tested with a Niko Home Control IP-interface (550-00508). This IP-interface provides access on the LAN. +The binding does not require a Niko Home Control Gateway (550-00580), but does work with it in the LAN. It will not make a remote connection. +It has also been confirmed to work with the Niko Home Control Connected Controller (550-00003) in a Niko Home Control I installation. + +For Niko Home Control II, the binding requires the Niko Home Control Connected Controller (550-00003). The installation does not need to be 'connected' (registered on the Niko Home Control website), and will work stricly in the LAN. + +For Niko Home Control I, the binding exposes all actions from the Niko Home Control System that can be triggered from the smartphone/tablet interface, as defined in the Niko Home Control I programming software. +For Niko Home Control II, the binding exposes all actions made visible in a touch profile, as configured in the Niko Home Control II programming software. +No actual Niko Touchscreen is required in the installation. + +Supported device types are switches, dimmers and rollershutters or blinds and thermostats. +Niko Home Control alarm and notice messages are retrieved and made available in the binding. + +## Supported Things + +The Niko Home Control Controller is represented as a bridge in the binding. +Connected to a bridge, the Niko Home Control Binding supports alloff actions, on/off actions (e.g. for lights or groups of lights), dimmers, rollershutters or blinds and thermostats. + +## Binding Configuration + +The bridge representing the Niko Home Control IP-interface needs to be added first in the things file or through Paper UI. +A bridge can be auto-discovered or created manually. +An auto-discovered bridge will have an IP-address parameter automatically filled with the current IP-address of the IP-interface. +This IP-address for the discovered bridge will automatically update when the IP-address of the IP-interface changes. + +The IP-address and port can be set when manually creating the bridge. + +If the IP-address is set on a manually created bridge, no attempt will be made to discover the correct IP-address. +You are responsible to force a fixed IP address on the Niko Home Control IP-interface through settings in your DHCP server. + +For Niko Home Control I, the port is set to 8000 by default and should match the port used by the Niko Home Control I IP-interface or Niko Home Control I Connected Controller. +For Niko Home Control II, the port is set to 8883 by default and should match the secure MQTT port used by the Niko Home Control II Connected Controller. + +For Niko Home Control I, no further bridge configuration is required when using auto-discovery. + +The Niko Home Control II bridge has extra required parameters for the touch profile and password. +Note that the password cannot be empty. +Therefore, a password needs to be set in the Niko Home Control II programming software for the used touch profile. + +An optional refresh interval will be used to restart the bridge at regular intervals (every 300 minutes by default). +Restarting the bridge at regular times improves the connection stability and avoids loss of connection. It can be turned off completely by setting the parameter to 0. + +## Discovery + +A discovery scan will first discover the Niko Home Control IP-interface or Niko Home Control Connected Controller in the network as a bridge. +Default parameters will be used. +Note that this may fail to find the correct Niko Home Control IP-interface when there are multiple IP-interfaces in the network, or when traffic to port 10000 on the openHAB server is blocked. + +When the Niko Home Control bridge is added as a thing, from the discovery inbox or manually, system information will be read from the Niko Home Control Controller and will be put in the bridge properties, visible through Paper UI. + +Subsequently, all defined actions that can be triggered from a smartphone/tablet in the Niko Home Control I system, respectively actions attached to a touch profile in the Niko Home Control II system, will be discovered and put in the inbox. +It is possible to trigger a manual scan for things on the Niko Home Control bridge. +Note that Niko Home Control II will require the touch profile and password parameters to be set on the bridge before the scan for actions can succeed. +The bridge will remain offline as long as these parameters are not set. + +If the Niko Home Control system has locations configured, these will be copied to thing locations and grouped as such in PaperUI. +Locations can subsequently be changed through the thing location parameter in PaperUI. + +## Thing Configuration + +Besides using PaperUI to manually configure things or adding automatically discovered things through PaperUI, you can add thing definitions in the things file. + +The Thing configuration for the **bridge** uses the following syntax: + +For Niko Home Control I: + +``` +Bridge nikohomecontrol:bridge: [ addr="", port=, + refresh= ] +``` + +`bridgeId` can have any value. + +`addr` is the fixed Niko Home Control IP-interface or Connected Controller address and is required. +`port` will be the port used to connect and is 8000 by default. +`refresh` is the interval to restart the communication in minutes (300 by default), if 0 or omitted the connection will not restart at regular intervals. + +For Niko Home Control II: + +``` +Bridge nikohomecontrol:bridge2: [ addr="", port=, + profile=, password=, + refresh= ] +``` + +`bridgeId` can have any value. + +`addr` is the fixed Niko Home Connected Controller address and is required. +`port` will be the port used to connect and is 8883 by default. +`profile` is the name of the touch profile configured in the Niko Home Control II programming software to be made available to openHAB. +`password` is the password for the touchprofile, cannot be empty. +`refresh` is the interval to restart the communication in minutes (300 by default), if 0 or omitted the connection will not restart at regular intervals. + +The Thing configuration for **Niko Home Control actions** has the following syntax: + +``` +Thing nikohomecontrol::: "Label" @ "Location" + [ actionId=, + step= ] +``` + +or nested in the bridge configuration: + +``` + "Label" @ "Location" [ actionId=, + step= ] +``` + +The following action thing types are valid for the configuration: + +``` +pushButton, onOff, dimmer, blind +``` + +`pushButton` types are used to map directly to stateless actions in the Niko Home Control system, such as All Off actions. +Discovery will identify All Off actions and map them to `pushButton` things. + +`thingId` can have any value, but will be set to the same value as the actionId parameter if discovery is used. + +`"Label"` is an optional label for the thing. + +`@ "Location"` is optional, and represents the location of the Thing. Auto-discovery would have assigned a value automatically. + +For Niko Home Control I, the `actionId` parameter is the unique IP Interface Object ID (`ipInterfaceObjectId`) as automatically assigned in the Niko Home Control Controller when programming the Niko Home Control system using the Niko Home Control I programming software. +It is not directly visible in the Niko Home Control programming or user software, but will be detected and automatically set by openHAB discovery. +For textual configuration, you can manually retrieve it from the content of the .nhcp configuration file created by the programming software. +Open the file with an unzip tool to read it's content. + +For Niko Home Control II, the `actionId` parameter is a unique ID for the action in the controller. It can only be auto-discovered. +If you want to define the action through textual configuration, the easiest way is to first do discovery on the bridge to get the correct `actionId` to use in the textual configuration. +Discover and add the thing you want to add. +Note down the `actionId` parameter from the thing, remove it before adding it again through textual configuration, with the same `actionId`parameter. +Alternatively the `actionId`can be retrieved from the configuration file. The file contains a SQLLite database. The database contains a table `Action` with column `FifthplayId` corresponding to the required `actionId`parameter. + +The `step` parameter is only available for dimmers. +It sets a step value for dimmer increase/decrease actions. The parameter is optional and set to 10 by default. + +The Thing configuration for **Niko Home Control thermostats** has the following syntax: + +``` +Thing nikohomecontrol:thermostat:: "Label" @ "Location" + [ thermostatId= ] +``` + +or nested in the bridge configuration: + +``` +thermostat "Label" @ "Location" [ thermostatId= ] +``` + +`thingId` can have any value, but will be set to the same value as the thermostatId parameter if discovery is used. + +`"Label"` is an optional label for the Thing. + +`@ "Location"` is optional, and represents the location of the thing. Auto-discovery would have assigned a value automatically. + +The `thermostatId` parameter is the unique ip Interface Object ID as automatically assigned in the Niko Home Control Controller I when programming the Niko Home Control system using the Niko Home Control programming software. +It is not directly visible in the Niko Home Control programming or user software, but will be detected and automatically set by openHAB discovery. +For textual configuration, it can be retrieved from the .nhcp configuration file. + +For Niko Home Control II, the `thermostatId` parameter is a unique ID for the action in the controller. It can only be auto-discovered. +If you want to define the action through textual configuration, you may first need to do discovery on the bridge to get the correct `thermostatId` to use in the textual configuration. + +The `overruleTime` parameter is used to set the standard overrule duration when you set a new setpoint without providing an overrule duration. +The default value is 60 minutes. + +## Channels + +For thing type `pushButton` the supported channel is `button`. +OnOff command types are supported. +For this channel, `autoupdate="false"` is set by default. +This will stop the linked item state from updating. + +For thing type `onOff` the supported channel is `switch`. +OnOff command types are supported. + +For thing type `dimmer` the supported channel is `brightness`. +OnOff, IncreaseDecrease and Percent command types are supported. +Note that sending an ON command will switch the dimmer to the value stored when last turning the dimmer off, or 100% depending on the configuration in the Niko Home Control Controller. +This can be changed with the Niko Home Control programming software. + +For thing type `blind` the supported channel is `rollershutter`. UpDown, StopMove and Percent command types are supported. + +For thing type `thermostat` the supported channels are `measured`, `mode`, `setpoint`, `overruletime` and `demand`. +`measured` gives the current temperature in QuantityType, allowing for different temperature units. +This channel is read only. +`mode` can be set and shows the current thermostat mode. +Allowed values are 0 (day), 1 (night), 2 (eco), 3 (off), 4 (cool), 5 (prog 1), 6 (prog 2), 7 (prog 3). If mode is set, the `setpoint` temperature will return to its standard value from the mode. +`setpoint` can be set and shows the current thermostat setpoint value in QuantityType. +When updating `setpoint`, it will overrule the temperature setpoint defined by the thermostat mode for `overruletime` duration. +`overruletime` is used to set the total duration to apply the setpoint temperature set in the setpoint channel before the thermostat returns to the setting in its mode. +`demand` is a number indicating of the system is actively heating/cooling. +The value will be 1 for heating, -1 for cooling and 0 if not heating or cooling. +Note that cooling in NHC I is set by the binding, and will incorrectly show cooling demand when the system does not have cooling capabilities. + +The bridge has two trigger channels `alarm` and `notice`. +It can be used as a trigger to rules. +The event message is the alarm or notice text coming from Niko Home Control. + +## Limitations + +The binding has been tested with a Niko Home Control I IP-interface (550-00508) and the Niko Home Control Connected Controller (550-00003) for Niko Home Control I and Niko Home Control II. + +The action events implemented are limited to onOff, dimmer, allOff, scenes, PIR and rollershutter or blinds. +Other actions have not been implemented. +It is not possible to tilt the slates of venetian blinds. + +Beyond action and thermostat events, the Niko Home Control communication also supports electricity, gas and water usage data. +Niko Home Control II also supports 3rd party devices. +All of this has not been implemented. + +## Example + +.things: + +``` +Bridge nikohomecontrol:bridge:nhc1 [ addr="192.168.0.70", port=8000, refresh=300 ] { + pushButton 1 "AllOff" [ actionId=1 ] + onOff 2 "LivingRoom" @ "Downstairs" [ actionId=2 ] + dimmer 3 "TVRoom" [ actionId=3, step=5 ] + blind 4 [ actionId=4 ] + thermostat 5 [ thermostatId=0, overruleTime=10 ] +} + +Bridge nikohomecontrol:bridge2:nhc2 [ addr="192.168.0.70", port=8883, profile="openHAB", password="mypassword", refresh=300 ] { + pushButton 1 "AllOff" [ actionId="12345678-abcd-1234-ef01-aa12bb34ee89" ] + onOff 2 "Office" @ "Downstairs" [ actionId="12345678-abcd-1234-ef01-aa12bb34cc56" ] + dimmer 3 "DiningRoom" [ actionId="abcdef01-abcd-1234-ab98-abcdef012345", step=5 ] + blind 4 [ actionId="abcdef01-abcd-1234-ab98-abcdefabcdef" ] + thermostat 5 [ thermostatId="abcdef01-abcd-1234-ab98-012345abcdef", overruleTime=10 ] +} + +Bridge nikohomecontrol:bridge:nhc3 [ addr="192.168.0.110" ] { + onOff 11 @ "Upstairs"[ actionId=11 ] + dimmer 12 [ actionId=12, step=5 ] + blind 13 [ actionId=13 ] + thermostat 14 [ thermostatId=10 ] +} +``` + +.items: + +``` +Switch AllOff {channel="nikohomecontrol:onOff:nhc1:1:button"} # Pushbutton for All Off action +Switch LivingRoom {channel="nikohomecontrol:onOff:nhc1:2:switch"} # Switch for onOff type action +Dimmer TVRoom {channel="nikohomecontrol:dimmer:nhc1:3:brightness"} # Changing brightness dimmer type action +Rollershutter Kitchen {channel="nikohomecontrol:blind:nhc1:4:rollershutter"} # Controlling rollershutter or blind type action +Number:Temperature CurTemperature "[%.1f °F]" {channel="nikohomecontrol:thermostat:nhc1:5:measured"} # Getting measured temperature from thermostat in °F, read only +Number ThermostatMode {channel="nikohomecontrol:thermostat:nhc1:5:mode"} # Get and set thermostat mode +Number:Temperature SetTemperature "[%.1f °C]" {channel="nikohomecontrol:thermostat:nhc1:5:setpoint"} # Get and set target temperature in °C +Number OverruleDuration {channel="nikohomecontrol:thermostat:nhc1:5:overruletime} # Get and set the overrule time +Number ThermostatDemand {channel="nikohomecontrol:thermostat:nhc1:5:demand} # Get the current heating/cooling demand +``` + +.sitemap: + +``` +Switch item=AllOff +Switch item=LivingRoom +Slider item=TVRoom +Switch item=TVRoom # allows switching dimmer item off or on (with controller defined behavior) +Rollershutter item=Kitchen +Text item=CurTemperature +Selection item=ThermostatMode mappings="[0="day", 1="night", 2="eco", 3="off", 4="cool", 5="prog 1", 6="prog 2", 7="prog 3"] +Setpoint item=SetTemperature minValue=0 maxValue=30 +Slider item=OverruleDuration minValue=0 maxValue=120 +``` + +Example trigger rule: + +``` +rule "example trigger rule" +when + Channel 'nikohomecontrol:bridge:nhc1:alarm' triggered or + Channel 'nikohomecontrol:bridge:nhc1:notice' triggered +then + var message = receivedEvent.getEvent() + logInfo("nhcTriggerExample", "Message: {}", message) + ... +end +``` diff --git a/bundles/org.openhab.binding.nikohomecontrol/pom.xml b/bundles/org.openhab.binding.nikohomecontrol/pom.xml new file mode 100644 index 0000000000000..c246a34f7f4b0 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.nikohomecontrol + + openHAB Add-ons :: Bundles :: Niko Home Control Binding + + diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/NikoHomeControlBindingConstants.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/NikoHomeControlBindingConstants.java new file mode 100644 index 0000000000000..756a10e8f9250 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/NikoHomeControlBindingConstants.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal; + +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link NikoHomeControlBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NikoHomeControlBindingConstants { + + public static final String BINDING_ID = "nikohomecontrol"; + + // List of all Thing Type UIDs + + // bridge + public static final ThingTypeUID BRIDGEI_THING_TYPE = new ThingTypeUID(BINDING_ID, "bridge"); + public static final ThingTypeUID BRIDGEII_THING_TYPE = new ThingTypeUID(BINDING_ID, "bridge2"); + + // generic thing types + public static final ThingTypeUID THING_TYPE_PUSHBUTTON = new ThingTypeUID(BINDING_ID, "pushButton"); + public static final ThingTypeUID THING_TYPE_ON_OFF_LIGHT = new ThingTypeUID(BINDING_ID, "onOff"); + public static final ThingTypeUID THING_TYPE_DIMMABLE_LIGHT = new ThingTypeUID(BINDING_ID, "dimmer"); + public static final ThingTypeUID THING_TYPE_BLIND = new ThingTypeUID(BINDING_ID, "blind"); + public static final ThingTypeUID THING_TYPE_THERMOSTAT = new ThingTypeUID(BINDING_ID, "thermostat"); + + // thing type sets + public static final Set BRIDGE_THING_TYPES_UIDS = Collections + .unmodifiableSet(Stream.of(BRIDGEI_THING_TYPE, BRIDGEII_THING_TYPE).collect(Collectors.toSet())); + public static final Set ACTION_THING_TYPES_UIDS = Collections.unmodifiableSet( + Stream.of(THING_TYPE_PUSHBUTTON, THING_TYPE_ON_OFF_LIGHT, THING_TYPE_DIMMABLE_LIGHT, THING_TYPE_BLIND) + .collect(Collectors.toSet())); + public static final Set SUPPORTED_THING_TYPES_UIDS = Collections + .unmodifiableSet(Stream.of(THING_TYPE_PUSHBUTTON, THING_TYPE_ON_OFF_LIGHT, THING_TYPE_DIMMABLE_LIGHT, + THING_TYPE_BLIND, THING_TYPE_THERMOSTAT).collect(Collectors.toSet())); + + // List of all Channel ids + public static final String CHANNEL_BUTTON = "button"; + public static final String CHANNEL_SWITCH = "switch"; + public static final String CHANNEL_BRIGHTNESS = "brightness"; + public static final String CHANNEL_ROLLERSHUTTER = "rollershutter"; + + public static final String CHANNEL_MEASURED = "measured"; + public static final String CHANNEL_SETPOINT = "setpoint"; + public static final String CHANNEL_OVERRULETIME = "overruletime"; + public static final String CHANNEL_MODE = "mode"; + public static final String CHANNEL_DEMAND = "demand"; + + public static final String CHANNEL_ALARM = "alarm"; + public static final String CHANNEL_NOTICE = "notice"; + + // Bridge config properties + public static final String CONFIG_HOST_NAME = "addr"; + public static final String CONFIG_PORT = "port"; + public static final String CONFIG_REFRESH = "refresh"; + public static final String CONFIG_PROFILE = "profile"; + public static final String CONFIG_PASSWORD = "password"; + + // Thing config properties + public static final String CONFIG_ACTION_ID = "actionId"; + public static final String CONFIG_STEP_VALUE = "step"; + + public static final String CONFIG_THERMOSTAT_ID = "thermostatId"; + public static final String CONFIG_OVERRULETIME = "overruleTime"; +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/NikoHomeControlHandlerFactory.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/NikoHomeControlHandlerFactory.java new file mode 100644 index 0000000000000..47a235c14a4fe --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/NikoHomeControlHandlerFactory.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal; + +import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*; + +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.net.NetworkAddressService; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.openhab.binding.nikohomecontrol.internal.discovery.NikoHomeControlDiscoveryService; +import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlActionHandler; +import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler; +import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler1; +import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler2; +import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlThermostatHandler; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +/** + * The {@link NikoHomeControlHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Mark Herwege - Initial Contribution + */ + +@Component(service = ThingHandlerFactory.class, configurationPid = "binding.nikohomecontrol") +@NonNullByDefault +public class NikoHomeControlHandlerFactory extends BaseThingHandlerFactory { + + private @NonNullByDefault({}) NetworkAddressService networkAddressService; + + private final Map> discoveryServiceRegs = new HashMap<>(); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID) || BRIDGE_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + if (BRIDGE_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) { + NikoHomeControlBridgeHandler handler; + if (BRIDGEII_THING_TYPE.equals(thing.getThingTypeUID())) { + handler = new NikoHomeControlBridgeHandler2((Bridge) thing, networkAddressService); + } else { + handler = new NikoHomeControlBridgeHandler1((Bridge) thing); + } + registerNikoHomeControlDiscoveryService(handler); + return handler; + } else if (THING_TYPE_THERMOSTAT.equals(thing.getThingTypeUID())) { + return new NikoHomeControlThermostatHandler(thing); + } else if (ACTION_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) { + return new NikoHomeControlActionHandler(thing); + } + + return null; + } + + private synchronized void registerNikoHomeControlDiscoveryService(NikoHomeControlBridgeHandler bridgeHandler) { + NikoHomeControlDiscoveryService nhcDiscoveryService = new NikoHomeControlDiscoveryService(bridgeHandler); + discoveryServiceRegs.put(bridgeHandler.getThing().getUID(), bundleContext.registerService( + DiscoveryService.class.getName(), nhcDiscoveryService, new Hashtable())); + nhcDiscoveryService.activate(); + } + + @Override + protected synchronized void removeHandler(ThingHandler thingHandler) { + if (thingHandler instanceof NikoHomeControlBridgeHandler) { + ServiceRegistration serviceReg = discoveryServiceRegs.remove(thingHandler.getThing().getUID()); + if (serviceReg != null) { + // remove discovery service, if bridge handler is removed + NikoHomeControlDiscoveryService service = (NikoHomeControlDiscoveryService) bundleContext + .getService(serviceReg.getReference()); + serviceReg.unregister(); + if (service != null) { + service.deactivate(); + } + } + } + } + + @Reference + protected void setNetworkAddressService(NetworkAddressService networkAddressService) { + this.networkAddressService = networkAddressService; + } + + protected void unsetNetworkAddressService(NetworkAddressService networkAddressService) { + this.networkAddressService = null; + } +} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlBridgeDiscoveryService.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlBridgeDiscoveryService.java similarity index 75% rename from addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlBridgeDiscoveryService.java rename to bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlBridgeDiscoveryService.java index 34f737c73809e..bbca57328815b 100644 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlBridgeDiscoveryService.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlBridgeDiscoveryService.java @@ -19,6 +19,8 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; import org.eclipse.smarthome.config.discovery.DiscoveryResult; import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; @@ -39,13 +41,14 @@ * @author Mark Herwege - Initial Contribution */ @Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.nikohomecontrol") +@NonNullByDefault public class NikoHomeControlBridgeDiscoveryService extends AbstractDiscoveryService { private final Logger logger = LoggerFactory.getLogger(NikoHomeControlBridgeDiscoveryService.class); - private ScheduledFuture nhcDiscoveryJob; + private volatile @Nullable ScheduledFuture nhcDiscoveryJob; - private NetworkAddressService networkAddressService; + private @NonNullByDefault({}) NetworkAddressService networkAddressService; private static final int TIMEOUT = 5; private static final int REFRESH_INTERVAL = 60; @@ -67,14 +70,18 @@ private void discoverBridge() { } logger.debug("Niko Home Control: discovery broadcast on {}", broadcastAddr); NikoHomeControlDiscover nhcDiscover = new NikoHomeControlDiscover(broadcastAddr); - addBridge(nhcDiscover.getAddr(), nhcDiscover.getNhcBridgeId()); + if (nhcDiscover.isNhcII()) { + addNhcIIBridge(nhcDiscover.getAddr(), nhcDiscover.getNhcBridgeId()); + } else { + addNhcIBridge(nhcDiscover.getAddr(), nhcDiscover.getNhcBridgeId()); + } } catch (IOException e) { logger.debug("Niko Home Control: no bridge found."); } } - private void addBridge(InetAddress addr, String bridgeId) { - logger.debug("Niko Home Control: bridge found at {}", addr); + private void addNhcIBridge(InetAddress addr, String bridgeId) { + logger.debug("Niko Home Control: NHC I bridge found at {}", addr); String bridgeName = "Niko Home Control Bridge"; ThingUID uid = new ThingUID(BINDING_ID, "bridge", bridgeId); @@ -84,6 +91,17 @@ private void addBridge(InetAddress addr, String bridgeId) { thingDiscovered(discoveryResult); } + private void addNhcIIBridge(InetAddress addr, String bridgeId) { + logger.debug("Niko Home Control: NHC II bridge found at {}", addr); + + String bridgeName = "Niko Home Control II Bridge"; + ThingUID uid = new ThingUID(BINDING_ID, "bridge2", bridgeId); + + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(uid).withLabel(bridgeName) + .withProperty(CONFIG_HOST_NAME, addr.getHostAddress()).build(); + thingDiscovered(discoveryResult); + } + @Override protected void startScan() { discoverBridge(); @@ -98,7 +116,8 @@ protected synchronized void stopScan() { @Override protected void startBackgroundDiscovery() { logger.debug("Niko Home Control: Start background bridge discovery"); - if (nhcDiscoveryJob == null || nhcDiscoveryJob.isCancelled()) { + ScheduledFuture job = nhcDiscoveryJob; + if (job == null || job.isCancelled()) { nhcDiscoveryJob = scheduler.scheduleWithFixedDelay(this::discoverBridge, 0, REFRESH_INTERVAL, TimeUnit.SECONDS); } @@ -107,8 +126,9 @@ protected void startBackgroundDiscovery() { @Override protected void stopBackgroundDiscovery() { logger.debug("Niko Home Control: Stop bridge background discovery"); - if (nhcDiscoveryJob != null && !nhcDiscoveryJob.isCancelled()) { - nhcDiscoveryJob.cancel(true); + ScheduledFuture job = nhcDiscoveryJob; + if (job != null && !job.isCancelled()) { + job.cancel(true); nhcDiscoveryJob = null; } } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlDiscoveryService.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlDiscoveryService.java new file mode 100644 index 0000000000000..5308c7c472d32 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/discovery/NikoHomeControlDiscoveryService.java @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.discovery; + +import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*; + +import java.util.Date; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.openhab.binding.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcThermostat; +import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * If a Niko Home Control bridge is added or if the user scans manually for things this + * {@link NikoHomeControlDiscoveryService} is used to return Niko Home Control Actions as things to the framework. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NikoHomeControlDiscoveryService extends AbstractDiscoveryService { + + private final Logger logger = LoggerFactory.getLogger(NikoHomeControlDiscoveryService.class); + + private static final int TIMEOUT = 5; + + private ThingUID bridgeUID; + private NikoHomeControlBridgeHandler handler; + + public NikoHomeControlDiscoveryService(NikoHomeControlBridgeHandler handler) { + super(SUPPORTED_THING_TYPES_UIDS, TIMEOUT, false); + logger.debug("Niko Home Control: discovery service {}", handler); + bridgeUID = handler.getThing().getUID(); + this.handler = handler; + } + + public void activate() { + handler.setNhcDiscovery(this); + } + + @Override + public void deactivate() { + removeOlderResults(new Date().getTime()); + handler.setNhcDiscovery(null); + } + + /** + * Discovers devices connected to a Niko Home Control controller + */ + public void discoverDevices() { + NikoHomeControlCommunication nhcComm = handler.getCommunication(); + + if ((nhcComm == null) || !nhcComm.communicationActive()) { + logger.warn("Niko Home Control: not connected."); + return; + } + logger.debug("Niko Home Control: getting devices on {}", handler.getThing().getUID().getId()); + + Map actions = nhcComm.getActions(); + + for (Map.Entry action : actions.entrySet()) { + + String actionId = action.getKey(); + NhcAction nhcAction = action.getValue(); + String thingName = nhcAction.getName(); + String thingLocation = nhcAction.getLocation(); + + switch (nhcAction.getType()) { + case TRIGGER: + addActionDevice(new ThingUID(THING_TYPE_PUSHBUTTON, handler.getThing().getUID(), actionId), + actionId, thingName, thingLocation); + break; + case RELAY: + addActionDevice(new ThingUID(THING_TYPE_ON_OFF_LIGHT, handler.getThing().getUID(), actionId), + actionId, thingName, thingLocation); + break; + case DIMMER: + addActionDevice(new ThingUID(THING_TYPE_DIMMABLE_LIGHT, handler.getThing().getUID(), actionId), + actionId, thingName, thingLocation); + break; + case ROLLERSHUTTER: + addActionDevice(new ThingUID(THING_TYPE_BLIND, handler.getThing().getUID(), actionId), actionId, + thingName, thingLocation); + break; + default: + logger.debug("Niko Home Control: unrecognized action type {} for {} {}", nhcAction.getType(), + actionId, thingName); + } + } + + Map thermostats = nhcComm.getThermostats(); + + for (Map.Entry thermostatEntry : thermostats.entrySet()) { + + String thermostatId = thermostatEntry.getKey(); + NhcThermostat nhcThermostat = thermostatEntry.getValue(); + String thingName = nhcThermostat.getName(); + String thingLocation = nhcThermostat.getLocation(); + addThermostatDevice(new ThingUID(THING_TYPE_THERMOSTAT, handler.getThing().getUID(), thermostatId), + thermostatId, thingName, thingLocation); + } + } + + private void addActionDevice(ThingUID uid, String actionId, String thingName, @Nullable String thingLocation) { + DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID) + .withLabel(thingName).withProperty(CONFIG_ACTION_ID, actionId); + if (thingLocation != null) { + discoveryResultBuilder.withProperty("Location", thingLocation); + } + thingDiscovered(discoveryResultBuilder.build()); + } + + private void addThermostatDevice(ThingUID uid, String thermostatId, String thingName, + @Nullable String thingLocation) { + DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID) + .withLabel(thingName).withProperty(CONFIG_THERMOSTAT_ID, thermostatId); + if (thingLocation != null) { + discoveryResultBuilder.withProperty("Location", thingLocation); + } + thingDiscovered(discoveryResultBuilder.build()); + } + + @Override + protected void startScan() { + discoverDevices(); + } + + @Override + protected synchronized void stopScan() { + super.stopScan(); + removeOlderResults(getTimestampOfLastScan()); + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionConfig.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionConfig.java new file mode 100644 index 0000000000000..525f795b51ffb --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionConfig.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.handler; + +/** + * {@link NikoHomeControlActionConfig} is the general config class for Niko Home Control Actions. + * + * @author Mark Herwege - Initial Contribution + */ +public class NikoHomeControlActionConfig { + public String actionId; +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionDimmerConfig.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionDimmerConfig.java new file mode 100644 index 0000000000000..050ec6c8e53fe --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionDimmerConfig.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.handler; + +/** + * {@link NikoHomeControlActionDimmerConfig} is the config class for Niko Home Control Dimmer Actions. + * + * @author Mark Herwege - Initial Contribution + */ +public class NikoHomeControlActionDimmerConfig extends NikoHomeControlActionConfig { + public int step; +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionHandler.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionHandler.java new file mode 100644 index 0000000000000..7d000d5d66c35 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlActionHandler.java @@ -0,0 +1,308 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.handler; + +import static org.eclipse.smarthome.core.types.RefreshType.REFRESH; +import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*; +import static org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.*; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.StopMoveType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcActionEvent; +import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication; +import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.ActionType; +import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcAction2; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link NikoHomeControlActionHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NikoHomeControlActionHandler extends BaseThingHandler implements NhcActionEvent { + + private final Logger logger = LoggerFactory.getLogger(NikoHomeControlActionHandler.class); + + private volatile @NonNullByDefault({}) NhcAction nhcAction; + + private String actionId = ""; + private int stepValue; + + public NikoHomeControlActionHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + Bridge nhcBridge = getBridge(); + if (nhcBridge == null) { + updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, + "Niko Home Control: no bridge initialized when trying to execute action " + actionId); + return; + } + NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler(); + if (nhcBridgeHandler == null) { + updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, + "Niko Home Control: no bridge initialized when trying to execute action " + actionId); + return; + } + NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication(); + + if (nhcComm == null || !nhcComm.communicationActive()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, + "Niko Home Control: bridge communication not initialized when trying to execute action " + + actionId); + return; + } + + if (nhcAction == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, + "Niko Home Control: actionId " + actionId + " does not match an action in the controller"); + return; + } + + if (nhcComm.communicationActive()) { + handleCommandSelection(channelUID, command); + } else { + // We lost connection but the connection object is there, so was correctly started. + // Try to restart communication. + // This can be expensive, therefore do it in a job. + scheduler.submit(() -> { + nhcComm.restartCommunication(); + // If still not active, take thing offline and return. + if (!nhcComm.communicationActive()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, + "Niko Home Control: communication error"); + return; + } + // Also put the bridge back online + nhcBridgeHandler.bridgeOnline(); + + // And finally handle the command + handleCommandSelection(channelUID, command); + }); + } + } + + private void handleCommandSelection(ChannelUID channelUID, Command command) { + logger.debug("Niko Home Control: handle command {} for {}", command, channelUID); + + if (command == REFRESH) { + actionEvent(nhcAction.getState()); + return; + } + + switch (channelUID.getId()) { + case CHANNEL_BUTTON: + case CHANNEL_SWITCH: + handleSwitchCommand(command); + updateStatus(ThingStatus.ONLINE); + break; + case CHANNEL_BRIGHTNESS: + handleBrightnessCommand(command); + updateStatus(ThingStatus.ONLINE); + break; + + case CHANNEL_ROLLERSHUTTER: + handleRollershutterCommand(command); + updateStatus(ThingStatus.ONLINE); + break; + + default: + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Niko Home Control: channel unknown " + channelUID.getId()); + } + } + + private void handleSwitchCommand(Command command) { + if (command instanceof OnOffType) { + OnOffType s = (OnOffType) command; + if (OnOffType.OFF.equals(s)) { + nhcAction.execute(NHCOFF); + } else { + nhcAction.execute(NHCON); + } + } + } + + private void handleBrightnessCommand(Command command) { + if (command instanceof OnOffType) { + OnOffType s = (OnOffType) command; + if (OnOffType.OFF.equals(s)) { + nhcAction.execute(NHCOFF); + } else { + nhcAction.execute(NHCON); + } + } else if (command instanceof IncreaseDecreaseType) { + IncreaseDecreaseType s = (IncreaseDecreaseType) command; + int currentValue = nhcAction.getState(); + int newValue; + if (IncreaseDecreaseType.INCREASE.equals(s)) { + newValue = currentValue + stepValue; + // round down to step multiple + newValue = newValue - newValue % stepValue; + nhcAction.execute(Integer.toString(newValue > 100 ? 100 : newValue)); + } else { + newValue = currentValue - stepValue; + // round up to step multiple + newValue = newValue + newValue % stepValue; + if (newValue <= 0) { + nhcAction.execute(NHCOFF); + } else { + nhcAction.execute(Integer.toString(newValue)); + } + } + } else if (command instanceof PercentType) { + PercentType p = (PercentType) command; + if (PercentType.ZERO.equals(p)) { + nhcAction.execute(NHCOFF); + } else { + nhcAction.execute(Integer.toString(p.intValue())); + } + } + } + + private void handleRollershutterCommand(Command command) { + if (command instanceof UpDownType) { + UpDownType s = (UpDownType) command; + if (UpDownType.UP.equals(s)) { + nhcAction.execute(NHCUP); + } else { + nhcAction.execute(NHCDOWN); + } + } else if (command instanceof StopMoveType) { + nhcAction.execute(NHCSTOP); + } else if (command instanceof PercentType) { + PercentType p = (PercentType) command; + nhcAction.execute(Integer.toString(100 - p.intValue())); + } + } + + @Override + public void initialize() { + NikoHomeControlActionConfig config; + if (thing.getThingTypeUID().equals(THING_TYPE_DIMMABLE_LIGHT)) { + config = getConfig().as(NikoHomeControlActionDimmerConfig.class); + stepValue = ((NikoHomeControlActionDimmerConfig) config).step; + } else { + config = getConfig().as(NikoHomeControlActionConfig.class); + } + actionId = config.actionId; + + Bridge nhcBridge = getBridge(); + if (nhcBridge == null) { + updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, + "Niko Home Control: no bridge initialized for action " + actionId); + return; + } + NikoHomeControlBridgeHandler nhcBridgeHandler = (NikoHomeControlBridgeHandler) nhcBridge.getHandler(); + if (nhcBridgeHandler == null) { + updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, + "Niko Home Control: no bridge initialized for action " + actionId); + return; + } + NikoHomeControlCommunication nhcComm = nhcBridgeHandler.getCommunication(); + + // We need to do this in a separate thread because we may have to wait for the communication to become active + scheduler.submit(() -> { + if (nhcComm == null || !nhcComm.communicationActive()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, + "Niko Home Control: no connection with Niko Home Control, could not initialize action " + + actionId); + return; + } + + nhcAction = nhcComm.getActions().get(actionId); + if (nhcAction == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, + "Niko Home Control: actionId does not match an action in the controller " + actionId); + return; + } + + String actionLocation = nhcAction.getLocation(); + + nhcAction.setEventHandler(this); + + updateProperties(); + + if (thing.getLocation() == null) { + thing.setLocation(actionLocation); + } + + actionEvent(nhcAction.getState()); + + logger.debug("Niko Home Control: action initialized {}", actionId); + }); + } + + private void updateProperties() { + Map properties = new HashMap<>(); + properties.put("type", String.valueOf(nhcAction.getType())); + if (getThing().getThingTypeUID() == THING_TYPE_BLIND) { + properties.put("timeToOpen", String.valueOf(nhcAction.getOpenTime())); + properties.put("timeToClose", String.valueOf(nhcAction.getCloseTime())); + } + + if (nhcAction instanceof NhcAction2) { + NhcAction2 action = (NhcAction2) nhcAction; + properties.put("model", action.getModel()); + properties.put("technology", action.getTechnology()); + } + + thing.setProperties(properties); + } + + @Override + public void actionEvent(int actionState) { + ActionType actionType = nhcAction.getType(); + + switch (actionType) { + case TRIGGER: + updateState(CHANNEL_BUTTON, (actionState == 0) ? OnOffType.OFF : OnOffType.ON); + updateStatus(ThingStatus.ONLINE); + case RELAY: + updateState(CHANNEL_SWITCH, (actionState == 0) ? OnOffType.OFF : OnOffType.ON); + updateStatus(ThingStatus.ONLINE); + break; + case DIMMER: + updateState(CHANNEL_BRIGHTNESS, new PercentType(actionState)); + updateStatus(ThingStatus.ONLINE); + break; + case ROLLERSHUTTER: + updateState(CHANNEL_ROLLERSHUTTER, new PercentType(actionState)); + updateStatus(ThingStatus.ONLINE); + break; + default: + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Niko Home Control: unknown action type " + actionType); + } + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig.java new file mode 100644 index 0000000000000..71e6ebcaac8c1 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.handler; + +/** + * {@link NikoHomeControlBridgeConfig} is the general config class for Niko Home Control Bridges. + * + * @author Mark Herwege - Initial Contribution + */ +public class NikoHomeControlBridgeConfig { + public String addr; + public int port; + public int refresh; +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig2.java new file mode 100644 index 0000000000000..3efff873763d7 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeConfig2.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.handler; + +/** + * {@link NikoHomeControlBridgeConfig2} is the extended config class for Niko Home Control II Bridges. + * + * @author Mark Herwege - Initial Contribution + */ +public class NikoHomeControlBridgeConfig2 extends NikoHomeControlBridgeConfig { + public String profile; + public String password; +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler.java new file mode 100644 index 0000000000000..4717ea0ac8a80 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler.java @@ -0,0 +1,281 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.handler; + +import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.nikohomecontrol.internal.discovery.NikoHomeControlDiscoveryService; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcControllerEvent; +import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * {@link NikoHomeControlBridgeHandler} is an abstract class representing a handler to all different interfaces to the + * Niko Home Control System. {@link NikoHomeControlBridgeHandler1} or {@link NikoHomeControlBridgeHandler2} should be + * used for the respective + * version of Niko Home Control. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public abstract class NikoHomeControlBridgeHandler extends BaseBridgeHandler implements NhcControllerEvent { + + private final Logger logger = LoggerFactory.getLogger(NikoHomeControlBridgeHandler.class); + + protected @NonNullByDefault({}) NikoHomeControlBridgeConfig config; + + protected @Nullable NikoHomeControlCommunication nhcComm; + + private volatile @Nullable ScheduledFuture refreshTimer; + + protected volatile @Nullable NikoHomeControlDiscoveryService nhcDiscovery; + + public NikoHomeControlBridgeHandler(Bridge nikoHomeControlBridge) { + super(nikoHomeControlBridge); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + // There is nothing to handle in the bridge handler + } + + /** + * Create communication object to Niko Home Control IP-interface and start communication. + * Trigger discovery when communication setup is successful. + */ + protected void startCommunication() { + NikoHomeControlCommunication comm = nhcComm; + if (comm == null) { + bridgeOffline(); + return; + } + + scheduler.submit(() -> { + comm.startCommunication(); + if (!comm.communicationActive()) { + bridgeOffline(); + return; + } + + updateProperties(); + + updateStatus(ThingStatus.ONLINE); + + int refreshInterval = config.refresh; + setupRefreshTimer(refreshInterval); + + if (nhcDiscovery != null) { + nhcDiscovery.discoverDevices(); + } else { + logger.debug("Niko Home Control: cannot discover devices, discovery service not started"); + } + }); + } + + /** + * Schedule future communication refresh. + * + * @param interval_config Time before refresh in minutes. + */ + private void setupRefreshTimer(int refreshInterval) { + if (refreshTimer != null) { + refreshTimer.cancel(true); + refreshTimer = null; + } + + if (refreshInterval == 0) { + return; + } + + // This timer will restart the bridge connection periodically + logger.debug("Niko Home Control: restart bridge connection every {} min", refreshInterval); + refreshTimer = scheduler.scheduleWithFixedDelay(() -> { + logger.debug("Niko Home Control: restart communication at scheduled time"); + + NikoHomeControlCommunication comm = nhcComm; + if (comm != null) { + comm.restartCommunication(); + if (!comm.communicationActive()) { + bridgeOffline(); + return; + } + + updateProperties(); + + updateStatus(ThingStatus.ONLINE); + } + }, refreshInterval, refreshInterval, TimeUnit.MINUTES); + } + + /** + * Take bridge offline when error in communication with Niko Home Control IP-interface. + */ + protected void bridgeOffline() { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, + "Niko Home Control: error starting bridge connection"); + } + + /** + * Put bridge online when error in communication resolved. + */ + public void bridgeOnline() { + updateProperties(); + updateStatus(ThingStatus.ONLINE); + } + + @Override + public void controllerOffline() { + bridgeOffline(); + + } + + @Override + public void controllerOnline() { + bridgeOnline(); + + int refreshInterval = config.refresh; + if (refreshTimer == null) { + setupRefreshTimer(refreshInterval); + } + } + + /** + * Update bridge properties with properties returned from Niko Home Control Controller, so they can be made visible + * in PaperUI. + */ + protected abstract void updateProperties(); + + @Override + public void dispose() { + if (refreshTimer != null) { + refreshTimer.cancel(true); + } + refreshTimer = null; + if (nhcComm != null) { + nhcComm.stopCommunication(); + } + nhcComm = null; + } + + @Override + public void handleConfigurationUpdate(Map configurationParameters) { + NikoHomeControlCommunication comm = nhcComm; + // if the communication had not been started yet, just dispose and initialize again + if (comm == null) { + super.handleConfigurationUpdate(configurationParameters); + return; + } + + Configuration configuration = editConfiguration(); + for (Entry configurationParmeter : configurationParameters.entrySet()) { + configuration.put(configurationParmeter.getKey(), configurationParmeter.getValue()); + } + updateConfiguration(configuration); + + setConfig(); + + scheduler.submit(() -> { + comm.restartCommunication(); + if (!comm.communicationActive()) { + bridgeOffline(); + return; + } + + updateProperties(); + + updateStatus(ThingStatus.ONLINE); + + int refreshInterval = config.refresh; + setupRefreshTimer(refreshInterval); + }); + } + + /** + * Set discovery service handler to be able to start discovery after bridge initialization. + * + * @param nhcDiscovery + */ + public void setNhcDiscovery(@Nullable NikoHomeControlDiscoveryService nhcDiscovery) { + this.nhcDiscovery = nhcDiscovery; + } + + /** + * Send a trigger from an alarm received from Niko Home Control. + * + * @param Niko Home Control alarm message + */ + @Override + public void alarmEvent(String alarmText) { + logger.debug("Niko Home Control: triggering alarm channel with {}", alarmText); + triggerChannel(CHANNEL_ALARM, alarmText); + updateStatus(ThingStatus.ONLINE); + } + + /** + * Send a trigger from a notice received from Niko Home Control. + * + * @param Niko Home Control alarm message + */ + @Override + public void noticeEvent(String alarmText) { + logger.debug("Niko Home Control: triggering notice channel with {}", alarmText); + triggerChannel(CHANNEL_NOTICE, alarmText); + updateStatus(ThingStatus.ONLINE); + } + + /** + * Get the Niko Home Control communication object. + * + * @return Niko Home Control communication object + */ + public @Nullable NikoHomeControlCommunication getCommunication() { + return nhcComm; + } + + @Override + public @Nullable InetAddress getAddr() { + InetAddress addr = null; + try { + addr = InetAddress.getByName(config.addr); + } catch (UnknownHostException e) { + logger.debug("Niko Home Control: Cannot resolve hostname {} to IP adress", config.addr); + } + return addr; + } + + @Override + public int getPort() { + return config.port; + } + + protected synchronized void setConfig() { + config = getConfig().as(NikoHomeControlBridgeConfig.class); + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler1.java new file mode 100644 index 0000000000000..ef30ae847c59e --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler1.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.handler; + +import java.net.InetAddress; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.openhab.binding.nikohomecontrol.internal.protocol.nhc1.NikoHomeControlCommunication1; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * {@link NikoHomeControlBridgeHandler1} is the handler for a Niko Home Control I IP-interface and connects it to + * the framework. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NikoHomeControlBridgeHandler1 extends NikoHomeControlBridgeHandler { + + private final Logger logger = LoggerFactory.getLogger(NikoHomeControlBridgeHandler1.class); + + public NikoHomeControlBridgeHandler1(Bridge nikoHomeControlBridge) { + super(nikoHomeControlBridge); + } + + @Override + public void initialize() { + logger.debug("Niko Home Control: initializing bridge handler"); + + setConfig(); + InetAddress addr = getAddr(); + int port = getPort(); + + logger.debug("Niko Home Control: bridge handler host {}, port {}", addr, port); + + if (addr != null) { + nhcComm = new NikoHomeControlCommunication1(this, scheduler); + startCommunication(); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, + "Niko Home Control: cannot resolve bridge IP with hostname " + config.addr); + } + } + + @Override + protected void updateProperties() { + Map properties = new HashMap<>(); + + NikoHomeControlCommunication1 comm = (NikoHomeControlCommunication1) nhcComm; + if (comm != null) { + properties.put("softwareVersion", comm.getSystemInfo().getSwVersion()); + properties.put("apiVersion", comm.getSystemInfo().getApi()); + properties.put("language", comm.getSystemInfo().getLanguage()); + properties.put("currency", comm.getSystemInfo().getCurrency()); + properties.put("units", comm.getSystemInfo().getUnits()); + properties.put("tzOffset", comm.getSystemInfo().getTz()); + properties.put("dstOffset", comm.getSystemInfo().getDst()); + properties.put("configDate", comm.getSystemInfo().getLastConfig()); + properties.put("energyEraseDate", comm.getSystemInfo().getLastEnergyErase()); + properties.put("connectionStartDate", comm.getSystemInfo().getTime()); + + thing.setProperties(properties); + } + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler2.java new file mode 100644 index 0000000000000..6903528686edf --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlBridgeHandler2.java @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.handler; + +import java.security.cert.CertificateException; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.config.core.ConfigConstants; +import org.eclipse.smarthome.core.net.NetworkAddressService; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NikoHomeControlCommunication2; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * {@link NikoHomeControlBridgeHandler2} is the handler for a Niko Home Control II Connected Controller and connects it + * to the framework. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NikoHomeControlBridgeHandler2 extends NikoHomeControlBridgeHandler { + + private final Logger logger = LoggerFactory.getLogger(NikoHomeControlBridgeHandler2.class); + + NetworkAddressService networkAddressService; + + public NikoHomeControlBridgeHandler2(Bridge nikoHomeControlBridge, NetworkAddressService networkAddressService) { + super(nikoHomeControlBridge); + this.networkAddressService = networkAddressService; + } + + @Override + public void initialize() { + logger.debug("Niko Home Control: initializing NHC II bridge handler"); + + setConfig(); + + String profile = getProfile(); + logger.debug("Niko Home Control: touch profile {}", profile); + if (profile.isEmpty()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, + "Niko Home Control: profile name not set."); + return; + } + + String password = getPassword(); + if (password.isEmpty()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, + "Niko Home Control: password for profile cannot be empty."); + return; + } + + String addr = networkAddressService.getPrimaryIpv4HostAddress(); + addr = (addr == null) ? "unknown" : addr.replace(".", "_"); + String clientId = addr + "-" + thing.getUID().toString().replace(":", "_"); + String userDataFolder = ConfigConstants.getUserDataFolder(); + try { + nhcComm = new NikoHomeControlCommunication2(this, clientId, userDataFolder); + startCommunication(); + } catch (CertificateException e) { + // this should not happen unless there is a programming error + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, + "Niko Home Control: not able to set SSL context"); + return; + } + } + + @Override + protected void updateProperties() { + Map properties = new HashMap<>(); + + NikoHomeControlCommunication2 comm = (NikoHomeControlCommunication2) nhcComm; + if (comm != null) { + properties.put("nhcVersion", comm.getSystemInfo().getNhcVersion()); + properties.put("cocoImage", comm.getSystemInfo().getCocoImage()); + properties.put("language", comm.getSystemInfo().getLanguage()); + properties.put("currency", comm.getSystemInfo().getCurrency()); + properties.put("units", comm.getSystemInfo().getUnits()); + properties.put("lastConfig", comm.getSystemInfo().getLastConfig()); + properties.put("electricityTariff", comm.getSystemInfo().getElectricityTariff()); + properties.put("gasTariff", comm.getSystemInfo().getGasTariff()); + properties.put("waterTariff", comm.getSystemInfo().getWaterTariff()); + properties.put("timeZone", comm.getTimeInfo().getTimeZone()); + properties.put("isDST", comm.getTimeInfo().getIsDst()); + properties.put("services", comm.getServices()); + + thing.setProperties(properties); + } + } + + @Override + public String getProfile() { + String profile = ((NikoHomeControlBridgeConfig2) config).profile; + if (profile == null) { + return ""; + } + return profile; + } + + @Override + public String getPassword() { + String password = ((NikoHomeControlBridgeConfig2) config).password; + if ((password == null) || password.isEmpty()) { + logger.debug("Niko Home Control: no password set."); + return ""; + } + return password; + } + + @Override + protected synchronized void setConfig() { + config = getConfig().as(NikoHomeControlBridgeConfig2.class); + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatConfig.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatConfig.java new file mode 100644 index 0000000000000..4e48b2cf1fd8c --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatConfig.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.handler; + +/** + * {@link NikoHomeControlThermostatConfig} is the config class for Niko Home Control Thermostats. + * + * @author Mark Herwege - Initial Contribution + */ +public class NikoHomeControlThermostatConfig { + public String thermostatId; + public int overruleTime; +} diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatHandler.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatHandler.java similarity index 81% rename from addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatHandler.java rename to bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatHandler.java index 486ea59bd1f4d..8b49d6f5e75b8 100644 --- a/addons/binding/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatHandler.java +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/handler/NikoHomeControlThermostatHandler.java @@ -23,7 +23,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.QuantityType; import org.eclipse.smarthome.core.thing.Bridge; @@ -34,6 +33,7 @@ import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; import org.eclipse.smarthome.core.types.Command; import org.openhab.binding.nikohomecontrol.internal.protocol.NhcThermostat; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcThermostatEvent; import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,12 +45,17 @@ * @author Mark Herwege - Initial Contribution */ @NonNullByDefault -public class NikoHomeControlThermostatHandler extends BaseThingHandler { +public class NikoHomeControlThermostatHandler extends BaseThingHandler implements NhcThermostatEvent { private final Logger logger = LoggerFactory.getLogger(NikoHomeControlThermostatHandler.class); - @Nullable - private volatile ScheduledFuture refreshTimer; // used to refresh the remaining overrule time every minute + private volatile @NonNullByDefault({}) NhcThermostat nhcThermostat; + + private String thermostatId = ""; + private int overruleTime; + + private volatile @Nullable ScheduledFuture refreshTimer; // used to refresh the remaining overrule time every + // minute public NikoHomeControlThermostatHandler(Thing thing) { super(thing); @@ -58,8 +63,6 @@ public NikoHomeControlThermostatHandler(Thing thing) { @Override public void handleCommand(ChannelUID channelUID, Command command) { - Integer thermostatId = ((Number) this.getConfig().get(CONFIG_THERMOSTAT_ID)).intValue(); - Bridge nhcBridge = getBridge(); if (nhcBridge == null) { updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.BRIDGE_UNINITIALIZED, @@ -92,7 +95,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } if (nhcComm.communicationActive()) { - handleCommandSelection(nhcThermostat, channelUID, command); + handleCommandSelection(channelUID, command); } else { // We lost connection but the connection object is there, so was correctly started. // Try to restart communication. @@ -109,28 +112,28 @@ public void handleCommand(ChannelUID channelUID, Command command) { nhcBridgeHandler.bridgeOnline(); // And finally handle the command - handleCommandSelection(nhcThermostat, channelUID, command); + handleCommandSelection(channelUID, command); }); } } - private void handleCommandSelection(NhcThermostat nhcThermostat, ChannelUID channelUID, Command command) { + private void handleCommandSelection(ChannelUID channelUID, Command command) { logger.debug("Niko Home Control: handle command {} for {}", command, channelUID); if (REFRESH.equals(command)) { - handleStateUpdate(nhcThermostat); + thermostatEvent(nhcThermostat.getMeasured(), nhcThermostat.getSetpoint(), nhcThermostat.getMode(), + nhcThermostat.getOverrule(), nhcThermostat.getDemand()); return; } switch (channelUID.getId()) { case CHANNEL_MEASURED: + case CHANNEL_DEMAND: updateStatus(ThingStatus.ONLINE); break; case CHANNEL_MODE: if (command instanceof DecimalType) { - // first reset the overrule - // nhcThermostat.executeOverruletime(0); nhcThermostat.executeMode(((DecimalType) command).intValue()); } updateStatus(ThingStatus.ONLINE); @@ -141,19 +144,21 @@ private void handleCommandSelection(NhcThermostat nhcThermostat, ChannelUID chan if (command instanceof QuantityType) { setpoint = ((QuantityType) command).toUnit(CELSIUS); // Always set the new setpoint temperature as an overrule - // If no overrule time is given yet, set the overrule to the configuration parameter + // If no overrule time is given yet, set the overrule time to the configuration parameter int time = nhcThermostat.getOverruletime(); if (time <= 0) { - time = ((Number) this.getConfig().get(CONFIG_OVERRULETIME)).intValue(); + time = overruleTime; + } + if (setpoint != null) { + nhcThermostat.executeOverrule(Math.round(setpoint.floatValue() * 10), time); } - nhcThermostat.executeOverrule(Math.round(setpoint.floatValue() * 10), time); } updateStatus(ThingStatus.ONLINE); break; case CHANNEL_OVERRULETIME: if (command instanceof DecimalType) { - Integer overruletime = ((DecimalType) command).intValue(); + int overruletime = ((DecimalType) command).intValue(); int overrule = nhcThermostat.getOverrule(); if (overruletime <= 0) { overruletime = 0; @@ -172,9 +177,10 @@ private void handleCommandSelection(NhcThermostat nhcThermostat, ChannelUID chan @Override public void initialize() { - Configuration config = this.getConfig(); + NikoHomeControlThermostatConfig config = getConfig().as(NikoHomeControlThermostatConfig.class); - Integer thermostatId = ((Number) config.get(CONFIG_THERMOSTAT_ID)).intValue(); + thermostatId = config.thermostatId; + overruleTime = config.overruleTime; Bridge nhcBridge = getBridge(); if (nhcBridge == null) { @@ -196,7 +202,7 @@ public void initialize() { return; } - NhcThermostat nhcThermostat = nhcComm.getThermostats().get(thermostatId); + nhcThermostat = nhcComm.getThermostats().get(thermostatId); if (nhcThermostat == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, "Niko Home Control: thermostatId does not match a thermostat in the controller " + thermostatId); @@ -205,24 +211,20 @@ public void initialize() { String thermostatLocation = nhcThermostat.getLocation(); - nhcThermostat.setThingHandler(this); + nhcThermostat.setEventHandler(this); if (thing.getLocation() == null) { thing.setLocation(thermostatLocation); } - handleStateUpdate(nhcThermostat); + thermostatEvent(nhcThermostat.getMeasured(), nhcThermostat.getSetpoint(), nhcThermostat.getMode(), + nhcThermostat.getOverrule(), nhcThermostat.getDemand()); logger.debug("Niko Home Control: thermostat intialized {}", thermostatId); } - /** - * Method to update state of all channels, called from Niko Home Control thermostat. - * - * @param nhcThermostat Niko Home Control thermostat - * - */ - public void handleStateUpdate(NhcThermostat nhcThermostat) { + @Override + public void thermostatEvent(int measured, int setpoint, int mode, int overrule, int demand) { updateState(CHANNEL_MEASURED, new QuantityType(nhcThermostat.getMeasured() / 10.0, CELSIUS)); long overruletime = nhcThermostat.getRemainingOverruletime(); @@ -233,12 +235,14 @@ public void handleStateUpdate(NhcThermostat nhcThermostat) { // If there is an overrule temperature set, use this in the setpoint channel, otherwise use the original // setpoint temperature if (overruletime == 0) { - updateState(CHANNEL_SETPOINT, new QuantityType(nhcThermostat.getSetpoint() / 10.0, CELSIUS)); + updateState(CHANNEL_SETPOINT, new QuantityType(setpoint / 10.0, CELSIUS)); } else { - updateState(CHANNEL_SETPOINT, new QuantityType(nhcThermostat.getOverrule() / 10.0, CELSIUS)); + updateState(CHANNEL_SETPOINT, new QuantityType(overrule / 10.0, CELSIUS)); } - updateState(CHANNEL_MODE, new DecimalType(nhcThermostat.getMode())); + updateState(CHANNEL_MODE, new DecimalType(mode)); + + updateState(CHANNEL_DEMAND, new DecimalType(demand)); updateStatus(ThingStatus.ONLINE); } @@ -256,7 +260,7 @@ private void scheduleRefreshOverruletime(NhcThermostat nhcThermostat) { return; } - this.refreshTimer = scheduler.scheduleWithFixedDelay(() -> { + refreshTimer = scheduler.scheduleWithFixedDelay(() -> { long remainingTime = nhcThermostat.getRemainingOverruletime(); updateState(CHANNEL_OVERRULETIME, new DecimalType(remainingTime)); if (remainingTime <= 0) { @@ -266,10 +270,10 @@ private void scheduleRefreshOverruletime(NhcThermostat nhcThermostat) { } private void cancelRefreshTimer() { - ScheduledFuture timer = this.refreshTimer; + ScheduledFuture timer = refreshTimer; if (timer != null) { timer.cancel(true); } - this.refreshTimer = null; + refreshTimer = null; } } diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcAction.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcAction.java new file mode 100644 index 0000000000000..9311252cdbb9b --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcAction.java @@ -0,0 +1,194 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.ActionType; +import org.openhab.binding.nikohomecontrol.internal.protocol.nhc1.NhcAction1; +import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcAction2; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link NhcAction} class represents the action Niko Home Control communication object. It contains all fields + * representing a Niko Home Control action and has methods to trigger the action in Niko Home Control and receive action + * updates. Specific implementation are {@link NhcAction1} and {@link NhcAction2}. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public abstract class NhcAction { + + private final Logger logger = LoggerFactory.getLogger(NhcAction.class); + + protected @Nullable NikoHomeControlCommunication nhcComm; + + protected String id; + protected String name; + protected ActionType type; + protected @Nullable String location; + protected volatile int state; + protected volatile int closeTime = 0; + protected volatile int openTime = 0; + + @Nullable + private NhcActionEvent eventHandler; + + protected NhcAction(String id, String name, ActionType type, @Nullable String location) { + this.id = id; + this.name = name; + this.type = type; + this.location = location; + } + + /** + * This method should be called when an object implementing the {@NhcActionEvent} interface is initialized. + * It keeps a record of the event handler in that object so it can be updated when the action receives an update + * from the Niko Home Control IP-interface. + * + * @param eventHandler + */ + public void setEventHandler(NhcActionEvent eventHandler) { + this.eventHandler = eventHandler; + } + + /** + * This method sets a pointer to the nhcComm object of class {@link NikoHomeControlCommuncation}. + * This is then used to be able to call back the sendCommand method in this class to send a command to the + * Niko Home Control IP-interface when.. + * + * @param nhcComm + */ + public void setNhcComm(NikoHomeControlCommunication nhcComm) { + this.nhcComm = nhcComm; + } + + /** + * Get the id of the action. + * + * @return the id + */ + public String getId() { + return id; + } + + /** + * Get name of action. + * + * @return action name + */ + public String getName() { + return name; + } + + /** + * Get type of action identified. + *

+ * ActionType can be RELAY (for simple light or socket switch), DIMMER, ROLLERSHUTTER, TRIGGER or GENERIC. + * + * @return {@link ActionType} + */ + public ActionType getType() { + return type; + } + + /** + * Get location name of action. + * + * @return location name + */ + public @Nullable String getLocation() { + return location; + } + + /** + * Get state of action. + *

+ * State is a value between 0 and 100 for a dimmer or rollershutter. + * Rollershutter state is 0 for fully closed and 100 for fully open. + * State is 0 or 100 for a switch. + * + * @return action state + */ + public int getState() { + return state; + } + + /** + * Set openTime and closeTime for rollershutter action. + *

+ * Time is in seconds to fully open or close a rollershutter. + * + * @param openTime + * @param closeTime + */ + public void setShutterTimes(int openTime, int closeTime) { + this.openTime = openTime; + this.closeTime = closeTime; + } + + /** + * Get openTime of action. + *

+ * openTime is the time in seconds to fully open a rollershutter. + * + * @return action openTime + */ + public int getOpenTime() { + return openTime; + } + + /** + * Get closeTime of action. + *

+ * closeTime is the time in seconds to fully close a rollershutter. + * + * @return action closeTime + */ + public int getCloseTime() { + return closeTime; + } + + protected void updateState() { + updateState(state); + } + + protected void updateState(int state) { + NhcActionEvent eventHandler = this.eventHandler; + if (eventHandler != null) { + logger.debug("Niko Home Control: update channel state for {} with {}", id, state); + eventHandler.actionEvent(state); + } + } + + /** + * Sets state of action. This method is implemented in {@link NhcAction1} and {@link NhcAction2}. + * + * @param state - The allowed values depend on the action type. + * switch action: 0 or 100 + * dimmer action: between 0 and 100 + * rollershutter action: between 0 and 100 + */ + public abstract void setState(int state); + + /** + * Sends action to Niko Home Control. This method is implemented in {@link NhcAction1} and {@link NhcAction2}. + * + * @param command - The allowed values depend on the action type. + * switch action: On or Off + * dimmer action: between 0 and 100, On or Off + * rollershutter action: between 0 and 100, Up, Down or Stop + */ + public abstract void execute(String command); +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcActionEvent.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcActionEvent.java new file mode 100644 index 0000000000000..a88978807bd95 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcActionEvent.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link NhcActionEvent} interface is used to pass action events received from the Niko Home Control controller to + * the consuming client. It is designed to pass events to openHAB handlers that implement this interface. Because of + * the design, the org.openhab.binding.nikohomecontrol.internal.protocol package can be extracted and used independent + * of openHAB. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public interface NhcActionEvent { + + /** + * This method is called when an action event is received from the Niko Home Control controller. + * + * @param state + */ + public void actionEvent(int state); + +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcControllerEvent.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcControllerEvent.java new file mode 100644 index 0000000000000..914f74b8de949 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcControllerEvent.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol; + +import java.net.InetAddress; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link NhcControllerEvent} interface is used to get configuration information and to pass alarm or notice events + * received from the Niko Home Control controller to the consuming client. It is designed to pass events to openHAB + * handlers that implement this interface. Because of the design, the + * org.openhab.binding.nikohomecontrol.internal.protocol package can be extracted and used independent of openHAB. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public interface NhcControllerEvent { + + /** + * Get the IP-address of the Niko Home Control IP-interface. + * + * @return the addr + */ + public default @Nullable InetAddress getAddr() { + return null; + }; + + /** + * Get the listening port of the Niko Home Control IP-interface. + * + * @return the port + */ + public default int getPort() { + return 0; + }; + + /** + * Get the touch profile of the Niko Home Control II system. + * + * @return the profile + */ + public default String getProfile() { + return ""; + }; + + /** + * Get the touch profile password of the Niko Home Control II system. + * + * @return the password + */ + public default String getPassword() { + return ""; + }; + + /** + * Called to indicate the connection with the Niko Home Control Controller is offline. + * + */ + public void controllerOffline(); + + /** + * Called to indicate the connection with the Niko Home Control Controller is online. + * + */ + public void controllerOnline(); + + /** + * This method is called when an alarm event is received from the Niko Home Control controller. + * + * @param alarmText + */ + public void alarmEvent(String alarmText); + + /** + * This method is called when a notice event is received from the Niko Home Control controller. + * + * @param alarmText + */ + public void noticeEvent(String noticeText); + +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostat.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostat.java new file mode 100644 index 0000000000000..a36e455ccb3a6 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostat.java @@ -0,0 +1,317 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.nikohomecontrol.internal.protocol.nhc1.NhcThermostat1; +import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcThermostat2; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link NhcThermostat} class represents the thermostat Niko Home Control communication object. It contains all + * fields representing a Niko Home Control thermostat and has methods to set the thermostat in Niko Home Control and + * receive thermostat updates. Specific implementation are {@link NhcThermostat1} and {@link NhcThermostat2}. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public abstract class NhcThermostat { + + private final Logger logger = LoggerFactory.getLogger(NhcThermostat.class); + + protected @Nullable NikoHomeControlCommunication nhcComm; + + protected String id; + protected String name; + protected @Nullable String location; + protected volatile int measured; + protected volatile int setpoint; + protected volatile int mode; + protected volatile int overrule; + protected volatile int overruletime; + protected volatile int ecosave; + protected volatile int demand; + + private @Nullable LocalDateTime overruleStart; + + private @Nullable NhcThermostatEvent eventHandler; + + protected NhcThermostat(String id, String name, @Nullable String location) { + this.id = id; + this.name = name; + this.location = location; + } + + /** + * Update all values of the thermostat without touching the thermostat definition (id, name and location) and + * without changing the ThingHandler callback. + * + * @param measured current temperature in 0.1°C multiples + * @param setpoint the setpoint temperature in 0.1°C multiples + * @param mode thermostat mode 0 = day, 1 = night, 2 = eco, 3 = off, 4 = cool, 5 = prog1, 6 = prog2, 7 = + * prog3 + * @param overrule the overrule temperature in 0.1°C multiples + * @param overruletime in minutes + * @param ecosave + * @param demand 0 if no demand, > 0 if heating, < 0 if cooling + */ + public void updateState(int measured, int setpoint, int mode, int overrule, int overruletime, int ecosave, + int demand) { + setMeasured(measured); + setSetpoint(setpoint); + setMode(mode); + setOverrule(overrule); + setOverruletime(overruletime); + setEcosave(ecosave); + setDemand(demand); + + updateChannels(); + } + + /** + * Update overrule values of the thermostat without touching the thermostat definition (id, name and location) and + * without changing the ThingHandler callback. + * + * @param overrule the overrule temperature in 0.1°C multiples + * @param overruletime in minutes + */ + public void updateState(int overrule, int overruletime) { + setOverrule(overrule); + setOverruletime(overruletime); + + updateChannels(); + } + + /** + * Update overrule values of the thermostat without touching the thermostat definition (id, name and location) and + * without changing the ThingHandler callback. + * + * @param overrule the overrule temperature in 0.1°C multiples + * @param overruletime in minutes + */ + public void updateState(int mode) { + setMode(mode); + + updateChannels(); + } + + private void updateChannels() { + NhcThermostatEvent handler = eventHandler; + if (handler != null) { + logger.debug("Niko Home Control: update channels for {}", id); + handler.thermostatEvent(measured, setpoint, mode, overrule, demand); + } + } + + /** + * This method should be called when an object implementing the {@NhcThermostatEvent} interface is initialized. + * It keeps a record of the event handler in that object so it can be updated when the action receives an update + * from the Niko Home Control IP-interface. + * + * @param eventHandler + */ + public void setEventHandler(NhcThermostatEvent eventHandler) { + this.eventHandler = eventHandler; + } + + /** + * This method sets a pointer to the nhcComm object of class {@link NikoHomeControlCommuncation}. + * This is then used to be able to call back the sendCommand method in this class to send a command to the + * Niko Home Control IP-interface when.. + * + * @param nhcComm + */ + public void setNhcComm(NikoHomeControlCommunication nhcComm) { + this.nhcComm = nhcComm; + } + + /** + * Get the id of the thermostat. + * + * @return the id + */ + public String getId() { + return id; + } + + /** + * Get name of thermostat. + * + * @return thermostat name + */ + public String getName() { + return name; + } + + /** + * Get location name of action. + * + * @return location name + */ + public @Nullable String getLocation() { + return location; + } + + /** + * Get measured temperature. + * + * @return measured temperature in 0.1°C multiples + */ + public int getMeasured() { + return measured; + } + + private void setMeasured(int measured) { + this.measured = measured; + } + + /** + * @return the setpoint temperature in 0.1°C multiples + */ + public int getSetpoint() { + return setpoint; + } + + private void setSetpoint(int setpoint) { + this.setpoint = setpoint; + } + + /** + * Get the thermostat mode. + * + * @return the mode: + * 0 = day, 1 = night, 2 = eco, 3 = off, 4 = cool, 5 = prog 1, 6 = prog 2, 7 = prog 3 + */ + public int getMode() { + return mode; + } + + private void setMode(int mode) { + this.mode = mode; + } + + /** + * Get the overrule temperature. + * + * @return the overrule temperature in 0.1°C multiples + */ + public int getOverrule() { + if (overrule > 0) { + return overrule; + } else { + return setpoint; + } + } + + private void setOverrule(int overrule) { + this.overrule = overrule; + } + + /** + * Get the duration for an overrule temperature + * + * @return the overruletime in minutes + */ + public int getOverruletime() { + return overruletime; + } + + /** + * Set the duration for an overrule temperature + * + * @param overruletime the overruletime in minutes + */ + private void setOverruletime(int overruletime) { + if (overruletime <= 0) { + stopOverrule(); + } else if (overruletime != this.overruletime) { + startOverrule(); + } + this.overruletime = overruletime; + } + + /** + * @return the ecosave mode + */ + public int getEcosave() { + return ecosave; + } + + /** + * @param ecosave the ecosave mode to set + */ + private void setEcosave(int ecosave) { + this.ecosave = ecosave; + } + + /** + * @return the heating/cooling demand: 0 if no demand, >0 if heating, <0 if cooling + */ + public int getDemand() { + return demand; + } + + /** + * @param demand set the heating/cooling demand + */ + private void setDemand(int demand) { + this.demand = demand; + } + + /** + * Sends thermostat mode to Niko Home Control. This method is implemented in {@link NhcThermostat1} and + * {@link NhcThermostat2}. + * + * @param mode + */ + public abstract void executeMode(int mode); + + /** + * Sends thermostat setpoint to Niko Home Control. This method is implemented in {@link NhcThermostat1} and + * {@link NhcThermostat2}. + * + * @param overrule temperature to overrule the setpoint in 0.1°C multiples + * @param time time duration in min for overrule + */ + public abstract void executeOverrule(int overrule, int overruletime); + + /** + * @return remaining overrule time in minutes + */ + public long getRemainingOverruletime() { + if (overruleStart == null) { + return 0; + } else { + return overruletime - ChronoUnit.MINUTES.between(overruleStart, LocalDateTime.now()); + } + } + + /** + * Start a new overrule, this method is used to be able to calculate the remaining overrule time + */ + private void startOverrule() { + overruleStart = LocalDateTime.now(); + } + + /** + * Reset overrule start + */ + private void stopOverrule() { + overruleStart = null; + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostatEvent.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostatEvent.java new file mode 100644 index 0000000000000..2b00b92fce248 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NhcThermostatEvent.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link NhcThermostatEvent} interface is used to pass thermostat events received from the Niko Home Control + * controller to + * the consuming client. It is designed to pass events to openHAB handlers that implement this interface. Because of + * the design, the org.openhab.binding.nikohomecontrol.internal.protocol package can be extracted and used independent + * of openHAB. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public interface NhcThermostatEvent { + + /** + * This method is called when thermostat event is received from the Niko Home Control controller. + * + * @param measured current temperature in 0.1°C multiples + * @param setpoint the setpoint temperature in 0.1°C multiples + * @param mode thermostat mode 0 = day, 1 = night, 2 = eco, 3 = off, 4 = cool, 5 = prog1, 6 = prog2, 7 = prog3 + * @param overrule the overrule temperature in 0.1°C multiples + * @param demand 0 if no demand, > 0 if heating, < 0 if cooling + */ + public void thermostatEvent(int measured, int setpoint, int mode, int overrule, int demand); + +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlCommunication.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlCommunication.java new file mode 100644 index 0000000000000..f2af169641d38 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlCommunication.java @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.nikohomecontrol.internal.protocol.nhc1.NikoHomeControlCommunication1; +import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NikoHomeControlCommunication2; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link NikoHomeControlCommunication} class is an abstract class representing the communication objects with the + * Niko Home Control System. {@link NikoHomeControlCommunication1} or {@link NikoHomeControlCommunication2} should be + * used for the respective version of Niko Home Control. + *

    + *
  • Start and stop communication with the Niko Home Control System. + *
  • Read all setup and status information from the Niko Home Control Controller. + *
  • Execute Niko Home Control commands. + *
  • Listen to events from Niko Home Control. + *
+ * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public abstract class NikoHomeControlCommunication { + + private final Logger logger = LoggerFactory.getLogger(NikoHomeControlCommunication.class); + + protected final Map actions = new ConcurrentHashMap<>(); + protected final Map thermostats = new ConcurrentHashMap<>(); + + protected final NhcControllerEvent handler; + + protected NikoHomeControlCommunication(NhcControllerEvent handler) { + this.handler = handler; + } + + /** + * Start Communication with Niko Home Control system. + */ + public abstract void startCommunication(); + + /** + * Stop Communication with Niko Home Control system. + */ + public abstract void stopCommunication(); + + /** + * Close and restart communication with Niko Home Control system. + */ + public synchronized void restartCommunication() { + stopCommunication(); + + logger.debug("Niko Home Control: restart communication from thread {}", Thread.currentThread().getId()); + + startCommunication(); + } + + /** + * Method to check if communication with Niko Home Control is active. + * + * @return True if active + */ + public abstract boolean communicationActive(); + + /** + * Return all actions in the Niko Home Control Controller. + * + * @return Map<String, {@link NhcAction}> + */ + public Map getActions() { + return actions; + } + + /** + * Return all thermostats in the Niko Home Control Controller. + * + * @return Map<String, {@link NhcThermostat}> + */ + public Map getThermostats() { + return thermostats; + } + + /** + * Execute an action command by sending it to Niko Home Control. + * + * @param actionId + * @param value + */ + public abstract void executeAction(String actionId, String value); + + /** + * Execute a thermostat command by sending it to Niko Home Control. + * + * @param thermostatId + * @param mode + */ + public abstract void executeThermostat(String thermostatId, String mode); + + /** + * Execute a thermostat command by sending it to Niko Home Control. + * + * @param thermostatId + * @param overruleTemp + * @param overruleTime + */ + public abstract void executeThermostat(String thermostatId, int overruleTemp, int overruleTime); +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlConstants.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlConstants.java new file mode 100644 index 0000000000000..d56d452c092ba --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlConstants.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link NikoHomeControlConstants} class defines common constants used in the Niko Home Control communication. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NikoHomeControlConstants { + + // Action types abstracted from NhcI and NhcII action types + public static enum ActionType { + TRIGGER, + RELAY, + DIMMER, + ROLLERSHUTTER, + GENERIC + }; + + // switch and dimmer constants in the Nhc layer + public static final String NHCON = "On"; + public static final String NHCOFF = "Off"; + + public static final String NHCTRIGGERED = "Triggered"; + + // rollershutter constants in the Nhc layer + public static final String NHCDOWN = "Down"; + public static final String NHCUP = "Up"; + public static final String NHCSTOP = "Stop"; + + // NhcII thermostat modes + public static final String[] THERMOSTATMODES = { "Day", "Night", "Eco", "Off", "Cool", "Prog1", "Prog2", "Prog3" }; +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlDiscover.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlDiscover.java new file mode 100644 index 0000000000000..8e14c2e97272c --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/NikoHomeControlDiscover.java @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.util.Arrays; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.util.HexUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class {@link NikoHomeControlDiscover} is used to get the Niko Home Control IP-interface IP address for bridge + * discovery. + *

+ * The constructor broadcasts a UDP packet with content 0x44 on port 10000. + * The Niko Home Control IP-interface responds to this UDP packet. + * The IP-address from the Niko Home Control IP-interface is then extracted from the response packet. + * The data content of the response packet is used as a unique identifier for the bridge. + * + * @author Mark Herwege - Initial Contribution + */ +/** + * @author Mark Herwege - Initial Contribution + * + */ +@NonNullByDefault +public final class NikoHomeControlDiscover { + + private final Logger logger = LoggerFactory.getLogger(NikoHomeControlDiscover.class); + + private InetAddress addr; + private String nhcBridgeId = ""; + private boolean isNhcII; + + /** + * Discover a Niko Home Control IP interface by broadcasting UDP packet 0x44 to port 10000. The IP interface will + * reply. The address of the IP interface is than derived from that response. + * + * @param broadcast Broadcast address of the network + * @throws IOException + */ + public NikoHomeControlDiscover(String broadcast) throws IOException { + final byte[] discoverBuffer = { (byte) 0x44 }; + final InetAddress broadcastAddr = InetAddress.getByName(broadcast); + final int broadcastPort = 10000; + + DatagramPacket discoveryPacket = new DatagramPacket(discoverBuffer, discoverBuffer.length, broadcastAddr, + broadcastPort); + byte[] buffer = new byte[1024]; + DatagramPacket packet = new DatagramPacket(buffer, buffer.length); + + try (DatagramSocket datagramSocket = new DatagramSocket(null)) { + datagramSocket.setBroadcast(true); + datagramSocket.setSoTimeout(500); + datagramSocket.send(discoveryPacket); + while (true) { + datagramSocket.receive(packet); + logger.trace("Niko Home Control: bridge discovery response {}", + HexUtils.bytesToHex(Arrays.copyOf(packet.getData(), packet.getLength()))); + if (isNhc(packet)) { + break; + } + } + addr = packet.getAddress(); + setNhcBridgeId(packet); + setIsNhcII(packet); + logger.debug("Niko Home Control: IP address is {}, unique ID is {}", addr, nhcBridgeId); + } + } + + /** + * @return the addr + */ + public InetAddress getAddr() { + return addr; + } + + /** + * @return the nhcBridgeId + */ + public String getNhcBridgeId() { + return nhcBridgeId; + } + + /** + * Check if the UDP packet comes from a Niko Home Control controller. The response should start with 0x44. + * + * @param packet + * @return true if packet is from a Niko Home Control controller + */ + private boolean isNhc(DatagramPacket packet) { + byte[] packetData = packet.getData(); + if ((packet.getLength() > 2) && (packetData[0] == 0x44)) { + return true; + } + return false; + } + + /** + * Retrieves a unique ID from the returned datagram packet received after sending the UDP discovery message. + * + * @param packet + */ + private void setNhcBridgeId(DatagramPacket packet) { + byte[] packetData = packet.getData(); + int packetLength = packet.getLength(); + packetLength = packetLength > 6 ? 6 : packetLength; + StringBuilder sb = new StringBuilder(packetLength); + for (int i = 0; i < packetLength; i++) { + sb.append(String.format("%02x", packetData[i])); + } + nhcBridgeId = sb.toString(); + } + + /** + * Checks if this is a NHC II Connected Controller + * + * @param packet + */ + private void setIsNhcII(DatagramPacket packet) { + byte[] packetData = packet.getData(); + int packetLength = packet.getLength(); + // The 16th byte in the packet is 2 for a NHC II Connected Controller + if ((packetLength >= 16) && (packetData[15] >= 2)) { + isNhcII = true; + } else { + isNhcII = false; + } + } + + /** + * Test if the installation is a Niko Home Control II installation + * + * @return true if this is a Niko Home Control II installation + */ + public boolean isNhcII() { + return isNhcII; + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcAction1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcAction1.java new file mode 100644 index 0000000000000..d6a7c7d33c3c7 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcAction1.java @@ -0,0 +1,315 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; + +import static org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.*; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction; +import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.ActionType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link NhcAction1} class represents the action Niko Home Control I communication object. It contains all fields + * representing a Niko Home Control action and has methods to trigger the action in Niko Home Control and receive action + * updates. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NhcAction1 extends NhcAction { + + private final Logger logger = LoggerFactory.getLogger(NhcAction1.class); + + @FunctionalInterface + private interface Action { + void execute(); + } + + private ScheduledExecutorService scheduler; + + private volatile @Nullable Action rollershutterTask; + private volatile @Nullable ScheduledFuture rollershutterStopTask; + private volatile @Nullable ScheduledFuture rollershutterMovingFlagTask; + + private volatile boolean filterEvent = false; // flag to filter first event from rollershutter on percent move to + // avoid wrong position update + private volatile boolean rollershutterMoving = false; // flag to indicate if rollershutter is currently moving + private volatile boolean waitForEvent = false; // flag to wait for position update rollershutter before doing next + // move + + NhcAction1(String id, String name, ActionType type, @Nullable String location, ScheduledExecutorService scheduler) { + super(id, name, type, location); + this.scheduler = scheduler; + } + + /** + * Sets state of action. This is the version for Niko Home Control I. + * + * @param newState - The allowed values depend on the action type. + * switch action: 0 or 100 + * dimmer action: between 0 and 100 + * rollershutter action: between 0 and 100 + */ + @Override + public void setState(int newState) { + if (getType() == ActionType.ROLLERSHUTTER) { + if (filterEvent) { + filterEvent = false; + logger.debug("Niko Home Control: filtered event {} for {}", newState, id); + return; + } + + cancelRollershutterStop(); + + if (((newState == 0) || (newState == 100)) && (newState != state)) { + long duration = rollershutterMoveTime(state, newState); + setRollershutterMovingTrue(duration); + } else { + setRollershutterMovingFalse(); + } + } + if (waitForEvent) { + logger.debug("Niko Home Control: received requested rollershutter {} position event {}", id, newState); + executeRollershutterTask(); + } else { + state = newState; + updateState(); + } + } + + /** + * Sends action to Niko Home Control. This version is used for Niko Home Control I. + * + * @param command - The allowed values depend on the action type. + */ + @Override + public void execute(String command) { + logger.debug("Niko Home Control: execute action {} of type {} for {}", command, type, id); + + String value = ""; + switch (getType()) { + case GENERIC: + case TRIGGER: + case RELAY: + if (command.equals(NHCON)) { + value = "100"; + } else { + value = "0"; + } + break; + case DIMMER: + if (command.equals(NHCON)) { + value = "254"; + } else if (command.equals(NHCOFF)) { + value = "255"; + } else { + value = command; + } + break; + case ROLLERSHUTTER: + // requires special treatment, so handled outside of this switch + break; + } + + if (nhcComm != null) { + nhcComm.executeAction(id, value); + return; + } + + if (getType() == ActionType.ROLLERSHUTTER) { + executeRollershutter(command); + } + } + + private void executeRollershutter(String command) { + if (logger.isTraceEnabled()) { + logger.trace("handleRollerShutterCommand: rollershutter {} command {}", id, command); + logger.trace("handleRollerShutterCommand: rollershutter {}, current position {}", id, state); + } + + // first stop all current movement of rollershutter and wait until exact position is known + if (rollershutterMoving) { + if (logger.isTraceEnabled()) { + logger.trace("handleRollerShutterCommand: rollershutter {} moving, therefore stop", id); + } + rollershutterPositionStop(); + } + + // task to be executed once exact position received from Niko Home Control + rollershutterTask = () -> { + if (logger.isTraceEnabled()) { + logger.trace("handleRollerShutterCommand: rollershutter {} task running", id); + } + + int currentValue = state; + + if (command.equals(NHCDOWN)) { + executeRollershutterDown(); + } else if (command.equals(NHCUP)) { + executeRollershutterUp(); + } else if (command.equals(NHCSTOP)) { + executeRollershutterStop(); + } else { + int newValue = 100 - Integer.parseInt(command); + if (logger.isTraceEnabled()) { + logger.trace("handleRollerShutterCommand: rollershutter {} percent command, current {}, new {}", id, + currentValue, newValue); + } + if (currentValue == newValue) { + return; + } + if ((newValue > 0) && (newValue < 100)) { + scheduleRollershutterStop(currentValue, newValue); + } + if (newValue < currentValue) { + executeRollershutterDown(); + } else if (newValue > currentValue) { + executeRollershutterUp(); + } + } + }; + + // execute immediately if not waiting for exact position + if (!waitForEvent) { + if (logger.isTraceEnabled()) { + logger.trace("handleRollerShutterCommand: rollershutter {} task executing immediately", id); + } + executeRollershutterTask(); + } + } + + private void executeRollershutterStop() { + if (nhcComm != null) { + nhcComm.executeAction(id, "253"); + } + } + + private void executeRollershutterDown() { + if (nhcComm != null) { + nhcComm.executeAction(id, "254"); + } + } + + private void executeRollershutterUp() { + if (nhcComm != null) { + nhcComm.executeAction(id, "255"); + } + } + + /** + * Method used to stop rollershutter when moving. This will then result in an exact position to be received, so next + * percentage movements could be done accurately. + */ + private void rollershutterPositionStop() { + if (logger.isTraceEnabled()) { + logger.trace("rollershutterPositionStop: rollershutter {} executing", id); + } + cancelRollershutterStop(); + rollershutterTask = null; + filterEvent = false; + waitForEvent = true; + executeRollershutterStop(); + } + + private void executeRollershutterTask() { + if (logger.isTraceEnabled()) { + logger.trace("executeRollershutterTask: rollershutter {} task triggered", id); + } + waitForEvent = false; + + Action action = rollershutterTask; + if (action != null) { + action.execute(); + rollershutterTask = null; + } + } + + /** + * Method used to schedule a rollershutter stop when moving. This allows stopping the rollershutter at a percent + * position. + * + * @param currentValue current percent position + * @param newValue new percent position + * + */ + private void scheduleRollershutterStop(int currentValue, int newValue) { + // filter first event for a rollershutter coming from Niko Home Control if moving to an intermediate + // position to avoid updating state to full open or full close + filterEvent = true; + + long duration = rollershutterMoveTime(currentValue, newValue); + setRollershutterMovingTrue(duration); + + if (logger.isTraceEnabled()) { + logger.trace("scheduleRollershutterStop: schedule rollershutter {} stop in {}ms", id, duration); + } + rollershutterStopTask = scheduler.schedule(() -> { + logger.trace("scheduleRollershutterStop: run rollershutter {} stop", id); + executeRollershutterStop(); + }, duration, TimeUnit.MILLISECONDS); + } + + private void cancelRollershutterStop() { + ScheduledFuture stopTask = rollershutterStopTask; + if (stopTask != null) { + if (logger.isTraceEnabled()) { + logger.trace("cancelRollershutterStop: cancel rollershutter {} stop", id); + } + stopTask.cancel(true); + } + rollershutterStopTask = null; + + filterEvent = false; + } + + private void setRollershutterMovingTrue(long duration) { + if (logger.isTraceEnabled()) { + logger.trace("setRollershutterMovingTrue: rollershutter {} moving", id); + } + rollershutterMoving = true; + rollershutterMovingFlagTask = scheduler.schedule(() -> { + if (logger.isTraceEnabled()) { + logger.trace("setRollershutterMovingTrue: rollershutter {} stopped moving", id); + } + rollershutterMoving = false; + }, duration, TimeUnit.MILLISECONDS); + } + + private void setRollershutterMovingFalse() { + if (logger.isTraceEnabled()) { + logger.trace("setRollershutterMovingFalse: rollershutter {} not moving", id); + } + rollershutterMoving = false; + ScheduledFuture future = rollershutterMovingFlagTask; + if (future != null) { + future.cancel(true); + rollershutterMovingFlagTask = null; + } + } + + private long rollershutterMoveTime(int currentValue, int newValue) { + int totalTime = (newValue > currentValue) ? getOpenTime() : getCloseTime(); + long duration = Math.abs(newValue - currentValue) * totalTime * 10; + if (logger.isTraceEnabled()) { + logger.trace("rollershutterMoveTime: rollershutter {} move time {}", id, duration); + } + return duration; + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcLocation1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcLocation1.java new file mode 100644 index 0000000000000..be4e249144719 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcLocation1.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link NhcLocation1} class represents the location Niko Home Control communication object. It contains all fields + * representing a Niko Home Control location. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public final class NhcLocation1 { + + private final String name; + + public NhcLocation1(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageBase1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageBase1.java new file mode 100644 index 0000000000000..15b478b065dd5 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageBase1.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; + +/** + * Class {@link NhcMessageBase1} used as base class for output from gson for cmd or event feedback from Niko Home + * Control. This class only contains the common base fields required for the deserializer + * {@link NikoHomeControlMessageDeserializer1} to select the specific formats implemented in {@link NhcMessageMap1}, + * {@link NhcMessageListMap1}, {@link NhcMessageCmd1}. + *

+ * + * @author Mark Herwege - Initial Contribution + */ +abstract class NhcMessageBase1 { + + private String cmd; + private String event; + + String getCmd() { + return cmd; + } + + void setCmd(String cmd) { + this.cmd = cmd; + } + + String getEvent() { + return event; + } + + void setEvent(String event) { + this.event = event; + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageCmd1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageCmd1.java new file mode 100644 index 0000000000000..c5e364ebb29b1 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageCmd1.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; + +/** + * Class {@link NhcMessageCmd1} used as input to gson to send commands to Niko Home Control. Extends + * {@link NhcMessageBase1}. + *

+ * Example: {"cmd":"executeactions","id":1,"value1":0} + * + * @author Mark Herwege - Initial Contribution + */ +@SuppressWarnings("unused") +class NhcMessageCmd1 extends NhcMessageBase1 { + + private int id; + private int value1; + private int value2; + private int value3; + private int mode; + private int overrule; + private String overruletime; + + NhcMessageCmd1(String cmd) { + super.setCmd(cmd); + } + + NhcMessageCmd1(String cmd, int id) { + this(cmd); + this.id = id; + } + + NhcMessageCmd1(String cmd, int id, int value1) { + this(cmd, id); + this.value1 = value1; + } + + NhcMessageCmd1(String cmd, int id, int value1, int value2, int value3) { + this(cmd, id, value1); + this.value2 = value2; + this.value3 = value3; + } + + NhcMessageCmd1 withMode(int mode) { + this.mode = mode; + return this; + } + + NhcMessageCmd1 withOverrule(int overrule) { + this.overrule = overrule; + return this; + } + + NhcMessageCmd1 withOverruletime(String overruletime) { + this.overruletime = overruletime; + return this; + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageListMap1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageListMap1.java new file mode 100644 index 0000000000000..8e199bf983e81 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageListMap1.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Class {@link NhcMessageListMap1} used as output from gson for cmd or event feedback from Niko Home Control where the + * data part is enclosed by [] and contains a list of json strings. Extends {@link NhcMessageBase1}. + *

+ * Example: {"cmd":"listactions","data":[{"id":1,"name":"Garage","type":1,"location":1,"value1":0}, + * {"id":25,"name":"Frontdoor","type":2,"location":2,"value1":0}]} + * + * @author Mark Herwege - Initial Contribution + */ +class NhcMessageListMap1 extends NhcMessageBase1 { + + private List> data = new ArrayList<>(); + + List> getData() { + return data; + } + + void setData(List> data) { + this.data = data; + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageMap1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageMap1.java new file mode 100644 index 0000000000000..47af6a8aafde8 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcMessageMap1.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class {@link NhcMessageMap1} used as output from gson for cmd or event feedback from Niko Home Control where the + * data part is a simple json string. Extends {@link NhcMessageBase1}. + *

+ * Example: {"cmd":"executeactions", "data":{"error":0}} + * + * @author Mark Herwege - Initial Contribution + */ +class NhcMessageMap1 extends NhcMessageBase1 { + + private Map data = new HashMap<>(); + + Map getData() { + return data; + } + + void setData(Map data) { + this.data = data; + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcSystemInfo1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcSystemInfo1.java new file mode 100644 index 0000000000000..9ae878586b295 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcSystemInfo1.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link NhcSystemInfo1} class represents the systeminfo Niko Home Control communication object. It contains all + * Niko Home Control system data received from the Niko Home Control controller when initializing the connection. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public final class NhcSystemInfo1 { + + private String swVersion = ""; + private String api = ""; + private String time = ""; + private String language = ""; + private String currency = ""; + private String units = ""; + private String dst = ""; + private String tz = ""; + private String lastEnergyErase = ""; + private String lastConfig = ""; + + public String getSwVersion() { + return swVersion; + } + + void setSwVersion(String swVersion) { + this.swVersion = swVersion; + } + + public String getApi() { + return api; + } + + void setApi(String api) { + this.api = api; + } + + public String getTime() { + return time; + } + + void setTime(String time) { + this.time = time; + } + + public String getLanguage() { + return language; + } + + void setLanguage(String language) { + this.language = language; + } + + public String getCurrency() { + return currency; + } + + void setCurrency(String currency) { + this.currency = currency; + } + + public String getUnits() { + return units; + } + + void setUnits(String units) { + this.units = units; + } + + public String getDst() { + return dst; + } + + void setDst(String dst) { + this.dst = dst; + } + + public String getTz() { + return tz; + } + + void setTz(String tz) { + this.tz = tz; + } + + public String getLastEnergyErase() { + return lastEnergyErase; + } + + void setLastEnergyErase(String lastEnergyErase) { + this.lastEnergyErase = lastEnergyErase; + } + + public String getLastConfig() { + return lastConfig; + } + + void setLastConfig(String lastConfig) { + this.lastConfig = lastConfig; + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcThermostat1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcThermostat1.java new file mode 100644 index 0000000000000..e0a89c5905142 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NhcThermostat1.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcThermostat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link NhcThermostat1} class represents the thermostat Niko Home Control I communication object. It contains all + * fields representing a Niko Home Control thermostat and has methods to set the thermostat in Niko Home Control and + * receive thermostat updates. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NhcThermostat1 extends NhcThermostat { + + private final Logger logger = LoggerFactory.getLogger(NhcThermostat1.class); + + NhcThermostat1(String id, String name, @Nullable String location) { + super(id, name, location); + } + + /** + * Sends thermostat mode to Niko Home Control. + * + * @param mode + */ + @Override + public void executeMode(int mode) { + logger.debug("Niko Home Control: execute thermostat mode {} for {}", mode, id); + + if (nhcComm != null) { + nhcComm.executeThermostat(id, Integer.toString(mode)); + } + } + + /** + * Sends thermostat setpoint to Niko Home Control. + * + * @param overrule temperature to overrule the setpoint in 0.1°C multiples + * @param time time duration in min for overrule + */ + @Override + public void executeOverrule(int overrule, int overruletime) { + logger.debug("Niko Home Control: execute thermostat overrule {} during {} min for {}", overrule, overruletime, + id); + + if (nhcComm != null) { + nhcComm.executeThermostat(id, overrule, overruletime); + } + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlCommunication1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlCommunication1.java new file mode 100644 index 0000000000000..0527bae8e13f6 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlCommunication1.java @@ -0,0 +1,541 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.InetAddress; +import java.net.Socket; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcControllerEvent; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcThermostat; +import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication; +import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.ActionType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; + +/** + * The {@link NikoHomeControlCommunication1} class is able to do the following tasks with Niko Home Control I + * systems: + *

    + *
  • Start and stop TCP socket connection with Niko Home Control IP-interface. + *
  • Read all setup and status information from the Niko Home Control Controller. + *
  • Execute Niko Home Control commands. + *
  • Listen to events from Niko Home Control. + *
+ * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NikoHomeControlCommunication1 extends NikoHomeControlCommunication { + + private Logger logger = LoggerFactory.getLogger(NikoHomeControlCommunication1.class); + + private final NhcSystemInfo1 systemInfo = new NhcSystemInfo1(); + private final Map locations = new ConcurrentHashMap<>(); + + private @Nullable Socket nhcSocket; + private @Nullable PrintWriter nhcOut; + private @Nullable BufferedReader nhcIn; + + private volatile boolean listenerStopped; + private volatile boolean nhcEventsRunning; + + private ScheduledExecutorService scheduler; + + // We keep only 2 gson adapters used to serialize and deserialize all messages sent and received + protected final Gson gsonOut = new Gson(); + protected Gson gsonIn; + + /** + * Constructor for Niko Home Control I communication object, manages communication with + * Niko Home Control IP-interface. + * + */ + public NikoHomeControlCommunication1(NhcControllerEvent handler, ScheduledExecutorService scheduler) { + super(handler); + this.scheduler = scheduler; + + // When we set up this object, we want to get the proper gson adapter set up once + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.registerTypeAdapter(NhcMessageBase1.class, new NikoHomeControlMessageDeserializer1()); + gsonIn = gsonBuilder.create(); + } + + @Override + public synchronized void startCommunication() { + try { + for (int i = 1; nhcEventsRunning && (i <= 5); i++) { + // the events listener thread did not finish yet, so wait max 5000ms before restarting + Thread.sleep(1000); + } + if (nhcEventsRunning) { + logger.debug("Niko Home Control: starting but previous connection still active after 5000ms"); + throw new IOException(); + } + + InetAddress addr = handler.getAddr(); + @SuppressWarnings("null") // default value specified, so cannot be null + int port = handler.getPort(); + + nhcSocket = new Socket(addr, port); + Socket socket = nhcSocket; + nhcOut = new PrintWriter(socket.getOutputStream(), true); + nhcIn = new BufferedReader(new InputStreamReader(socket.getInputStream())); + logger.debug("Niko Home Control: connected via local port {}", socket.getLocalPort()); + + // initialize all info in local fields + initialize(); + + // Start Niko Home Control event listener. This listener will act on all messages coming from + // IP-interface. + (new Thread(this::runNhcEvents)).start(); + + } catch (IOException | InterruptedException e) { + logger.warn("Niko Home Control: error initializing communication"); + stopCommunication(); + handler.controllerOffline(); + } + } + + /** + * Cleanup socket when the communication with Niko Home Control IP-interface is closed. + * + */ + @Override + public synchronized void stopCommunication() { + listenerStopped = true; + + if (nhcSocket != null) { + try { + nhcSocket.close(); + } catch (IOException ignore) { + // ignore IO Error when trying to close the socket if the intention is to close it anyway + } + } + nhcSocket = null; + + logger.debug("Niko Home Control: communication stopped"); + } + + @Override + public boolean communicationActive() { + return (nhcSocket != null); + } + + /** + * Method that handles inbound communication from Niko Home Control, to be called on a separate thread. + *

+ * The thread listens to the TCP socket opened at instantiation of the {@link NikoHomeControlCommunication} class + * and interprets all inbound json messages. It triggers state updates for active channels linked to the Niko Home + * Control actions. It is started after initialization of the communication. + * + */ + private void runNhcEvents() { + @Nullable + String nhcMessage; + + logger.debug("Niko Home Control: listening for events"); + listenerStopped = false; + nhcEventsRunning = true; + + try { + while (!listenerStopped & (nhcIn != null) & ((nhcMessage = nhcIn.readLine()) != null)) { + readMessage(nhcMessage); + } + } catch (IOException e) { + if (!listenerStopped) { + nhcEventsRunning = false; + // this is a socket error, not a communication stop triggered from outside this runnable + logger.warn("Niko Home Control: IO error in listener"); + // the IO has stopped working, so we need to close cleanly and try to restart + restartCommunication(); + return; + } + } finally { + nhcEventsRunning = false; + } + + nhcEventsRunning = false; + // this is a stop from outside the runnable, so just log it and stop + logger.debug("Niko Home Control: event listener thread stopped"); + }; + + /** + * After setting up the communication with the Niko Home Control IP-interface, send all initialization messages. + *

+ * Only at first initialization, also set the return values. Otherwise use the runnable to get updated values. + * While communication is set up for thermostats, tariff data and alarms, only info from locations and actions + * is used beyond this point in openHAB. All other elements are for future extensions. + * + * @throws IOException + */ + private void initialize() throws IOException { + sendAndReadMessage("systeminfo"); + sendAndReadMessage("startevents"); + sendAndReadMessage("listlocations"); + sendAndReadMessage("listactions"); + sendAndReadMessage("listthermostat"); + sendAndReadMessage("listthermostatHVAC"); + sendAndReadMessage("readtariffdata"); + sendAndReadMessage("getalarms"); + } + + @SuppressWarnings("null") + private void sendAndReadMessage(String command) throws IOException { + sendMessage(new NhcMessageCmd1(command)); + readMessage(nhcIn.readLine()); + } + + /** + * Called by other methods to send json cmd to Niko Home Control. + * + * @param nhcMessage + */ + @SuppressWarnings("null") + private synchronized void sendMessage(Object nhcMessage) { + String json = gsonOut.toJson(nhcMessage); + logger.debug("Niko Home Control: send json {}", json); + nhcOut.println(json); + if (nhcOut.checkError()) { + logger.warn("Niko Home Control: error sending message, trying to restart communication"); + restartCommunication(); + // retry sending after restart + logger.debug("Niko Home Control: resend json {}", json); + nhcOut.println(json); + if (nhcOut.checkError()) { + logger.warn("Niko Home Control: error resending message"); + handler.controllerOffline(); + } + } + } + + /** + * Method that interprets all feedback from Niko Home Control and calls appropriate handling methods. + * + * @param nhcMessage message read from Niko Home Control. + */ + private void readMessage(@Nullable String nhcMessage) { + logger.debug("Niko Home Control: received json {}", nhcMessage); + + try { + NhcMessageBase1 nhcMessageGson = gsonIn.fromJson(nhcMessage, NhcMessageBase1.class); + + String cmd = nhcMessageGson.getCmd(); + String event = nhcMessageGson.getEvent(); + + if ("systeminfo".equals(cmd)) { + cmdSystemInfo(((NhcMessageMap1) nhcMessageGson).getData()); + } else if ("startevents".equals(cmd)) { + cmdStartEvents(((NhcMessageMap1) nhcMessageGson).getData()); + } else if ("listlocations".equals(cmd)) { + cmdListLocations(((NhcMessageListMap1) nhcMessageGson).getData()); + } else if ("listactions".equals(cmd)) { + cmdListActions(((NhcMessageListMap1) nhcMessageGson).getData()); + } else if (("listthermostat").equals(cmd)) { + cmdListThermostat(((NhcMessageListMap1) nhcMessageGson).getData()); + } else if ("executeactions".equals(cmd)) { + cmdExecuteActions(((NhcMessageMap1) nhcMessageGson).getData()); + } else if ("executethermostat".equals(cmd)) { + cmdExecuteThermostat(((NhcMessageMap1) nhcMessageGson).getData()); + } else if ("listactions".equals(event)) { + eventListActions(((NhcMessageListMap1) nhcMessageGson).getData()); + } else if ("listthermostat".equals(event)) { + eventListThermostat(((NhcMessageListMap1) nhcMessageGson).getData()); + } else if ("getalarms".equals(event)) { + eventGetAlarms(((NhcMessageMap1) nhcMessageGson).getData()); + } else { + logger.debug("Niko Home Control: not acted on json {}", nhcMessage); + } + } catch (JsonParseException e) { + logger.debug("Niko Home Control: not acted on unsupported json {}", nhcMessage); + } + } + + private synchronized void cmdSystemInfo(Map data) { + logger.debug("Niko Home Control: systeminfo"); + + if (data.containsKey("swversion")) { + systemInfo.setSwVersion(data.get("swversion")); + } + if (data.containsKey("api")) { + systemInfo.setApi(data.get("api")); + } + if (data.containsKey("time")) { + systemInfo.setTime(data.get("time")); + } + if (data.containsKey("language")) { + systemInfo.setLanguage(data.get("language")); + } + if (data.containsKey("currency")) { + systemInfo.setCurrency(data.get("currency")); + } + if (data.containsKey("units")) { + systemInfo.setUnits(data.get("units")); + } + if (data.containsKey("DST")) { + systemInfo.setDst(data.get("DST")); + } + if (data.containsKey("TZ")) { + systemInfo.setTz(data.get("TZ")); + } + if (data.containsKey("lastenergyerase")) { + systemInfo.setLastEnergyErase(data.get("lastenergyerase")); + } + if (data.containsKey("lastconfig")) { + systemInfo.setLastConfig(data.get("lastconfig")); + } + } + + /** + * Return the object with system info as read from the Niko Home Control controller. + * + * @return the systemInfo + */ + public synchronized NhcSystemInfo1 getSystemInfo() { + return systemInfo; + } + + private void cmdStartEvents(Map data) { + int errorCode = Integer.parseInt(data.get("error")); + + if (errorCode == 0) { + logger.debug("Niko Home Control: start events success"); + } else { + logger.warn("Niko Home Control: error code {} returned on start events", errorCode); + } + } + + private void cmdListLocations(List> data) { + logger.debug("Niko Home Control: list locations"); + + locations.clear(); + + for (Map location : data) { + String id = location.get("id"); + String name = location.get("name"); + NhcLocation1 nhcLocation1 = new NhcLocation1(name); + locations.put(id, nhcLocation1); + } + } + + private void cmdListActions(List> data) { + logger.debug("Niko Home Control: list actions"); + + for (Map action : data) { + + String id = action.get("id"); + int state = Integer.parseInt(action.get("value1")); + String value2 = action.get("value2"); + int closeTime = ((value2 == null) || value2.isEmpty() ? 0 : Integer.parseInt(value2)); + String value3 = action.get("value3"); + int openTime = ((value3 == null) || value3.isEmpty() ? 0 : Integer.parseInt(value3)); + + if (!actions.containsKey(id)) { + // Initial instantiation of NhcAction class for action object + String name = action.get("name"); + String type = action.get("type"); + ActionType actionType = ActionType.GENERIC; + switch (type) { + case "0": + actionType = ActionType.TRIGGER; + break; + case "1": + actionType = ActionType.RELAY; + break; + case "2": + actionType = ActionType.DIMMER; + break; + case "4": + case "5": + actionType = ActionType.ROLLERSHUTTER; + break; + default: + logger.debug("Niko Home Control: unknown action type {} for action {}", type, id); + continue; + } + String locationId = action.get("location"); + String location = ""; + if (!locationId.isEmpty()) { + location = locations.get(locationId).getName(); + } + NhcAction nhcAction = new NhcAction1(id, name, actionType, location, scheduler); + if (actionType == ActionType.ROLLERSHUTTER) { + nhcAction.setShutterTimes(openTime, closeTime); + } + nhcAction.setNhcComm(this); + nhcAction.setState(state); + actions.put(id, nhcAction); + } else { + // Action object already exists, so only update state. + // If we would re-instantiate action, we would lose pointer back from action to thing handler that was + // set in thing handler initialize(). + actions.get(id).setState(state); + } + } + } + + private void cmdListThermostat(List> data) { + logger.debug("Niko Home Control: list thermostats"); + + for (Map thermostat : data) { + + String id = thermostat.get("id"); + int measured = Integer.parseInt(thermostat.get("measured")); + int setpoint = Integer.parseInt(thermostat.get("setpoint")); + int mode = Integer.parseInt(thermostat.get("mode")); + int overrule = Integer.parseInt(thermostat.get("overrule")); + // overruletime received in "HH:MM" format + String[] overruletimeStrings = thermostat.get("overruletime").split(":"); + int overruletime = 0; + if (overruletimeStrings.length == 2) { + overruletime = Integer.parseInt(overruletimeStrings[0]) * 60 + Integer.parseInt(overruletimeStrings[1]); + } + int ecosave = Integer.parseInt(thermostat.get("ecosave")); + + // For parity with NHC II, assume heating/cooling if thermostat is on and setpoint different from measured + int demand = (mode != 3) ? (setpoint > measured ? 1 : (setpoint < measured ? -1 : 0)) : 0; + + if (!thermostats.containsKey(id)) { + // Initial instantiation of NhcThermostat class for thermostat object + String name = thermostat.get("name"); + String locationId = thermostat.get("location"); + String location = ""; + if (!locationId.isEmpty()) { + location = locations.get(locationId).getName(); + } + NhcThermostat nhcThermostat = new NhcThermostat1(id, name, location); + nhcThermostat.updateState(measured, setpoint, mode, overrule, overruletime, ecosave, demand); + nhcThermostat.setNhcComm(this); + thermostats.put(id, nhcThermostat); + } else { + // Thermostat object already exists, so only update state. + // If we would re-instantiate thermostat, we would lose pointer back from thermostat to thing handler + // that was set in thing handler initialize(). + thermostats.get(id).updateState(measured, setpoint, mode, overrule, overruletime, ecosave, demand); + } + } + } + + private void cmdExecuteActions(Map data) { + int errorCode = Integer.parseInt(data.get("error")); + if (errorCode == 0) { + logger.debug("Niko Home Control: execute action success"); + } else { + logger.warn("Niko Home Control: error code {} returned on command execution", errorCode); + } + } + + private void cmdExecuteThermostat(Map data) { + int errorCode = Integer.parseInt(data.get("error")); + if (errorCode == 0) { + logger.debug("Niko Home Control: execute thermostats success"); + } else { + logger.warn("Niko Home Control: error code {} returned on command execution", errorCode); + } + } + + private void eventListActions(List> data) { + for (Map action : data) { + String id = action.get("id"); + if (!actions.containsKey(id)) { + logger.warn("Niko Home Control: action in controller not known {}", id); + return; + } + int state = Integer.parseInt(action.get("value1")); + logger.debug("Niko Home Control: event execute action {} with state {}", id, state); + actions.get(id).setState(state); + } + } + + private void eventListThermostat(List> data) { + for (Map thermostat : data) { + String id = thermostat.get("id"); + if (!thermostats.containsKey(id)) { + logger.warn("Niko Home Control: thermostat in controller not known {}", id); + return; + } + + int measured = Integer.parseInt(thermostat.get("measured")); + int setpoint = Integer.parseInt(thermostat.get("setpoint")); + int mode = Integer.parseInt(thermostat.get("mode")); + int overrule = Integer.parseInt(thermostat.get("overrule")); + // overruletime received in "HH:MM" format + String[] overruletimeStrings = thermostat.get("overruletime").split(":"); + int overruletime = 0; + if (overruletimeStrings.length == 2) { + overruletime = Integer.parseInt(overruletimeStrings[0]) * 60 + Integer.parseInt(overruletimeStrings[1]); + } + int ecosave = Integer.parseInt(thermostat.get("ecosave")); + + int demand = (mode != 3) ? (setpoint > measured ? 1 : (setpoint < measured ? -1 : 0)) : 0; + + logger.debug( + "Niko Home Control: event execute thermostat {} with measured {}, setpoint {}, mode {}, overrule {}, overruletime {}, ecosave {}, demand {}", + id, measured, setpoint, mode, overrule, overruletime, ecosave, demand); + thermostats.get(id).updateState(measured, setpoint, mode, overrule, overruletime, ecosave, demand); + } + } + + private void eventGetAlarms(Map data) { + int type = Integer.parseInt(data.get("type")); + String alarmText = data.get("text"); + switch (type) { + case 0: + logger.debug("Niko Home Control: alarm - {}", alarmText); + handler.alarmEvent(alarmText); + break; + case 1: + logger.debug("Niko Home Control: notice - {}", alarmText); + handler.noticeEvent(alarmText); + break; + default: + logger.debug("Niko Home Control: unexpected message type {}", type); + } + } + + @Override + public void executeAction(String actionId, String value) { + NhcMessageCmd1 nhcCmd = new NhcMessageCmd1("executeactions", Integer.parseInt(actionId), + Integer.parseInt(value)); + sendMessage(nhcCmd); + } + + @Override + public void executeThermostat(String thermostatId, String mode) { + NhcMessageCmd1 nhcCmd = new NhcMessageCmd1("executethermostat", Integer.parseInt(thermostatId)) + .withMode(Integer.parseInt(mode)); + sendMessage(nhcCmd); + } + + @Override + public void executeThermostat(String thermostatId, int overruleTemp, int overruleTime) { + String overruletimeString = String.format("%1$02d:%2$02d", overruleTime / 60, overruleTime % 60); + NhcMessageCmd1 nhcCmd = new NhcMessageCmd1("executethermostat", Integer.parseInt(thermostatId)) + .withOverrule(overruleTemp).withOverruletime(overruletimeString); + sendMessage(nhcCmd); + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlMessageDeserializer1.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlMessageDeserializer1.java new file mode 100644 index 0000000000000..a0d207469db07 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc1/NikoHomeControlMessageDeserializer1.java @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc1; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +/** + * Class {@link NikoHomeControlMessageDeserializer1} deserializes all json messages from Niko Home Control. Various json + * message formats are supported. The format is selected based on the content of the cmd and event json objects. + * + * @author Mark Herwege - Initial Contribution + * + */ +class NikoHomeControlMessageDeserializer1 implements JsonDeserializer { + + @Override + public NhcMessageBase1 deserialize(final JsonElement json, final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + final JsonObject jsonObject = json.getAsJsonObject(); + + try { + String cmd = null; + String event = null; + if (jsonObject.has("cmd")) { + cmd = jsonObject.get("cmd").getAsString(); + } + if (jsonObject.has("event")) { + event = jsonObject.get("event").getAsString(); + } + + JsonElement jsonData = null; + if (jsonObject.has("data")) { + jsonData = jsonObject.get("data"); + } + + NhcMessageBase1 message = null; + + if (jsonData != null) { + if (jsonData.isJsonObject()) { + message = new NhcMessageMap1(); + + Map data = new HashMap<>(); + for (Entry entry : jsonData.getAsJsonObject().entrySet()) { + data.put(entry.getKey(), entry.getValue().getAsString()); + } + ((NhcMessageMap1) message).setData(data); + + } else if (jsonData.isJsonArray()) { + JsonArray jsonDataArray = jsonData.getAsJsonArray(); + + message = new NhcMessageListMap1(); + + List> dataList = new ArrayList<>(); + for (int i = 0; i < jsonDataArray.size(); i++) { + JsonObject jsonDataObject = jsonDataArray.get(i).getAsJsonObject(); + + Map data = new HashMap<>(); + for (Entry entry : jsonDataObject.entrySet()) { + data.put(entry.getKey(), entry.getValue().getAsString()); + } + dataList.add(data); + } + ((NhcMessageListMap1) message).setData(dataList); + } + } + + if (message != null) { + message.setCmd(cmd); + message.setEvent(event); + } else { + throw new JsonParseException("Unexpected Json type"); + } + + return message; + + } catch (IllegalStateException | ClassCastException e) { + throw new JsonParseException("Unexpected Json type"); + } + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcAction2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcAction2.java new file mode 100644 index 0000000000000..94f3836fac769 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcAction2.java @@ -0,0 +1,131 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc2; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction; +import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.ActionType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link NhcAction2} class represents the action Niko Home Control II communication object. It contains all fields + * representing a Niko Home Control action and has methods to trigger the action in Niko Home Control and receive action + * updates. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NhcAction2 extends NhcAction { + + private final Logger logger = LoggerFactory.getLogger(NhcAction2.class); + + private volatile boolean booleanState; + private String model; + private String technology; + + NhcAction2(String id, String name, String model, String technology, ActionType type, @Nullable String location) { + super(id, name, type, location); + this.model = model; + this.technology = technology; + } + + /** + * Get on/off state of action. + *

+ * true for on, false for off + * + * @return action on/off state + */ + boolean booleanState() { + return booleanState; + } + + @Override + public int getState() { + return booleanState ? state : 0; + } + + /** + * Sets on/off state of action. + * + * @param state - boolean false for on, true for off + */ + public void setBooleanState(boolean state) { + booleanState = state; + if (getType().equals(ActionType.DIMMER)) { + if (booleanState) { + // only send stored brightness value if on + updateState(); + } else { + updateState(0); + } + } else { + if (booleanState) { + this.state = 100; + updateState(100); + } else { + this.state = 0; + updateState(0); + } + } + } + + /** + * Sets state of action. This version is used for Niko Home Control II. + * + * @param state - The allowed values depend on the action type. + * switch action: 0 or 100 + * dimmer action: between 0 and 100 + * rollershutter action: between 0 and 100 + */ + @Override + public void setState(int state) { + this.state = state; + if (booleanState) { // only send the update to the event handler if on + updateState(); + } + } + + /** + * Sends action to Niko Home Control. This version is used for Niko Home Control II, that has extra status options. + * + * @param command - The allowed values depend on the action type. + * switch action: On or Off + * dimmer action: between 0 and 100, On or Off + * rollershutter action: between 0 and 100, Up, Down or Stop + */ + @Override + public void execute(String command) { + logger.debug("Niko Home Control: execute action {} of type {} for {}", command, type, id); + + if (nhcComm != null) { + nhcComm.executeAction(id, command); + } + } + + /** + * @return model as returned from Niko Home Control + */ + public String getModel() { + return model; + } + + /** + * @return technology as returned from Niko Home Control + */ + public String getTechnology() { + return technology; + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcDevice2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcDevice2.java new file mode 100644 index 0000000000000..c3004b9020a12 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcDevice2.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc2; + +import java.util.List; + +/** + * {@link NhcDevice2} represents a Niko Home Control II device. It is used when parsing the device response json and + * when creating the state update json to send to the Connected Controller. + * + * @author Mark Herwege - Initial Contribution + */ +class NhcDevice2 { + class NhcProperty { + // fields for lights + String status; + String brightness; + String aligned; + String basicState; + // fields for motors + String action; + String position; + String moving; + // fields for thermostats + String setpointTemperature; + String program; + String overruleActive; + String overruleSetpoint; + String overruleTime; + String ecoSave; + String demand; + String ambientTemperature; + } + + class NhcTrait { + + } + + class NhcParameter { + String locationId; + String locationName; + String locationIcon; + } + + String name; + String uuid; + String technology; + String identifier; + String model; + String type; + String online; + List properties; + List traits; + List parameters; +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcLocation2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcLocation2.java new file mode 100644 index 0000000000000..25d60f13eefb7 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcLocation2.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc2; + +/** + * {@link NhcLocation2} represents a Niko Home Control II location. It is used when parsing the location response json. + * + * @author Mark Herwege - Initial Contribution + */ +class NhcLocation2 { + String name; + String uuid; + String index; + String icon; +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcMessage2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcMessage2.java new file mode 100644 index 0000000000000..a60ae4dc6b5a5 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcMessage2.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc2; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * {@link NhcMessage2} represents a Niko Home Control II message. It is used when sending messages to the Connected + * Controller or when parsing the message response json. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +class NhcMessage2 { + class NhcMessageParam { + @Nullable + List systemInfo; + @Nullable + List profiles; + @Nullable + List services; + @Nullable + List locations; + @Nullable + List devices; + @Nullable + List notifications; + @Nullable + List timeInfo; + } + + @Nullable + String method; + String errCode = ""; + String errMessage = ""; + @Nullable + List params; +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcMqttConnection2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcMqttConnection2.java new file mode 100644 index 0000000000000..cb9310d8855ca --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcMqttConnection2.java @@ -0,0 +1,358 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc2; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ResourceBundle; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.io.transport.mqtt.MqttActionCallback; +import org.eclipse.smarthome.io.transport.mqtt.MqttBrokerConnection; +import org.eclipse.smarthome.io.transport.mqtt.MqttConnectionState; +import org.eclipse.smarthome.io.transport.mqtt.MqttException; +import org.eclipse.smarthome.io.transport.mqtt.MqttMessageSubscriber; +import org.eclipse.smarthome.io.transport.mqtt.sslcontext.SSLContextProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * {@link NhcMqttConnection2} manages the MQTT connections to the Connected Controller. The initial secured connection + * is used for general system communication. This communication also communicates the profile uuid's needed as username + * for touch profile specific communication. The touch profile specific communication uses the same secure communication + * with added username and password. It allows receiving state information about specific devices and sending updates to + * specific devices. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NhcMqttConnection2 implements MqttActionCallback { + + private final Logger logger = LoggerFactory.getLogger(NhcMqttConnection2.class); + + private volatile @Nullable MqttBrokerConnection mqttPublicConnection; + private volatile @Nullable MqttBrokerConnection mqttProfileConnection; + + private volatile @Nullable CompletableFuture publicSubscribedFuture; + private volatile @Nullable CompletableFuture profileSubscribedFuture; + + private volatile @Nullable CompletableFuture publicStoppedFuture; + private volatile @Nullable CompletableFuture profileStoppedFuture; + + private Path persistenceBasePath; + private SSLContextProvider sslContextProvider; + private @Nullable String clientId; + + private volatile String cocoAddress = ""; + private volatile int port; + + NhcMqttConnection2(String clientId, String persistencePath) throws CertificateException { + persistenceBasePath = Paths.get(persistencePath).resolve("nikohomecontrol"); + // to be removed after testing + logger.debug("Niko Home Control: base persistence path set to {}", persistenceBasePath); + sslContextProvider = getSSLContext(); + this.clientId = clientId; + } + + private SSLContextProvider getSSLContext() throws CertificateException { + ResourceBundle certificatesBundle = ResourceBundle.getBundle("nikohomecontrol/certificates"); + + try { + // Load server public certificates into key store + CertificateFactory cf = CertificateFactory.getInstance("X509"); + InputStream certificateStream; + final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, null); + for (String certName : certificatesBundle.keySet()) { + certificateStream = new ByteArrayInputStream( + certificatesBundle.getString(certName).getBytes(StandardCharsets.UTF_8)); + X509Certificate certificate = (X509Certificate) cf.generateCertificate(certificateStream); + keyStore.setCertificateEntry(certName, certificate); + } + + ResourceBundle.clearCache(); + + // Create trust managers used to validate server + TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmFactory.init(keyStore); + TrustManager[] trustManagers = tmFactory.getTrustManagers(); + + // Return the SSL context provider + return new NhcSSLContextProvider2(trustManagers); + + } catch (CertificateException | KeyStoreException | NoSuchAlgorithmException | IOException e) { + logger.warn("Niko Home Control: error with SSL context creation", e.getMessage()); + throw new CertificateException("SSL context creation exception", e); + } finally { + ResourceBundle.clearCache(); + } + } + + /** + * Start a secure MQTT connection and subscribe to all topics. This is the general connection, not touch profile + * specific. + * + * @param subscriber MqttMessageSubscriber that will handle received messages + * @param cocoAddress IP Address of the Niko Connected Controller + * @param port Port for MQTT communication with the Niko Connected Controller + * @throws MqttException + */ + synchronized void startPublicConnection(MqttMessageSubscriber subscriber, String cocoAddress, int port) + throws MqttException { + if (publicStoppedFuture != null) { + try { + publicStoppedFuture.get(5000, TimeUnit.MILLISECONDS); + logger.debug("Niko Home Control: finished stopping public connection"); + } catch (InterruptedException | ExecutionException | TimeoutException ignore) { + logger.debug("Niko Home Control: error stopping public connection"); + } + publicStoppedFuture = null; + } + + logger.debug("Niko Home Control: starting public connection..."); + this.cocoAddress = cocoAddress; + this.port = port; + String clientId = this.clientId + "-public"; + MqttBrokerConnection connection = createMqttConnection(subscriber, null, null, clientId); + mqttPublicConnection = connection; + try { + if (connection.start().get(5000, TimeUnit.MILLISECONDS)) { + if (publicSubscribedFuture == null) { + publicSubscribedFuture = connection.subscribe("#", subscriber); + } + } else { + logger.debug("Niko Home Control: error connecting"); + throw new MqttException(32103); + } + } catch (InterruptedException e) { + logger.debug("Niko Home Control: public connection interrupted exception"); + throw new MqttException(0); + } catch (ExecutionException e) { + logger.debug("Niko Home Control: public connection execution exception for {}", e.getCause()); + throw new MqttException(32103); + } catch (TimeoutException e) { + logger.debug("Niko Home Control: public connection timeout exception"); + throw new MqttException(32000); + } + } + + /** + * Start a secure MQTT connection and subscribe to all topics. This is the touch profile specific connection. + * Note that {@link startConnection} must be called before this method. This method does not have cocoAddress and + * port as parameters. The class fields will already have been set by {@link startConnection}. + * + * @param subscriber MqttMessageSubscriber that will handle received messages + * @param username MQTT username that identifies the specific touch profile. It should be the uuid retrieved from + * the profile list in the general communication that matches the touch profile name. + * @param password Password for the touch profile + * @throws MqttException + */ + synchronized void startProfileConnection(MqttMessageSubscriber subscriber, String username, String password) + throws MqttException { + if (profileStoppedFuture != null) { + try { + profileStoppedFuture.get(5000, TimeUnit.MILLISECONDS); + logger.debug("Niko Home Control: finished stopping profile connection"); + } catch (InterruptedException | ExecutionException | TimeoutException ignore) { + logger.debug("Niko Home Control: error stopping profile connection"); + } + profileStoppedFuture = null; + } + + if (isProfileConnected()) { + logger.debug("Niko Home Control: profile already connected, no need to connect again"); + return; + } + + logger.debug("Niko Home Control: starting profile connection..."); + String clientId = this.clientId + "-profile"; + MqttBrokerConnection connection = createMqttConnection(subscriber, username, password, clientId); + mqttProfileConnection = connection; + try { + if (connection.start().get(5000, TimeUnit.MILLISECONDS)) { + if (profileSubscribedFuture == null) { + profileSubscribedFuture = connection.subscribe("#", subscriber); + } + } else { + logger.warn("Niko Home Control: error with profile password"); + throw new MqttException(4); + } + } catch (InterruptedException e) { + logger.debug("Niko Home Control: profile connection interrupted exception "); + throw new MqttException(0); + } catch (ExecutionException e) { + logger.debug("Niko Home Control: profile connection execution exception for {}", e.getCause()); + throw new MqttException(32103); + } catch (TimeoutException e) { + logger.debug("Niko Home Control: public connection timeout exception"); + throw new MqttException(32000); + } + } + + private MqttBrokerConnection createMqttConnection(MqttMessageSubscriber subscriber, @Nullable String username, + @Nullable String password, @Nullable String clientId) throws MqttException { + Path persistencePath = persistenceBasePath.resolve(clientId); + // to remove after testing + logger.debug("Niko Home Control: persistence path set to {}", persistencePath); + MqttBrokerConnection connection = new MqttBrokerConnection(cocoAddress, port, true, clientId); + connection.setPersistencePath(persistencePath); + connection.setSSLContextProvider(sslContextProvider); + connection.setCredentials(username, password); + connection.setQos(1); + return connection; + } + + /** + * Stop the public MQTT connection. + */ + void stopPublicConnection() { + logger.debug("Niko Home Control: stopping public connection..."); + publicStoppedFuture = stopConnection(mqttPublicConnection); + mqttPublicConnection = null; + + if (publicSubscribedFuture != null) { + publicSubscribedFuture.complete(false); + publicSubscribedFuture = null; + } + } + + /** + * Stop the profile specific MQTT connection. + */ + void stopProfileConnection() { + logger.debug("Niko Home Control: stopping profile connection..."); + profileStoppedFuture = stopConnection(mqttProfileConnection); + mqttProfileConnection = null; + + if (profileSubscribedFuture != null) { + profileSubscribedFuture.complete(false); + profileSubscribedFuture = null; + } + } + + private CompletableFuture stopConnection(@Nullable MqttBrokerConnection connection) { + if (connection != null) { + return connection.stop(); + } else { + return CompletableFuture.completedFuture(true); + } + } + + /** + * @return true if connection established and subscribed to all topics + */ + private boolean isPublicConnected() { + return isConnected(mqttPublicConnection, publicSubscribedFuture); + } + + /** + * @return true if touch profile specific connection is established and subscribed to all topics + */ + private boolean isProfileConnected() { + return isConnected(mqttProfileConnection, profileSubscribedFuture); + } + + private boolean isConnected(@Nullable MqttBrokerConnection brokerConnection, + @Nullable CompletableFuture completableFuture) { + MqttBrokerConnection connection = brokerConnection; + CompletableFuture future = completableFuture; + + if (connection != null) { + try { + if ((future != null) && future.get(5000, TimeUnit.MILLISECONDS)) { + MqttConnectionState state = connection.connectionState(); + logger.debug("Niko Home Control: connection state {} for {}", state, connection.getClientId()); + return state == MqttConnectionState.CONNECTED; + } + } catch (InterruptedException | ExecutionException | TimeoutException e) { + return false; + } + } + return false; + } + + /** + * Publish a message on the general connection. + * + * @param topic + * @param payload + * @throws MqttException + */ + void publicConnectionPublish(String topic, String payload) throws MqttException { + MqttBrokerConnection connection = mqttPublicConnection; + if (connection == null) { + logger.debug("Niko Home Control: cannot publish, no public connection"); + throw new MqttException(32104); + } + + if (isPublicConnected()) { + publish(connection, topic, payload); + } else { + logger.debug("Niko Home Control: cannot publish, not subscribed to public connection messages"); + } + } + + /** + * Publish a message on the touch profile specific connection. + * + * @param topic + * @param payload + * @throws MqttException + */ + void profileConnectionPublish(String topic, String payload) throws MqttException { + MqttBrokerConnection connection = mqttProfileConnection; + if (connection == null) { + logger.debug("Niko Home Control: cannot publish, no profile connection"); + throw new MqttException(32104); + } + + if (isProfileConnected()) { + publish(connection, topic, payload); + } else { + logger.debug("Niko Home Control: cannot publish, not subscribed to profile connection messages"); + } + } + + private void publish(MqttBrokerConnection connection, String topic, String payload) { + logger.debug("Niko Home Control: publish {}, {}", topic, payload); + connection.publish(topic, payload.getBytes()); + } + + @Override + public void onSuccess(String topic) { + logger.debug("Niko Home Control: publish succeeded {}", topic); + } + + @Override + public void onFailure(String topic, Throwable error) { + logger.debug("Niko Home Control: publish failed {}, {}", topic, error); + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcNotification2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcNotification2.java new file mode 100644 index 0000000000000..09070ee694147 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcNotification2.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc2; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * {@link NhcNotification2} represents a Niko Home Control II notification. It is used when parsing the notification + * response json. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +class NhcNotification2 { + String status = ""; + String type = ""; + String timeOccured = ""; + String uuid = ""; + String text = ""; +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcProfile2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcProfile2.java new file mode 100644 index 0000000000000..a0330fc6ff957 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcProfile2.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc2; + +/** + * {@link NhcProfile2} represents a Niko Home Control II profile. It is used when parsing the profile response json. + * + * @author Mark Herwege - Initial Contribution + * + */ +class NhcProfile2 { + + String name; + String type; + String uuid; +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcSSLContextProvider2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcSSLContextProvider2.java new file mode 100644 index 0000000000000..633894a823aeb --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcSSLContextProvider2.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc2; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.io.transport.mqtt.sslcontext.SSLContextProvider; +import org.osgi.service.cm.ConfigurationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is an implementation of the {@link SSLContextProvider}. The {@link NhcTrustManagerII} + * will be used for the SSLContext. This implementation forces a TLS 1.2 context. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NhcSSLContextProvider2 implements SSLContextProvider { + private final Logger logger = LoggerFactory.getLogger(NhcSSLContextProvider2.class); + final TrustManager[] trustManagers; + + public NhcSSLContextProvider2(TrustManager[] trustManagers) { + this.trustManagers = trustManagers; + } + + @Override + public SSLContext getContext() throws ConfigurationException { + try { + SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); + sslContext.init(null, trustManagers, new java.security.SecureRandom()); + return sslContext; + } catch (KeyManagementException | NoSuchAlgorithmException e) { + logger.warn("SSL configuration failed", e); + throw new ConfigurationException("ssl", e.getMessage()); + } + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcService2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcService2.java new file mode 100644 index 0000000000000..efb7a7b94b65b --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcService2.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc2; + +/** + * {@link NhcService2} represents a Niko Home Control II service. It is used when parsing the service response json. + * + * @author Mark Herwege - Initial Contribution + */ +class NhcService2 { + String name; + + String name() { + return name; + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcSystemInfo2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcSystemInfo2.java new file mode 100644 index 0000000000000..b1d7f57ed5083 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcSystemInfo2.java @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc2; + +import java.util.ArrayList; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +import com.google.gson.annotations.SerializedName; + +/** + * {@link NhcSystemInfo2} represents Niko Home Control II system info. It is used when parsing the systeminfo response + * json. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NhcSystemInfo2 { + class NhcSwVersion { + String nhcVersion = ""; + String cocoImage = ""; + } + + String lastConfig = ""; + String waterTariff = ""; + String electricityTariff = ""; + String gasTariff = ""; + String currency = ""; + String units = ""; + String language = ""; + @SerializedName(value = "SWversions") + ArrayList swVersions = new ArrayList<>(); + + /** + * @return the lastConfig + */ + public String getNhcVersion() { + if (swVersions.size() > 0) { + return swVersions.get(0).nhcVersion; + } else { + return ""; + } + } + + /** + * @return the lastConfig + */ + public String getCocoImage() { + if (swVersions.size() > 0) { + return swVersions.get(0).cocoImage; + } else { + return ""; + } + } + + /** + * @return the lastConfig + */ + public String getLastConfig() { + return lastConfig; + } + + /** + * @return the waterTariff + */ + public String getWaterTariff() { + return waterTariff; + } + + /** + * @return the electricityTariff + */ + public String getElectricityTariff() { + return electricityTariff; + } + + /** + * @return the gasTariff + */ + public String getGasTariff() { + return gasTariff; + } + + /** + * @return the currency + */ + public String getCurrency() { + return currency; + } + + /** + * @return the units + */ + public String getUnits() { + return units; + } + + /** + * @return the language + */ + public String getLanguage() { + return language; + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcThermostat2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcThermostat2.java new file mode 100644 index 0000000000000..a21db8e4c502b --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcThermostat2.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc2; + +import static org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.THERMOSTATMODES; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcThermostat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link NhcThermostat2} class represents the thermostat Niko Home Control II communication object. It contains all + * fields representing a Niko Home Control thermostat and has methods to set the thermostat in Niko Home Control and + * receive thermostat updates. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NhcThermostat2 extends NhcThermostat { + + private final Logger logger = LoggerFactory.getLogger(NhcThermostat2.class); + + protected NhcThermostat2(String id, String name, @Nullable String location) { + super(id, name, location); + } + + @Override + public void executeMode(int mode) { + logger.debug("Niko Home Control: execute thermostat mode {} for {}", mode, id); + + String program = THERMOSTATMODES[mode]; + + if (nhcComm != null) { + nhcComm.executeThermostat(id, program); + } + } + + @Override + public void executeOverrule(int overrule, int overruletime) { + logger.debug("Niko Home Control: execute thermostat overrule {} during {} min for {}", overrule, overruletime, + id); + + if (nhcComm != null) { + nhcComm.executeThermostat(id, overrule / 10, overruletime); + } + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcTimeInfo2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcTimeInfo2.java new file mode 100644 index 0000000000000..846a115b78ffa --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NhcTimeInfo2.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc2; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +import com.google.gson.annotations.SerializedName; + +/** + * {@link NhcTimeInfo2} represents Niko Home Control II timeinfo. It is used when parsing the timeinfo response json. + * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NhcTimeInfo2 { + @SerializedName(value = "GMTOffset") + String gmtOffset = ""; + String timeZone = ""; + String isDST = ""; + @SerializedName(value = "UTCTime") + String utcTime = ""; + + /** + * @return the gMTOffset + */ + public String getGMTOffset() { + return gmtOffset; + } + + /** + * @return the timeZone + */ + public String getTimeZone() { + return timeZone; + } + + /** + * @return the isDST + */ + public String getIsDst() { + return isDST; + } + + /** + * @return the uTCTime + */ + public String getUTCTime() { + return utcTime; + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NikoHomeControlCommunication2.java b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NikoHomeControlCommunication2.java new file mode 100644 index 0000000000000..c3801866bae8d --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/java/org/openhab/binding/nikohomecontrol/internal/protocol/nhc2/NikoHomeControlCommunication2.java @@ -0,0 +1,811 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.nikohomecontrol.internal.protocol.nhc2; + +import static org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.*; + +import java.lang.reflect.Type; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.io.transport.mqtt.MqttException; +import org.eclipse.smarthome.io.transport.mqtt.MqttMessageSubscriber; +import org.openhab.binding.nikohomecontrol.internal.protocol.NhcControllerEvent; +import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication; +import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlConstants.ActionType; +import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcDevice2.NhcProperty; +import org.openhab.binding.nikohomecontrol.internal.protocol.nhc2.NhcMessage2.NhcMessageParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; + +/** + * The {@link NikoHomeControlCommunication2} class is able to do the following tasks with Niko Home Control II + * systems: + *

    + *
  • Start and stop MQTT connection with Niko Home Control II Connected Controller. + *
  • Read all setup and status information from the Niko Home Control Controller. + *
  • Execute Niko Home Control commands. + *
  • Listen for events from Niko Home Control. + *
+ * + * @author Mark Herwege - Initial Contribution + */ +@NonNullByDefault +public class NikoHomeControlCommunication2 extends NikoHomeControlCommunication implements MqttMessageSubscriber { + + private final Logger logger = LoggerFactory.getLogger(NikoHomeControlCommunication2.class); + + private final NhcMqttConnection2 mqttConnection; + + private final List profiles = new CopyOnWriteArrayList<>(); + private final List services = new CopyOnWriteArrayList<>(); + private final List locations = new CopyOnWriteArrayList<>(); + + private volatile @Nullable NhcSystemInfo2 nhcSystemInfo; + private volatile @Nullable NhcTimeInfo2 nhcTimeInfo; + + private volatile String profileUuid = ""; + + private volatile @Nullable CompletableFuture communicationStarted; + + private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create(); + + /** + * Constructor for Niko Home Control communication object, manages communication with + * Niko Home Control II Connected Controller. + * + * @throws CertificateException when the SSL context for MQTT communication cannot be created + * @throws UnknownHostException when the IP address is not provided + * + */ + public NikoHomeControlCommunication2(NhcControllerEvent handler, String clientId, String persistencePath) + throws CertificateException { + super(handler); + mqttConnection = new NhcMqttConnection2(clientId, persistencePath); + } + + @Override + public synchronized void startCommunication() { + communicationStarted = new CompletableFuture<>(); + + startPublicCommunication(); + } + + /** + * This method executes the first part of the communication start. A public connection (no username or password, but + * secured with SSL) will be started and the controller will be queried for existing capabilities and profiles. + */ + private void startPublicCommunication() { + InetAddress addr = handler.getAddr(); + if (addr == null) { + logger.warn("Niko Home Control: IP address cannot be empty"); + stopCommunication(); + return; + } + String addrString = addr.getHostAddress(); + @SuppressWarnings("null") // default provided, so cannot be null + int port = handler.getPort(); + logger.debug("Niko Home Control: initializing for mqtt connection to CoCo on {}:{}", addrString, port); + + try { + mqttConnection.startPublicConnection(this, addrString, port); + initializePublic(); + } catch (MqttException e) { + logger.debug("Niko Home Control: error in mqtt communication"); + stopCommunication(); + } + } + + /** + * This method executes the second part of the communication start. After the list of profiles are received on the + * public MQTT connection, this method should be called to stop the general connection and start a touch profile + * specific MQTT connection. This will allow receiving state information and updating state of devices. + */ + private void startProfileCommunication() { + String profile = handler.getProfile(); + String password = handler.getPassword(); + + if (profile.isEmpty()) { + logger.warn("Niko Home Control: no profile set"); + stopCommunication(); + return; + } + try { + profileUuid = profiles.stream().filter(p -> profile.equals(p.name)).findFirst().get().uuid; + } catch (NoSuchElementException e) { + logger.warn("Niko Home Control: profile '{}' does not match a profile in the controller", profile); + stopCommunication(); + return; + } + + if (password.isEmpty()) { + logger.warn("Niko Home Control: password for profile cannot be empty"); + stopCommunication(); + return; + } + + mqttConnection.stopPublicConnection(); + try { + mqttConnection.startProfileConnection(this, profileUuid, password); + initializeProfile(); + } catch (MqttException e) { + logger.warn("Niko Home Control: error in mqtt communication"); + stopCommunication(); + } + } + + @Override + public synchronized void stopCommunication() { + if (communicationStarted != null) { + communicationStarted.complete(false); + } + communicationStarted = null; + mqttConnection.stopPublicConnection(); + mqttConnection.stopProfileConnection(); + } + + @Override + public boolean communicationActive() { + CompletableFuture started = communicationStarted; + if (started == null) { + return false; + } + try { + // Wait until we received all devices info to confirm we are active. + return started.get(5000, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + logger.debug("Niko Home Control: exception waiting for connection start"); + return false; + } + } + + /** + * After setting up the communication with the Niko Home Control Connected Controller, send all initialization + * messages. + * + */ + private void initializePublic() throws MqttException { + NhcMessage2 message = new NhcMessage2(); + + message.method = "systeminfo.publish"; + mqttConnection.publicConnectionPublish("public/system/cmd", gson.toJson(message)); + + message.method = "profiles.list"; + mqttConnection.publicConnectionPublish("public/authentication/cmd", gson.toJson(message)); + } + + /** + * After setting up the profile communication with the Niko Home Control Connected Controller, send all profile + * specific initialization messages. + * + */ + private void initializeProfile() throws MqttException { + NhcMessage2 message = new NhcMessage2(); + + message.method = "services.list"; + mqttConnection.profileConnectionPublish(profileUuid + "/authentication/cmd", gson.toJson(message)); + + message.method = "devices.list"; + mqttConnection.profileConnectionPublish(profileUuid + "/control/devices/cmd", gson.toJson(message)); + + message.method = "locations.list"; + mqttConnection.profileConnectionPublish(profileUuid + "/control/locations/cmd", gson.toJson(message)); + + message.method = "notifications.list"; + mqttConnection.profileConnectionPublish(profileUuid + "/notification/cmd", gson.toJson(message)); + } + + private void connectionLost() { + logger.debug("Niko Home Control: connection lost"); + stopCommunication(); + handler.controllerOffline(); + } + + private void timePublishEvt(String response) { + Type messageType = new TypeToken() { + }.getType(); + List timeInfo = null; + try { + NhcMessage2 message = gson.fromJson(response, messageType); + if (message.params != null) { + timeInfo = message.params.stream().filter(p -> (p.timeInfo != null)).findFirst().get().timeInfo; + } + } catch (JsonSyntaxException e) { + logger.debug("Niko Home Control: unexpected json {}", response); + } catch (NoSuchElementException ignore) { + // Ignore if timeInfo not present in response, this should not happen in a timeInfo response + } + if (timeInfo != null) { + nhcTimeInfo = timeInfo.get(0); + } + } + + private void systeminfoPublishRsp(String response) { + Type messageType = new TypeToken() { + }.getType(); + List systemInfo = null; + try { + NhcMessage2 message = gson.fromJson(response, messageType); + if (message.params != null) { + systemInfo = message.params.stream().filter(p -> (p.systemInfo != null)).findFirst().get().systemInfo; + } + } catch (JsonSyntaxException e) { + logger.debug("Niko Home Control: unexpected json {}", response); + } catch (NoSuchElementException ignore) { + // Ignore if systemInfo not present in response, this should not happen in a systemInfo response + } + if (systemInfo != null) { + nhcSystemInfo = systemInfo.get(0); + } + } + + private void profilesListRsp(String response) { + Type messageType = new TypeToken() { + }.getType(); + List profileList = null; + try { + NhcMessage2 message = gson.fromJson(response, messageType); + if (message.params != null) { + profileList = message.params.stream().filter(p -> (p.profiles != null)).findFirst().get().profiles; + } + } catch (JsonSyntaxException e) { + logger.debug("Niko Home Control: unexpected json {}", response); + } catch (NoSuchElementException ignore) { + // Ignore if profiles not present in response, this should not happen in a profiles response + } + profiles.clear(); + if (profileList != null) { + profiles.addAll(profileList); + } + } + + private void servicesListRsp(String response) { + Type messageType = new TypeToken() { + }.getType(); + List serviceList = null; + try { + NhcMessage2 message = gson.fromJson(response, messageType); + if (message.params != null) { + serviceList = message.params.stream().filter(p -> (p.services != null)).findFirst().get().services; + } + } catch (JsonSyntaxException e) { + logger.debug("Niko Home Control: unexpected json {}", response); + } catch (NoSuchElementException ignore) { + // Ignore if services not present in response, this should not happen in a services response + } + services.clear(); + if (serviceList != null) { + services.addAll(serviceList); + } + } + + private void locationsListRsp(String response) { + Type messageType = new TypeToken() { + }.getType(); + List locationList = null; + try { + NhcMessage2 message = gson.fromJson(response, messageType); + if (message.params != null) { + locationList = message.params.stream().filter(p -> (p.locations != null)).findFirst().get().locations; + } + } catch (JsonSyntaxException e) { + logger.debug("Niko Home Control: unexpected json {}", response); + } catch (NoSuchElementException ignore) { + // Ignore if locations not present in response, this should not happen in a locations response + } + locations.clear(); + if (locationList != null) { + locations.addAll(locationList); + } + } + + private void devicesListRsp(String response) { + Type messageType = new TypeToken() { + }.getType(); + List deviceList = null; + try { + NhcMessage2 message = gson.fromJson(response, messageType); + if (message.params != null) { + deviceList = message.params.stream().filter(p -> (p.devices != null)).findFirst().get().devices; + } + } catch (JsonSyntaxException e) { + logger.debug("Niko Home Control: unexpected json {}", response); + } catch (NoSuchElementException ignore) { + // Ignore if devices not present in response, this should not happen in a devices response + } + if (deviceList == null) { + return; + } + + for (NhcDevice2 device : deviceList) { + String location; + try { + location = device.parameters.stream().filter(p -> (p.locationId != null)).findFirst() + .get().locationName; + } catch (NoSuchElementException e) { + location = null; + } + + if ("action".equals(device.type)) { + ActionType actionType = ActionType.GENERIC; + switch (device.model) { + case "generic": + case "pir": + case "simulation": + case "comfort": + case "alarms": + case "alloff": + actionType = ActionType.TRIGGER; + break; + case "light": + case "socket": + case "switched-generic": + actionType = ActionType.RELAY; + break; + case "dimmer": + actionType = ActionType.DIMMER; + break; + case "rolldownshutter": + case "sunblind": + case "venetianblind": + actionType = ActionType.ROLLERSHUTTER; + break; + } + + if (!actions.containsKey(device.uuid)) { + logger.debug("Niko Home Control: adding action device {}, {}", device.uuid, device.name); + + NhcAction2 nhcAction = new NhcAction2(device.uuid, device.name, device.model, device.technology, + actionType, location); + nhcAction.setNhcComm(this); + actions.put(device.uuid, nhcAction); + } + + updateActionState((NhcAction2) actions.get(device.uuid), device); + } else if ("thermostat".equals(device.type)) { + if (!thermostats.containsKey(device.uuid)) { + logger.debug("Niko Home Control: adding thermostatdevice {}, {}", device.uuid, device.name); + + NhcThermostat2 nhcThermostat = new NhcThermostat2(device.uuid, device.name, location); + nhcThermostat.setNhcComm(this); + thermostats.put(device.uuid, nhcThermostat); + } + + updateThermostatState((NhcThermostat2) thermostats.get(device.uuid), device); + } else { + logger.debug("Niko Home Control: device type {} not supported for {}, {}", device.type, device.uuid, + device.name); + } + } + + // Once a devices list response is received, we know the communication is fully started. + logger.debug("Niko Home Control: Communication start complete."); + handler.controllerOnline(); + if (communicationStarted != null) { + communicationStarted.complete(true); + } + } + + private void devicesStatusEvt(String response) { + Type messageType = new TypeToken() { + }.getType(); + List deviceList = null; + try { + NhcMessage2 message = gson.fromJson(response, messageType); + if (message.params != null) { + deviceList = message.params.stream().filter(p -> (p.devices != null)).findFirst().get().devices; + } + } catch (JsonSyntaxException e) { + logger.debug("Niko Home Control: unexpected json {}", response); + } catch (NoSuchElementException ignore) { + // Ignore if devices not present in response, this should not happen in a devices event + } + if (deviceList == null) { + return; + } + + for (NhcDevice2 device : deviceList) { + if (actions.containsKey(device.uuid)) { + updateActionState((NhcAction2) actions.get(device.uuid), device); + } else if (thermostats.containsKey(device.uuid)) { + updateThermostatState((NhcThermostat2) thermostats.get(device.uuid), device); + } + } + } + + private void notificationEvt(String response) { + Type messageType = new TypeToken() { + }.getType(); + List notificationList = null; + try { + NhcMessage2 message = gson.fromJson(response, messageType); + if (message.params != null) { + notificationList = message.params.stream().filter(p -> (p.notifications != null)).findFirst() + .get().notifications; + } + } catch (JsonSyntaxException e) { + logger.debug("Niko Home Control: unexpected json {}", response); + } catch (NoSuchElementException ignore) { + // Ignore if notifications not present in response, this should not happen in a notifications event + } + logger.debug("Niko Home Control: notifications {}", notificationList); + if (notificationList == null) { + return; + } + + for (NhcNotification2 notification : notificationList) { + if ("new".equals(notification.status)) { + String alarmText = notification.text; + switch (notification.type) { + case "alarm": + handler.alarmEvent(alarmText); + break; + case "notification": + handler.noticeEvent(alarmText); + break; + default: + logger.debug("Niko Home Control: unexpected message type {}", notification.type); + } + } + } + } + + private void updateActionState(NhcAction2 action, NhcDevice2 device) { + if (action.getType() == ActionType.ROLLERSHUTTER) { + updateRollershutterState(action, device); + } else { + updateLightState(action, device); + } + } + + private void updateLightState(NhcAction2 action, NhcDevice2 device) { + Optional statusProperty = device.properties.stream().filter(p -> (p.status != null)).findFirst(); + Optional dimmerProperty = device.properties.stream().filter(p -> (p.brightness != null)) + .findFirst(); + Optional basicStateProperty = device.properties.stream().filter(p -> (p.basicState != null)) + .findFirst(); + + String booleanState = null; + if (statusProperty.isPresent()) { + booleanState = statusProperty.get().status; + } else if (basicStateProperty.isPresent()) { + booleanState = basicStateProperty.get().basicState; + } + + if (booleanState != null) { + if (NHCON.equals(booleanState)) { + action.setBooleanState(true); + logger.debug("Niko Home Control: setting action {} internally to ON", action.getId()); + } else if (NHCOFF.equals(booleanState)) { + action.setBooleanState(false); + logger.debug("Niko Home Control: setting action {} internally to OFF", action.getId()); + } + } + + if (dimmerProperty.isPresent()) { + action.setState(Integer.parseInt(dimmerProperty.get().brightness)); + logger.debug("Niko Home Control: setting action {} internally to {}", action.getId(), + dimmerProperty.get().brightness); + } + } + + private void updateRollershutterState(NhcAction2 action, NhcDevice2 device) { + Optional positionProperty = device.properties.stream().filter(p -> (p.position != null)) + .findFirst(); + Optional movingProperty = device.properties.stream().filter(p -> (p.moving != null)).findFirst(); + + if (!(movingProperty.isPresent() && Boolean.parseBoolean(movingProperty.get().moving)) + && positionProperty.isPresent()) { + action.setState(Integer.parseInt(positionProperty.get().position)); + logger.debug("Niko Home Control: setting action {} internally to {}", action.getId(), + positionProperty.get().position); + } + } + + private void updateThermostatState(NhcThermostat2 thermostat, NhcDevice2 device) { + Optional overruleActiveProperty = device.properties.stream() + .filter(p -> (p.overruleActive != null)).findFirst(); + Optional overruleSetpointProperty = device.properties.stream() + .filter(p -> (p.overruleSetpoint != null)).findFirst(); + Optional overruleTimeProperty = device.properties.stream().filter(p -> (p.overruleTime != null)) + .findFirst(); + Optional programProperty = device.properties.stream().filter(p -> (p.program != null)).findFirst(); + + Optional setpointTemperatureProperty = device.properties.stream() + .filter(p -> (p.setpointTemperature != null)).findFirst(); + Optional ecoSaveProperty = device.properties.stream().filter(p -> (p.ecoSave != null)).findFirst(); + Optional ambientTemperatureProperty = device.properties.stream() + .filter(p -> (p.ambientTemperature != null)).findFirst(); + + Optional demandProperty = device.properties.stream().filter(p -> (p.demand != null)).findFirst(); + + String modeString = programProperty.isPresent() ? programProperty.get().program : ""; + int mode = IntStream.range(0, THERMOSTATMODES.length).filter(i -> THERMOSTATMODES[i].equals(modeString)) + .findFirst().orElse(thermostat.getMode()); + + int measured = ambientTemperatureProperty.isPresent() + ? Math.round(Float.parseFloat(ambientTemperatureProperty.get().ambientTemperature) * 10) + : thermostat.getMeasured(); + int setpoint = setpointTemperatureProperty.isPresent() + ? Math.round(Float.parseFloat(setpointTemperatureProperty.get().setpointTemperature) * 10) + : thermostat.getSetpoint(); + + int overrule = 0; + int overruletime = 0; + if (overruleActiveProperty.isPresent() && "True".equals(overruleActiveProperty.get().overruleActive)) { + overrule = overruleSetpointProperty.isPresent() + ? Math.round(Float.parseFloat(overruleSetpointProperty.get().overruleSetpoint) * 10) + : 0; + overruletime = overruleTimeProperty.isPresent() ? Integer.parseInt(overruleTimeProperty.get().overruleTime) + : 0; + } + + int ecosave = ecoSaveProperty.isPresent() ? ("True".equals(ecoSaveProperty.get().ecoSave) ? 1 : 0) + : thermostat.getEcosave(); + + int demand = thermostat.getDemand(); + if (demandProperty.isPresent()) { + switch (demandProperty.get().demand) { + case "None": + demand = 0; + break; + case "Heating": + demand = 1; + break; + case "Cooling": + demand = -1; + break; + } + } + + logger.debug( + "Niko Home Control: setting thermostat {} with measured {}, setpoint {}, mode {}, overrule {}, overruletime {}, ecosave {}, demand {}", + thermostat.getId(), measured, setpoint, mode, overrule, overruletime, ecosave, demand); + thermostat.updateState(measured, setpoint, mode, overrule, overruletime, ecosave, demand); + } + + @Override + public void executeAction(String actionId, String value) { + NhcMessage2 message = new NhcMessage2(); + + message.method = "devices.control"; + ArrayList params = new ArrayList<>(); + NhcMessageParam param = message.new NhcMessageParam(); + params.add(param); + message.params = params; + ArrayList devices = new ArrayList<>(); + NhcDevice2 device = new NhcDevice2(); + devices.add(device); + param.devices = devices; + device.uuid = actionId; + device.properties = new ArrayList<>(); + NhcProperty property = device.new NhcProperty(); + device.properties.add(property); + + NhcAction2 action = (NhcAction2) actions.get(actionId); + + switch (action.getType()) { + case GENERIC: + case TRIGGER: + property.basicState = NHCTRIGGERED; + break; + case RELAY: + property.status = value; + break; + case DIMMER: + if (NHCON.equals(value)) { + action.setBooleanState(true); // this will trigger sending the stored brightness value event out + property.status = value; + } else if (NHCOFF.equals(value)) { + property.status = value; + } else { + // If the light is off, turn the light on before sending the brightness value, needs to happen + // in 2 separate messages. + if (!action.booleanState()) { + executeAction(actionId, NHCON); + } + property.brightness = value; + } + break; + case ROLLERSHUTTER: + if (NHCSTOP.equals(value)) { + property.action = value; + } else if (NHCUP.equals(value)) { + property.position = "100"; + } else if (NHCDOWN.equals(value)) { + property.position = "0"; + } else { + int position = 100 - Integer.parseInt(value); + property.position = String.valueOf(position); + } + break; + } + + String topic = profileUuid + "/control/devices/cmd"; + String gsonMessage = gson.toJson(message); + sendDeviceMessage(topic, gsonMessage); + } + + @Override + public void executeThermostat(String thermostatId, String mode) { + NhcMessage2 message = new NhcMessage2(); + + message.method = "devices.control"; + ArrayList params = new ArrayList<>(); + NhcMessageParam param = message.new NhcMessageParam(); + params.add(param); + message.params = params; + ArrayList devices = new ArrayList<>(); + NhcDevice2 device = new NhcDevice2(); + devices.add(device); + param.devices = devices; + device.uuid = thermostatId; + device.properties = new ArrayList<>(); + + NhcProperty overruleActiveProp = device.new NhcProperty(); + device.properties.add(overruleActiveProp); + overruleActiveProp.overruleActive = "False"; + + NhcProperty program = device.new NhcProperty(); + device.properties.add(program); + program.program = mode; + + String topic = profileUuid + "/control/devices/cmd"; + String gsonMessage = gson.toJson(message); + sendDeviceMessage(topic, gsonMessage); + } + + @Override + public void executeThermostat(String thermostatId, int overruleTemp, int overruleTime) { + NhcMessage2 message = new NhcMessage2(); + + message.method = "devices.control"; + ArrayList params = new ArrayList<>(); + NhcMessageParam param = message.new NhcMessageParam(); + params.add(param); + message.params = params; + ArrayList devices = new ArrayList<>(); + NhcDevice2 device = new NhcDevice2(); + devices.add(device); + param.devices = devices; + device.uuid = thermostatId; + device.properties = new ArrayList<>(); + + if (overruleTime > 0) { + NhcProperty overruleActiveProp = device.new NhcProperty(); + overruleActiveProp.overruleActive = "True"; + device.properties.add(overruleActiveProp); + + NhcProperty overruleSetpointProp = device.new NhcProperty(); + overruleSetpointProp.overruleSetpoint = String.valueOf(overruleTemp); + device.properties.add(overruleSetpointProp); + + NhcProperty overruleTimeProp = device.new NhcProperty(); + overruleTimeProp.overruleTime = String.valueOf(overruleTime); + device.properties.add(overruleTimeProp); + } else { + NhcProperty overruleActiveProp = device.new NhcProperty(); + overruleActiveProp.overruleActive = "False"; + device.properties.add(overruleActiveProp); + } + + String topic = profileUuid + "/control/devices/cmd"; + String gsonMessage = gson.toJson(message); + sendDeviceMessage(topic, gsonMessage); + } + + private void sendDeviceMessage(String topic, String gsonMessage) { + try { + mqttConnection.profileConnectionPublish(topic, gsonMessage); + + } catch (MqttException e) { + logger.warn("Niko Home Control: sending command failed, trying to restart communication"); + restartCommunication(); + // retry sending after restart + try { + if (communicationActive()) { + mqttConnection.profileConnectionPublish(topic, gsonMessage); + } else { + logger.warn("Niko Home Control: failed to restart communication"); + connectionLost(); + } + } catch (MqttException e1) { + logger.warn("Niko Home Control: error resending thermostat command"); + connectionLost(); + } + } + } + + @Override + public void processMessage(String topic, byte[] payload) { + String message = new String(payload); + if ("public/system/evt".equals(topic)) { + timePublishEvt(message); + } else if ((profileUuid + "/system/evt").equals(topic)) { + // ignore + } else if ("public/system/rsp".equals(topic)) { + logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); + systeminfoPublishRsp(message); + } else if ("public/authentication/rsp".equals(topic)) { + logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); + profilesListRsp(message); + startProfileCommunication(); + } else if ((profileUuid + "/notification/evt").equals(topic)) { + logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); + notificationEvt(message); + } else if ((profileUuid + "/control/devices/evt").equals(topic)) { + logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); + devicesStatusEvt(message); + } else if ((profileUuid + "/control/devices/rsp").equals(topic)) { + logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); + devicesListRsp(message); + } else if ((profileUuid + "/authentication/rsp").equals(topic)) { + logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); + servicesListRsp(message); + } else if ((profileUuid + "/locations/rsp").equals(topic)) { + logger.debug("Niko Home Control: received topic {}, payload {}", topic, message); + locationsListRsp(message); + } else { + logger.trace("Niko Home Control: not acted on received message topic {}, payload {}", topic, message); + } + } + + /** + * @return system info retrieved from Connected Controller + */ + public NhcSystemInfo2 getSystemInfo() { + NhcSystemInfo2 systemInfo = nhcSystemInfo; + if (systemInfo == null) { + systemInfo = new NhcSystemInfo2(); + } + return systemInfo; + } + + /** + * @return time info retrieved from Connected Controller + */ + public NhcTimeInfo2 getTimeInfo() { + NhcTimeInfo2 timeInfo = nhcTimeInfo; + if (timeInfo == null) { + timeInfo = new NhcTimeInfo2(); + } + return timeInfo; + } + + /** + * @return comma separated list of services retrieved from Connected Controller + */ + public String getServices() { + return services.stream().map(NhcService2::name).collect(Collectors.joining(", ")); + } +} diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.nikohomecontrol/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..d0fe52a933857 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,9 @@ + + + + Niko Home Control Binding + This is the binding for the Niko Home Control system + Mark Herwege + diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.nikohomecontrol/src/main/resources/ESH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..417607f649e21 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/resources/ESH-INF/thing/thing-types.xml @@ -0,0 +1,261 @@ + + + + + + This bridge represents a Niko Home Control I IP-interface + + + + + + + + IP Address of Niko Home Control IP-interface + false + network-address + + + + Port to communicate with Niko Home Control IP-interface, default 8000 + 8000 + true + + + + Refresh interval for connection with Niko Home Control IP-interface (min), default 300. If set to 0 or left empty, no refresh will be scheduled + 300 + true + + + + + + This bridge represents a Niko Home Control II Connected Controller + + + + + + + + IP Address of Connected Controller + false + network-address + + + + Port for secure MQTT communication with Connected Controller, default 8883 + 8883 + true + + + + Touch profile for Niko Home Control II + + + + Password for Niko Home Control II touch profile, should not be empty + password + + + + Refresh interval for connection with Connected Controller (min), default 300. If set to 0 or left empty, no refresh will be scheduled + 300 + true + + + + + + + + + + + Pushbutton type action in Niko Home Control + + + + + + + Niko Home Control action ID + false + + + + + + + + + + On/Off type action in Niko Home Control + + + + + + + Niko Home Control action ID + false + + + + + + + + + + Dimmer type action in Niko Home Control + + + + + + + Niko Home Control action ID + false + + + + Step value used for increase/decrease of dimmer brightness, default 10% + 10 + true + + + + + + + + + + Rollershutter type action in Niko Home Control + + + + + + + Niko Home Control action ID + false + + + + + + + + + Thermostat in the Niko Home Control system + + + + + + + + + + Niko Home Control Thermostat ID + false + + + + Default overrule duration in minutes when an overrule temperature is set without providing overrule time, 60 minutes by default + 60 + true + + + + + + Switch + + Pushbutton control for action in Niko Home Control + Switch + veto + + + Switch + + Switch control for action in Niko Home Control + Switch + + + Dimmer + + Brightness control for dimmer action in Niko Home Control + DimmableLight + + + Rollershutter + + Rollershutter control for rollershutter action in Niko Home Control + Blinds + + + + Number:Temperature + + Temperature measured by thermostat + Temperature + + CurrentTemperature + + + + + Number:Temperature + + Setpoint temperature of thermostat + Temperature + + TargetTemperature + + + + + Number + + Time duration for overruling thermostat target temperature in min. + Number + + + + Number + + Thermostat mode + Number + + + + + + + + + + + + + + + + trigger + + Alarm from Niko Home Control + + + trigger + + Notice from Niko Home Control + + + diff --git a/bundles/org.openhab.binding.nikohomecontrol/src/main/resources/nikohomecontrol/certificates.properties b/bundles/org.openhab.binding.nikohomecontrol/src/main/resources/nikohomecontrol/certificates.properties new file mode 100644 index 0000000000000..50e3412511ca1 --- /dev/null +++ b/bundles/org.openhab.binding.nikohomecontrol/src/main/resources/nikohomecontrol/certificates.properties @@ -0,0 +1,136 @@ +newintermediate-certificate = -----BEGIN CERTIFICATE-----\r\n\ + MIIF7jCCA9agAwIBAgICEA4wDQYJKoZIhvcNAQELBQAwgYExCzAJBgNVBAYTAkJF\ + MRgwFgYDVQQIDA9Pb3N0LVZsYWFuZGVyZW4xFTATBgNVBAcMDFNpbnQtTmlrbGFh\ + czENMAsGA1UECgwETmlrbzEVMBMGA1UECwwMSG9tZSBDb250cm9sMRswGQYJKoZI\ + hvcNAQkBFgxpbmZvQG5pa28uYmUwHhcNNzAwMTAxMDAwMDAwWhcNMzcwMTAxMDAw\ + MDAwWjCBiTELMAkGA1UEBhMCQkUxGDAWBgNVBAgMD09vc3QtVmxhYW5kZXJlbjEN\ + MAsGA1UECgwETmlrbzEVMBMGA1UECwwMSG9tZSBDb250cm9sMR0wGwYDVQQDDBRO\ + aWtvIEludGVybWVkaWF0ZSBDQTEbMBkGCSqGSIb3DQEJARYMaW5mb0BuaWtvLmJl\ + MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuSDk7ob45c78+b/SSfMl\ + TOY82nJ/RQjNrIFRTdMUwrt18GMz2TDXJnaz+N5bkxC4L2CkPWZE3eOr3l10al+r\ + hZ+m55AhZZcoHHN9vFIul8pw86mVAY8uxr3pM72/270L9yJ+Ra8d+qwM6+L8zWUc\ + S/RoGokyutkzfuC20tC1u8IOsUgNHuHwh2dWA0OrI+GWZ6k+Mr/Ojsj7YL5xIrOK\ + eZHIN0jy6/hSnWDN1GTxIKpiKCOoFUGAj5Wwpf3Z3mpmSIvAG048fczX2ZdcjCcg\ + Iaiw5yeK77G5iMYtzPxJwZRKBVfo+Kf0sPn7QSOJwMJZ8KRgO1KAysuCtspUsemg\ + mA0I0pzXOwFJI5dIquMj/2vO+JFB+T8XeoPdeaOc9RJA5Wj2ENIjHTu/W86ElJwU\ + 8Aw3Z6Gc63mto4FGkM7kN7VQyQVX7EbTmuMC5gHDltrYpsnlKz2d0pShBg++x6IY\ + Hd321i8HGqg7NyfG6jZpISQSKKzPZKG++9l2/w7eQ8qJYpGZ6zqiUphygKdx9q2s\ + sP8AUbKYZzRBK0u4XDwtJtYAaNw5arKGH4qLHn+EEYTruC1fo9SAGqkPoACd0Oze\ + 3w8tjsHwwzD8NXJzEpnUyjDmtvi1VfUzKlc82CrNW6iePzR0lGzEQtVBI4rfqbfJ\ + RvQ9Hq9HaCrX1P6M5s/ZfisCAwEAAaNmMGQwHQYDVR0OBBYEFHoJvtyYZ7/j4nDe\ + kGT2q+xKCWE/MB8GA1UdIwQYMBaAFOa0NGf2t36uYioWVapmm073eJBZMBIGA1Ud\ + EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IC\ + AQBsl6Y9A5qhTMQHL+Z7S0+t1xjRuzYtzgddEGIWK9nsx1RmH6NDv4KoziaUX3Tx\ + YMlOFqx6Q+P0Tyl+x+wbPC3stVa8h4hmr5mOd1ph15hecAK0VbcWeRfMAEqg9o47\ + 6MheSjXwwIp/oRx3YCX3MhiBaWj1IgLElOA99Xtlksk6VsR6QVoSmTKUNDR0T3TF\ + AKq6AH+IIa4wsMlXdkK7LQFGnArmYwXuTyVpDoaYbYP9F5sXslfa294oqPp0kfUl\ + niyzX0jLYKAL7CqEBzMXVtLPo2Be6X6uagBIz6MV8s1FGmETf++pWKsuvR9EOoh8\ + Cm0xozW9WlPm0dBeMyT991QqDkfaMyOtFT6KZwkD3HxAiTBOZ1LI/P00kaPjpJwt\ + +8OKGjqQcXBn6p4ZxF6AmZ9fMCWkYyG37HwSeQYJM/zqrbP+Opfl6dgGJ+Qa5P6k\ + 1f8YzBkE1gG1V9YcAAWOGPMOgqBE0V0uZfPVctp4wcC4WBqti4pYC28+iHdewQzl\ + 9LB6RwIJmWNrhRLY+fdutV8NgTVb44vtkaQ+ewyc8y01Fk/G0HXarPt3UYgO6oqa\ + FpEU/wi2o9qMVgvHmkXdR1yQLSYZs2R/yzE1KDUSOmxa5T+XFfW7KQ07fhwk27Gk\ + y7Ob3mU1LT25MO7yLXUjGqNj9k9aa5FLUTyoh1JGGM64Zw==\r\n\ + -----END CERTIFICATE-----\r\n +newca-certificate = -----BEGIN CERTIFICATE-----\r\n\ + MIIF6jCCA9KgAwIBAgIJANTA8rXGnhG7MA0GCSqGSIb3DQEBCwUAMIGBMQswCQYD\ + VQQGEwJCRTEYMBYGA1UECAwPT29zdC1WbGFhbmRlcmVuMRUwEwYDVQQHDAxTaW50\ + LU5pa2xhYXMxDTALBgNVBAoMBE5pa28xFTATBgNVBAsMDEhvbWUgQ29udHJvbDEb\ + MBkGCSqGSIb3DQEJARYMaW5mb0BuaWtvLmJlMB4XDTcwMDEwMTAwMDAwNVoXDTM3\ + MDEyOTAwMDAwNVowgYExCzAJBgNVBAYTAkJFMRgwFgYDVQQIDA9Pb3N0LVZsYWFu\ + ZGVyZW4xFTATBgNVBAcMDFNpbnQtTmlrbGFhczENMAsGA1UECgwETmlrbzEVMBMG\ + A1UECwwMSG9tZSBDb250cm9sMRswGQYJKoZIhvcNAQkBFgxpbmZvQG5pa28uYmUw\ + ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpKNKKHC0fCND19D96G78G\ + Zdj+OGLvy/DRJswbLepG8cPedqEZwXjn762fvLdTlcTX/ohkeG4QPb1mxPzjpEgl\ + M5aNmp2rmlAFVtLWILQx7mWir5FjG5eTyYi2fbYHnPQpx8XuVk2INENd85818R4j\ + RfouYLaZWSd8wc7LP20N0rVtjg5RJ/zAkQ6A7KzdgeOkKhn07wSGBWu9vDw7gCdL\ + +Oyeo4LQmABXB7up8nIDCl+o23QL4/aSzdrS5cBCXoPWwto7OiXw0RRcEbpumQyW\ + mTGS8jT2FCUNAIWAxC3pKEIXbzf03pLo7EMfFcmjsLDcvcnkB+EJX0fuATwl5CLz\ + SneUFY7MNTpv9xgZFX83LhoiFbycZwzWEUr/Q0pmHYZdmezm84+W6EA3E9qH+oR8\ + V09bwEMAMSQpbebEB8JmvvwykQHxowkpnV01bmimBEOaquAmyfiW3YSO90vJu+kg\ + Zrkihc0AEMFcDbLRCEKvx/u6Hs2xMmVPz0W9mPW37t5zKOV0vcrHmFgMp+9EyDAQ\ + vfNofLx790lD1LFp3qvD/H0+IbydQoEc7Q1/tTQDjL45TLNXwwBWQVQLIEQY5sqN\ + n8p2ita3MPpSnu5XU93pBcns8jUNlc6/wFIMSBDWK40RiJKzTsr/2jTGVqZX8PXA\ + rDnIoa0Eapt0nq87qnkQzQIDAQABo2MwYTAdBgNVHQ4EFgQU5rQ0Z/a3fq5iKhZV\ + qmabTvd4kFkwHwYDVR0jBBgwFoAU5rQ0Z/a3fq5iKhZVqmabTvd4kFkwDwYDVR0T\ + AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAFLw\ + 6lbxex6hElSrqOZljoFQzQg78dmtUFm4BgKu5EAqn1Ug/OHOKk8LBbNMf2X0Y+4i\ + SO4g8yO/C9M/YPxjnM5As1ouTu7UzQL4hEJynyHoPuCBD8nZxuaKeGMX7nQYm7GD\ + 0iaL0iP9gFwv/A2O/isQUB/sTAclhm1zKAw4f/SaBq8t22wf59e8na0xIfHui0PD\ + s8PfRbC4xIOKMxHkHFv+DHeMGjCbR4x20RV/z4JNx1ALEBGo6Oh7Dph/maAQWbje\ + x9BCstNR3V1Bhx9rUe7BjIMyJUGEItpZXG+N+qnQr2K7xDdloJl4X0flIa74sdUE\ + K4s0X7p+JixLMSxbu5oS6W+d3g6EG0ZgEUwwwc98D1fsm1ziNqwcnYMkI6P2601G\ + kEaK/54kYqCxvw6fu5+PNmsDD8ptdazoO3/UOxWvspI1U3drcpnaEHuNclEF7WeL\ + yqTfi+8UiL9xJgq9ivjKjZdchkdaD2THgrnzs0XxLbZnwAPeh3cHooUJQkInmKp3\ + O05Gv0rnSr29bH8vh/sy4/yJJCUd036pF9C8mPHAYsvNDVGaGYVmNt5P28z3PO16\ + YKNJCOJ0x333F6PJaqWAQQP9bGMuJThX8ZQ9Fd8KMXVUfFVKICEkb4erWpL2RIz3\ + 9JFSC56ZtXv2losfASTyXJwCpyib7FcTZ1rJze+l\r\n\ + -----END CERTIFICATE-----\r\n +intermediate-certificate = -----BEGIN CERTIFICATE-----\r\n\ + MIIF2jCCA8KgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UEBhMCQkUx\ + GDAWBgNVBAgMD09vc3QtVmxhYW5kZXJlbjEVMBMGA1UEBwwMU2ludC1OaWtsYWFz\ + MQ0wCwYDVQQKDAROaWtvMQwwCgYDVQQLDANSJkQxGjAYBgkqhkiG9w0BCQEWC2pn\ + b0BuaWtvLmJlMB4XDTE3MDExNjEwMDA1N1oXDTI3MDExNDEwMDA1N1owgYAxCzAJ\ + BgNVBAYTAkJFMRgwFgYDVQQIDA9Pb3N0LVZsYWFuZGVyZW4xDTALBgNVBAoMBE5p\ + a28xDDAKBgNVBAsMA1ImRDEeMBwGA1UEAwwVaW50ZXJtZWRpYXRlIEFQSSBjZXJ0\ + MRowGAYJKoZIhvcNAQkBFgtqZ29Abmlrby5iZTCCAiIwDQYJKoZIhvcNAQEBBQAD\ + ggIPADCCAgoCggIBAPRSUHw3o3F46vfkSD19O3vqW6GRVofDLWjTz9tFIGUlT0wK\ + 0MkrjSzfuAmUEU8EqS24aSSk8NOyM1aCUPOs8TCq72otxskad4SUmvJo+azVALgB\ + 7aiK4pZEoAcCyb3xzJDTSeyetx7Nd4udHwzi7Eez9cE41ZGlbgPWl+mr0FwNr+bV\ + EZ6tZYqri88xGFVgWxP0Gu/IMRW08g3Zp7MGaQNRv6ygOIc+aeiUwXFWjF8n2Tpu\ + h893+uB6HBDCLfW7DonJaoUZWvl84c8HTGg9bFpp76PC8b61jCqu4ehdYTVxltIY\ + VyKwUpibd5e9PA+1d0l21cQ1Pu9nl7jCGSJupvuUwYnh190szDZcAdGJ/ufSfTcj\ + d+/UOfBAVe7wsQS8yZCM/BZv58n/yPfkv2JRFVuYrsRzJ3v+8uYBw0zmjps245oe\ + YI//okITAUF8m669+CRG6w4QhLk4PjjA7nLXfHCEOMMEP1P+tVEoDj4qOuPKRPh2\ + 6fNvF6c3V9VPwg8gRNn647c+rnY/nXm0RsR5KG6+eVhJJK2XQUdzvBGsjqYOXGEP\ + 1AJia8T1HRzDxjkZ+XOJLvm7lvU2QALspUiHkSujuLUtiLjq9PVTcQYnPe2drOdh\ + umveD20bKgxKpQMrUDEfDSjypI2tYb76M0qJazyEAoamE7a14ZQf6s655p6nAgMB\ + AAGjZjBkMB0GA1UdDgQWBBSJysj201o5jl4vz36kZyaoPnFvvjAfBgNVHSMEGDAW\ + gBS1GTXKMka186SljwL8tG78B7hqVjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1Ud\ + DwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAmOS2Jag9WFSEfVr1iXux5ZsV\ + 5DFY7dMrmRdjmdjBMKe51bwN3wrEiWM+dJfJ8VtSAhPxpNmcbLAzlSamPHZ3hXUe\ + 3X2wY8xFxAwZy7c5zZmh4pnqDFbzuqRZ6L+Hc7S/t7rG1wZB/2tnot+BZaPnEaFA\ + jU/N2EYRHjj6zGz1xRTVO4ycmNfRtYrYCe2c9/aDB3f37ekRofDxu4d8rT70qAci\ + Ycm1zkRadg9u0KPVQ0XaQAsxZ1eI/EJowlkC18oHdQF/MpfhCP6EcudvskaLFNmA\ + pv4L/i+VO3ovRRyt+U3UpEzsA66X5XzFZ58pOikilIVV8odVRRVKvGhUKnK+CU8h\ + nnb+RKxsTGP2mKMXiSEUG3peFQupJ18eE6VXlOPbSgtcU2dPUVZUDrRlAaQAZLiI\ + gicNnGb6EnB8jqqIXcFfuLOYId8PQcEsLu42Nz+DtAzQvzhsXgb2HQZfunDwsIwW\ + Z6HL/AfE/gCPqLlYZqb2l2NZeKjzPPGE1ohX5NIvDl0/tHsNw1Yb23xntx5s29EX\ + FUmXnAuS1u5M44VQPNZ11RuzlWxyj/gfDGIKYl2K9YO1etVTVRQHcS4yro3M+9yy\ + ysRrjZo7jtZl7iZ2mHLAfEP3FJucHe8diCY/vMxSxvn+89aP4k0x051MNw3xW7Mn\ + 422w/GItAjwcLjagfxA=\r\n\ + -----END CERTIFICATE-----\r\n +ca-certificate = -----BEGIN CERTIFICATE-----\r\n\ + MIIF1DCCA7ygAwIBAgIJAMwcTOyTL+pMMA0GCSqGSIb3DQEBCwUAMHcxCzAJBgNV\ + BAYTAkJFMRgwFgYDVQQIDA9Pb3N0LVZsYWFuZGVyZW4xFTATBgNVBAcMDFNpbnQt\ + TmlrbGFhczENMAsGA1UECgwETmlrbzEMMAoGA1UECwwDUiZEMRowGAYJKoZIhvcN\ + AQkBFgtqZ29Abmlrby5iZTAeFw0xNzAxMTYwOTU0MDJaFw0zNzAxMTEwOTU0MDJa\ + MHcxCzAJBgNVBAYTAkJFMRgwFgYDVQQIDA9Pb3N0LVZsYWFuZGVyZW4xFTATBgNV\ + BAcMDFNpbnQtTmlrbGFhczENMAsGA1UECgwETmlrbzEMMAoGA1UECwwDUiZEMRow\ + GAYJKoZIhvcNAQkBFgtqZ29Abmlrby5iZTCCAiIwDQYJKoZIhvcNAQEBBQADggIP\ + ADCCAgoCggIBANu1DzZLGgEQgmW9PD8KR4Seskp3HJbgbgUcN98/wlbCK6Zd/l9L\ + sKz16shaJfcw45kTYF4eDcSgORtI8SUc0pgKEKKtvLE8R19BBayPb/FkiGV7yFPa\ + Q41eCySOdrWC5vrm2cP7pLroLqXuJefR0e0ajy3HpBoxkOSmUXt5Q8dKIuYQCV3I\ + I8pE1Jl8f0Xc0AhQm2g3Gpdsn0+98zwc90meoICRQf1nzl+T1Cs2n6f0YqRkbjzQ\ + zXN7TJz5Z0bYM5XvHvBOBr/70afIX6eAUE4bsimP6dHSpdfKnCdc+Wx+KefHG3Or\ + w5pmUVfZWIDoQVMuEsUw4s0QAHWNLULnB+ARj6joNCzRttj1Jkybq+qb3x626/d3\ + TrcwUk1yMPJPcvQyv73pJga6zfi5vap+PBROB7SvKFvqvHxJi0EMfddGiMhqYwLe\ + 4pr1vZj2nn60mYNr4mhFfSXArvoPstf0pSX5M3o/B/+qXyJMuX4gcWSeq75BOZ/4\ + 3bBGtYfdj+9Xx0rflG6hK8c6BDzSoox9iQHCbq6DgGTpNTunoBhQdNvP53XbzMuF\ + an7xjaJbjtjaJV88XPOFW7eZmAdjgTC1qxgMLvZl6uZbrTJkcwyT3gsAszV2/Mlu\ + tfor9ngrsrVIjGnzmho87EYhPRpZKD7rzoCDiv5pQHJleYIzYskv8wpJAgMBAAGj\ + YzBhMB0GA1UdDgQWBBS1GTXKMka186SljwL8tG78B7hqVjAfBgNVHSMEGDAWgBS1\ + GTXKMka186SljwL8tG78B7hqVjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE\ + AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAgTbEQ4Vd85/2WvAy/5fD4u2u6xtFaKR/\ + OOkZ/nlUAuqheDCEXVOlkRNUzqqXL2nn95yiUtYKK9xXEAiClq9TtvLT5WM4XagH\ + zoWcXOGzZFwxOZkFO1bla8x3GtsSDfiPIMw7eJIIWEewXNWs01InbCvzQaNyYBTL\ + tdTABcGKIlPivUwfBfhYXRwN5/ZRN0Gbca6jLoF9xZog9+1rTnbWh8cASWJwodc+\ + nvmmTO34zAHbXXBcBeJwWjFKtA6L08C3lJ2dON/u9eSL8CesICZnaSRDPR0qBIhU\ + uAhHER/Ps8a9POYzrev7P37v/e7nqGbFzZZFAZM+yM4kAcntzj9yFX/sdx+YFTqI\ + ZYQsLFOv1RISYGJK32mA851/oLGHCR0/5x3Mc2eZktH7lmJ0N8NmQ7k2gLoACbXF\ + mUwfEVm8RATKgi9afJRCYbeyi1zzFAwpib3IddRQZf4NguTn0vrpxVlSeF+AHr1A\ + Xn0gwutwNB07mi2vRl7teC69AFdv3ZyLsLt7bXnCvO0vNMRBWGFv9iUh48piAGjA\ + 8QHuxo55OOtopg5WNR3Moz+ebsq33B6mCws5ov8V8X8slU6+4xoerID4js/Kre1d\ + PW0VwFd8ok2ocQ/g8wpFEtyWlzdQVWJe8FUyR97kjlKsy+H4AO6twuhi3GYLZoqr\ + K1GCaaZcI1Q=\r\n\ + -----END CERTIFICATE-----\r\n diff --git a/bundles/org.openhab.binding.onewire/README.md b/bundles/org.openhab.binding.onewire/README.md index 66317b7cadd9e..ce3e4c80694a5 100644 --- a/bundles/org.openhab.binding.onewire/README.md +++ b/bundles/org.openhab.binding.onewire/README.md @@ -3,19 +3,23 @@ The OneWire binding integrates OneWire (also spelled 1-Wire) devices. OneWire is a serial bus developed by Dallas Semiconductor. It provides cheap sensors for temperature, humidity, digital I/O and more. - + +## Getting Started + +The OneWire File System (OWFS, http://owfs.org) provides an abstraction layer between the OneWire bus and this binding. +It is assumed that you already have a working OWFS installation. +Besides your sensors, you need a busmaster device (e.g. DS9490R). + ## Supported Things ### Bridges Currently only one bridge is supported. - -The OneWire File System (OWFS, http://owfs.org) provides an abstraction layer between the OneWire bus and this binding. The `owserver` is the bridge that connects to an existing OWFS installation. ### Things -There are different types of things: the simple one (`basic`), multisensors built around the DS1923/DS2438 chip (`ms-tx`) and more advanced sensors from Elaborated Networks (www.wiregate.de) (`ams`, `bms`) and Embedded Data System (`edsenv`). +There are different types of things: the simple one (`basic`), multisensors built around the DS1923/DS2438 chip (`ms-tx`) and more advanced sensors from Elaborated Networks (www.wiregate.de) (`ams`, `bms`), Embedded Data System (www.embeddeddatasystems.com)(`edsenv`) and Brain4Home (www.brain4home.eu) (`bae091x`). ** Important: Breaking Change with next release ** @@ -74,18 +78,19 @@ It always provides a `temperature` channel. Depnding on the actual sensor, additional channels (`current`, `humidity`, `light`, `voltage`, `supplyvoltage`) are added. If the voltage input of the DS2438 is connected to a humidity sensor, several common types are supported (see below). -It has two parameters: sensor id `id` and refresh time `refresh`. +It has three parameters: sensor id `id`, refresh time `refresh` and `manualsensor` (advanced option). Known DS2438-base sensors are iButtonLink (https://www.ibuttonlink.com/) MS-T (recognized as generic DS2438), MS-TH, MS-TC, MS-TL, MS-TV. Unknown multisensors are added as generic DS2438 and have `temperature`, `current`, `voltage` and `supplyvoltage` channels. -In case the sensor is not properly detected (e.g. because it is a self-made sensor), check if it is compatible with one of the sensors listed above. If so, the first byte of page 3 of the DS2438 needs to be set to the correct identification (0x00 = generic/MS-T, 0x19 = MS-TH, 0x1A = MS-TV, 0x1B = MS-TL, 0x1C = MS-TC). **Note: Updating the pages of a sensor can break other software. This is fully your own risk.** +In case the sensor is not properly detected (e.g. because it is a self-made sensor), check if it is compatible with one of the sensors listed above. +You can use `manualsensor` to override the auto-detected sensortype by setting `DS2438`, `MS_TH`, `MS_TV`, `MS_TL` or `MS_TC`. ### Elaborated Networks Multisensors (`ams`, `bms`) These things are complex devices from Elaborated networks. They consist of a DS2438 and a DS18B20 with additional circuitry on one PCB. -The AMS additionally has a second DS242438 and a DS2413 for digital I/O on-board. +The AMS additionally has a second DS2438 and a DS2413 for digital I/O on-board. Analog light sensors can optionally be attached to both sensors. These sensors provide `temperature`, `humidity` and `supplyvoltage` channels. @@ -116,6 +121,21 @@ It has two parameters: sensor id `id` and refresh time `refresh`. All things have a `temperature` channel. Additional channels (`light`, `pressure`, `humidity`, `dewpoint`, `abshumidity`) will be added if available from the sensor automatically. +### Brain4Home BAE091x (`bae091x`) + +Currently this thing only supports BAE0910 sensors. +All functional pins of this sensor have multiple functions which can be configured individually. +For detailed information of each mode, please see the official documentation. +Each pin has the can be configured as `disabled`. +The necessary channels are automatically added. + +Pin 1 (`pin1`) has only one function `counter` (channel `counter`). +Pin 2 (`pin2`) can be configured as digital output (`output`, channel `digital2`) or pulse width modulated output (`pwm`, software PWM 4, channels `freq2`, `duty4`). +Pin 6 (`pin6`) can be configured as digital in-/output (`pio`, channel `digital6`) or pulse width modulated output (`pwm`, software PWM 3, channels `freq1`, `duty3`). +Pin 7 (`pin7`) can be configured as analog input (`analog`), digital output (`output`, channel `digital7`) or pulse width modulated output (`pwm`, hardware PWM 2, channels `freq2`, `duty2`). +Pin 8 (`pin8`) can be configured as digital input (`input`, channel `digital8`), digital output (`output`, channel `digital8`) or pulse width modulated output (`pwm`, hardware PWM 1, channels `freq1`, `duty1`). + +Please note: support for this sensor is considered experimental. ## Channels @@ -138,6 +158,13 @@ Additional channels (`light`, `pressure`, `humidity`, `dewpoint`, `abshumidity`) | temperature-por | temperature | Number:Temperature | yes | environmental temperature | | temperature-por-res | temperature, ams, bms | Number:Temperature | yes | environmental temperature | | voltage | ms-tx, ams | Number:ElectricPotential | yes | voltage input | +| bae-pwm-frequency | bae091x | Number:Frequency | no | frequency for PWM output | +| bae-pwm-duty | bae091x | Number:Dimensionless | no | duty cycle (0-100%) for PWM output | +| bae-di | bae091x | Switch | yes | digital input | +| bae-do | bae091x | Switch | no | digital output | +| bae-pio | bae091x | Switch | yes | digital in-/output | +| bae-analog | bae091x | Number:ElectricPotential | yes | analog input | +| bae-counter | bae091x | Number | yes | countervalue | ### Digital I/O (`dio`) @@ -186,6 +213,34 @@ This corresponds to 0.5 °C, 0.25 °C, 0.125 °C, 0.0625 °C respectively. The conversion time is inverse to that and ranges from 95 ms to 750 ms. For best performance it is recommended to set the resolution only as high as needed. + +### BAE PWM (`bae-pwm-frequency`, `bae-pwm-duty`) + +PWM output 1 and 3 (2 and 4) share a frequency channel `pwmfreq1` (`pwmfreq2`). +Each PWM output has its own duty cycle (`pwmduty1` to `pwmduty4`). + +The frequency channel has two configuration options (`prescaler`, `reversePolarity`). +The `prescaler` sets the frequency range which can be used. +Valid values are `0`to `7` (`0` => 245 Hz - 8 MHz, `1`=> 123 Hz - 4 MHz, `2` => 62 Hz - 2 MHz, `3` => 31 Hz - 1 MHz, `4` => 16 Hz - 500 kHz, `5` => 8 Hz - 250 kHz, `6` => 4 Hz - 125 kHz, `7` => 2 Hz - 62.5 kHz). +The default value is `0`. +The `reversePolarity` option is used to invert the output. +It can be `true` or `false`. +The default value is `false`. + +The duty cycle can be set from 0-100%. + +### BAE PIO (`bae-pio`) + + +The PIO channel (programmable I/O channel) has two configuration options: `mode` and `pulldevice`. +The `mode`can be set to `input`or `output`. +The default is `input`. + +The `pulldevice` is only relevant for `input` mode. +It can be configured as `disabled`, `pullup`, `pulldown`. +The default is disabled. + + ## Full Example ** Attention: Adding channels with UIDs different from the ones mentioned in the thing description will not work and may cause problems. diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/OwBindingConstants.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/OwBindingConstants.java index 5c1638edd75c7..7a731df9a320d 100644 --- a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/OwBindingConstants.java +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/OwBindingConstants.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.onewire.internal; -import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -42,6 +42,7 @@ public class OwBindingConstants { public static final ThingTypeUID THING_TYPE_AMS = new ThingTypeUID(BINDING_ID, "ams"); public static final ThingTypeUID THING_TYPE_BASIC = new ThingTypeUID(BINDING_ID, "basic"); public static final ThingTypeUID THING_TYPE_EDS_ENV = new ThingTypeUID(BINDING_ID, "edsenv"); + public static final ThingTypeUID THING_TYPE_BAE091X = new ThingTypeUID(BINDING_ID, "bae091x"); // TODO: remove after 2.5.0 release & cleanup public static final ThingTypeUID THING_TYPE_MS_TH = new ThingTypeUID(BINDING_ID, "ms-th"); @@ -54,10 +55,13 @@ public class OwBindingConstants { public static final ThingTypeUID THING_TYPE_IBUTTON = new ThingTypeUID(BINDING_ID, "ibutton"); public static final ThingTypeUID THING_TYPE_TEMPERATURE = new ThingTypeUID(BINDING_ID, "temperature"); - public static final Set SUPPORTED_THING_TYPES = new HashSet<>(Arrays.asList(THING_TYPE_OWSERVER, - THING_TYPE_TEMPERATURE, THING_TYPE_IBUTTON, THING_TYPE_DIGITALIO, THING_TYPE_DIGITALIO2, - THING_TYPE_DIGITALIO8, THING_TYPE_AMS, THING_TYPE_BMS, THING_TYPE_MS_TH, THING_TYPE_MS_TX, THING_TYPE_MS_TV, - THING_TYPE_EDS_ENV, THING_TYPE_COUNTER, THING_TYPE_COUNTER2, THING_TYPE_BASIC)); + public static final Set SUPPORTED_THING_TYPES = Collections + .unmodifiableSet(Stream + .of(THING_TYPE_OWSERVER, THING_TYPE_TEMPERATURE, THING_TYPE_IBUTTON, THING_TYPE_DIGITALIO, + THING_TYPE_DIGITALIO2, THING_TYPE_DIGITALIO8, THING_TYPE_AMS, THING_TYPE_BMS, + THING_TYPE_MS_TH, THING_TYPE_MS_TX, THING_TYPE_MS_TV, THING_TYPE_EDS_ENV, + THING_TYPE_COUNTER, THING_TYPE_COUNTER2, THING_TYPE_BASIC, THING_TYPE_BAE091X) + .collect(Collectors.toSet())); // List of all config options public static final String CONFIG_ADDRESS = "network-address"; @@ -74,6 +78,13 @@ public class OwBindingConstants { public static final String CONFIG_DIGITAL_LOGIC = "logic"; public static final String CONFIG_TEMPERATURESENSOR = "temperaturesensor"; public static final String CONFIG_LIGHTSENSOR = "lightsensor"; + public static final String CONFIG_BAE_PIN_DISABLED = "disabled"; + public static final String CONFIG_BAE_PIN_PIO = "pio"; + public static final String CONFIG_BAE_PIN_COUNTER = "counter"; + public static final String CONFIG_BAE_PIN_PWM = "pwm"; + public static final String CONFIG_BAE_PIN_ANALOG = "analog"; + public static final String CONFIG_BAE_PIN_IN = "input"; + public static final String CONFIG_BAE_PIN_OUT = "output"; // list of all properties public static final String PROPERTY_MODELID = "modelId"; @@ -102,9 +113,16 @@ public class OwBindingConstants { public static final String CHANNEL_DIGITAL5 = "digital5"; public static final String CHANNEL_DIGITAL6 = "digital6"; public static final String CHANNEL_DIGITAL7 = "digital7"; + public static final String CHANNEL_DIGITAL8 = "digital8"; public static final String CHANNEL_COUNTER = "counter"; public static final String CHANNEL_COUNTER0 = "counter0"; public static final String CHANNEL_COUNTER1 = "counter1"; + public static final String CHANNEL_PWM_DUTY1 = "pwmduty1"; + public static final String CHANNEL_PWM_DUTY2 = "pwmduty2"; + public static final String CHANNEL_PWM_DUTY3 = "pwmduty3"; + public static final String CHANNEL_PWM_DUTY4 = "pwmduty4"; + public static final String CHANNEL_PWM_FREQ1 = "pwmfreq1"; + public static final String CHANNEL_PWM_FREQ2 = "pwmfreq2"; public static final ChannelTypeUID CHANNEL_TYPE_UID_ABSHUMIDITY = new ChannelTypeUID(BINDING_ID, "abshumidity"); public static final ChannelTypeUID CHANNEL_TYPE_UID_COUNTER = new ChannelTypeUID(BINDING_ID, "counter"); @@ -122,6 +140,15 @@ public class OwBindingConstants { public static final ChannelTypeUID CHANNEL_TYPE_UID_TEMPERATURE_POR_RES = new ChannelTypeUID(BINDING_ID, "temperature-por-res"); public static final ChannelTypeUID CHANNEL_TYPE_UID_VOLTAGE = new ChannelTypeUID(BINDING_ID, "voltage"); + public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_ANALOG = new ChannelTypeUID(BINDING_ID, "bae-analog"); + public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_COUNTER = new ChannelTypeUID(BINDING_ID, "bae-counter"); + public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_DIGITAL_OUT = new ChannelTypeUID(BINDING_ID, "bae-do"); + public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_DIN = new ChannelTypeUID(BINDING_ID, "bae-in"); + public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_DOUT = new ChannelTypeUID(BINDING_ID, "bae-out"); + public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_PIO = new ChannelTypeUID(BINDING_ID, "bae-pio"); + public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_PWM_DUTY = new ChannelTypeUID(BINDING_ID, "bae-pwm-duty"); + public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_PWM_FREQUENCY = new ChannelTypeUID(BINDING_ID, + "bae-pwm-frequency"); public static final ChannelTypeUID CHANNEL_TYPE_UID_OWFS_NUMBER = new ChannelTypeUID(BINDING_ID, "owfs-number"); public static final ChannelTypeUID CHANNEL_TYPE_UID_OWFS_STRING = new ChannelTypeUID(BINDING_ID, "owfs-string"); @@ -141,8 +168,9 @@ public class OwBindingConstants { e -> new ThingTypeUID(BINDING_ID, e.getValue()))); SENSOR_TYPE_CHANNEL_MAP = properties.entrySet().stream().filter(e -> e.getKey().endsWith(".channels")) .collect(Collectors.toConcurrentMap(e -> OwSensorType.valueOf(e.getKey().split("\\.")[0]), - e -> Stream.of(e.getValue().split(",")).map(c -> OwChannelConfig.fromString(c)) - .collect(Collectors.toSet()))); + e -> !e.getValue().isEmpty() ? Stream.of(e.getValue().split(",")) + .map(c -> OwChannelConfig.fromString(c)).collect(Collectors.toSet()) + : new HashSet<>())); THING_LABEL_MAP = properties.entrySet().stream().filter(e -> e.getKey().endsWith(".label")).collect( Collectors.toConcurrentMap(e -> OwSensorType.valueOf(e.getKey().split("\\.")[0]), e -> e.getValue())); } diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/OwHandlerFactory.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/OwHandlerFactory.java index f672af5a5528d..e42a642feef6b 100644 --- a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/OwHandlerFactory.java +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/OwHandlerFactory.java @@ -30,6 +30,7 @@ import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; import org.openhab.binding.onewire.internal.discovery.OwDiscoveryService; import org.openhab.binding.onewire.internal.handler.AdvancedMultisensorThingHandler; +import org.openhab.binding.onewire.internal.handler.BAE091xSensorThingHandler; import org.openhab.binding.onewire.internal.handler.BasicMultisensorThingHandler; import org.openhab.binding.onewire.internal.handler.BasicThingHandler; import org.openhab.binding.onewire.internal.handler.EDSSensorThingHandler; @@ -38,6 +39,8 @@ import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.ConfigurationPolicy; import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The {@link OwHandlerFactory} is responsible for creating things and thing @@ -48,8 +51,8 @@ @NonNullByDefault @Component(service = ThingHandlerFactory.class, configurationPid = "binding.onewire", configurationPolicy = ConfigurationPolicy.OPTIONAL) public class OwHandlerFactory extends BaseThingHandlerFactory { - - private final Map> discoveryServiceRegs = new HashMap<>(); + Logger logger = LoggerFactory.getLogger(OwHandlerFactory.class); + private final Map> discoveryServiceRegs = new HashMap<>(); @NonNullByDefault({}) private OwDynamicStateDescriptionProvider dynamicStateDescriptionProvider; @@ -61,6 +64,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { @Override protected @Nullable ThingHandler createHandler(Thing thing) { + logger.error("factory {} creating thing {}", this, thing); ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (OwserverBridgeHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) { @@ -75,11 +79,19 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { return new BasicThingHandler(thing, dynamicStateDescriptionProvider); } else if (EDSSensorThingHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) { return new EDSSensorThingHandler(thing, dynamicStateDescriptionProvider); + } else if (BAE091xSensorThingHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) { + return new BAE091xSensorThingHandler(thing, dynamicStateDescriptionProvider); } return null; } + @Override + public void unregisterHandler(Thing thing) { + super.unregisterHandler(thing); + logger.error("factory {} deleting thing {}", this, thing); + } + private synchronized void registerDiscoveryService(OwserverBridgeHandler owserverBridgeHandler) { OwDiscoveryService owDiscoveryService = new OwDiscoveryService(owserverBridgeHandler); diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/AMSHandlerConfiguration.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/AMSHandlerConfiguration.java new file mode 100644 index 0000000000000..8a60facbe2f7a --- /dev/null +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/AMSHandlerConfiguration.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.onewire.internal.config; + +/** + * The {@link AMSHandlerConfiguration} is a helper class for the mstx thing handler configuration + * + * @author Jan N. Klug - Initial contribution + */ +public class AMSHandlerConfiguration extends BaseHandlerConfiguration { + public int digitalRefresh = 10; +} diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BAE091xAnalogConfiguration.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BAE091xAnalogConfiguration.java new file mode 100644 index 0000000000000..21829d16ceef6 --- /dev/null +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BAE091xAnalogConfiguration.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.onewire.internal.config; + +/** + * The {@link BAE091xAnalogConfiguration} is a helper class for the BAE091x ADC Pin configuration + * + * @author Jan N. Klug - Initial contribution + */ +public class BAE091xAnalogConfiguration { + public Boolean hires = false; +} diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BAE091xHandlerConfiguration.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BAE091xHandlerConfiguration.java new file mode 100644 index 0000000000000..f3e45e8b59e17 --- /dev/null +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BAE091xHandlerConfiguration.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.onewire.internal.config; + +import org.openhab.binding.onewire.internal.OwBindingConstants; + +/** + * The {@link BAE091xHandlerConfiguration} is a helper class for the BAE091x thing handler configuration + * + * @author Jan N. Klug - Initial contribution + */ +public class BAE091xHandlerConfiguration extends BaseHandlerConfiguration { + public String pin1 = OwBindingConstants.CONFIG_BAE_PIN_DISABLED; + public String pin2 = OwBindingConstants.CONFIG_BAE_PIN_DISABLED; + public String pin6 = OwBindingConstants.CONFIG_BAE_PIN_DISABLED; + public String pin7 = OwBindingConstants.CONFIG_BAE_PIN_DISABLED; + public String pin8 = OwBindingConstants.CONFIG_BAE_PIN_DISABLED; +} diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BAE091xPIOConfiguration.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BAE091xPIOConfiguration.java new file mode 100644 index 0000000000000..ad6b792dca8a1 --- /dev/null +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BAE091xPIOConfiguration.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.onewire.internal.config; + +/** + * The {@link BAE091xPIOConfiguration} is a helper class for the BAE091x PIO Pin configuration + * + * @author Jan N. Klug - Initial contribution + */ +public class BAE091xPIOConfiguration { + public String mode = "input"; + public String pulldevice = "disabled"; +} diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BAE091xPWMConfiguration.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BAE091xPWMConfiguration.java new file mode 100644 index 0000000000000..137fee9867b1a --- /dev/null +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BAE091xPWMConfiguration.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.onewire.internal.config; + +/** + * The {@link BAE091xPWMConfiguration} is a helper class for the BAE091x PWM Frequency configuration + * + * @author Jan N. Klug - Initial contribution + */ +public class BAE091xPWMConfiguration { + public int prescaler = 0; + public boolean reversePolarity = false; +} diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BaseHandlerConfiguration.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BaseHandlerConfiguration.java new file mode 100644 index 0000000000000..3ec0432f8da9f --- /dev/null +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/BaseHandlerConfiguration.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.onewire.internal.config; + +/** + * The {@link BaseHandlerConfiguration} is a helper class for the base thing handler configuration + * + * @author Jan N. Klug - Initial contribution + */ +public class BaseHandlerConfiguration { + public String id; + public int refresh = 300; +} diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/MstxHandlerConfiguration.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/MstxHandlerConfiguration.java new file mode 100644 index 0000000000000..d3a13ebd647c7 --- /dev/null +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/config/MstxHandlerConfiguration.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.onewire.internal.config; + +import org.openhab.binding.onewire.internal.device.OwSensorType; + +/** + * The {@link MstxHandlerConfiguration} is a helper class for the mstx thing handler configuration + * + * @author Jan N. Klug - Initial contribution + */ +public class MstxHandlerConfiguration extends BaseHandlerConfiguration { + public OwSensorType manualsensor; +} diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/device/BAE0910.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/device/BAE0910.java new file mode 100644 index 0000000000000..1accba637bc24 --- /dev/null +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/device/BAE0910.java @@ -0,0 +1,407 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.onewire.internal.device; + +import static org.openhab.binding.onewire.internal.OwBindingConstants.*; + +import java.util.BitSet; + +import javax.measure.quantity.Dimensionless; +import javax.measure.quantity.Frequency; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.openhab.binding.onewire.internal.OwException; +import org.openhab.binding.onewire.internal.SensorId; +import org.openhab.binding.onewire.internal.config.BAE091xAnalogConfiguration; +import org.openhab.binding.onewire.internal.config.BAE091xPIOConfiguration; +import org.openhab.binding.onewire.internal.config.BAE091xPWMConfiguration; +import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler; +import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler; +import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link BAE0910} class defines an BAE0910 device + * + * @author Jan N. Klug - Initial contribution + */ +@NonNullByDefault +public class BAE0910 extends AbstractOwDevice { + private static final int OUTC_OUTEN = 4; + private static final int OUTC_DS = 3; + + private static final int PIOC_PIOEN = 4; + private static final int PIOC_DS = 3; + private static final int PIOC_PD = 2; + private static final int PIOC_PE = 1; + private static final int PIOC_DD = 0; + + private static final int ADCC_ADCEN = 4; + private static final int ADCC_10BIT = 3; + @SuppressWarnings("unused") // for future use + private static final int ADCC_OFS = 2; + @SuppressWarnings("unused") + private static final int ADCC_GRP = 1; + @SuppressWarnings("unused") + private static final int ADCC_STP = 0; + + private static final int TPMC_POL = 7; + private static final int TPMC_INENA = 5; + private static final int TPMC_PWMDIS = 4; + private static final int TPMC_PS2 = 2; + private static final int TPMC_PS1 = 1; + private static final int TPMC_PS0 = 0; + + private final Logger logger = LoggerFactory.getLogger(BAE0910.class); + private final OwserverDeviceParameter pin1CounterParameter = new OwserverDeviceParameter("/counter"); + private final OwserverDeviceParameter pin2OutParameter = new OwserverDeviceParameter("/out"); + private final OwserverDeviceParameter pin6PIOParameter = new OwserverDeviceParameter("/pio"); + private final OwserverDeviceParameter pin7AnalogParameter = new OwserverDeviceParameter("/adc"); + private final OwserverDeviceParameter outcParameter = new OwserverDeviceParameter("/outc"); + private final OwserverDeviceParameter piocParameter = new OwserverDeviceParameter("/pioc"); + private final OwserverDeviceParameter adccParameter = new OwserverDeviceParameter("/adcc"); + private final OwserverDeviceParameter tpm1cParameter = new OwserverDeviceParameter("/tpm1c"); + private final OwserverDeviceParameter tpm2cParameter = new OwserverDeviceParameter("/tpm2c"); + private final OwserverDeviceParameter period1Parameter = new OwserverDeviceParameter("/period1"); + private final OwserverDeviceParameter period2Parameter = new OwserverDeviceParameter("/period2"); + private final OwserverDeviceParameter duty1Parameter = new OwserverDeviceParameter("/duty1"); + private final OwserverDeviceParameter duty2Parameter = new OwserverDeviceParameter("/duty2"); + private final OwserverDeviceParameter duty3Parameter = new OwserverDeviceParameter("/duty3"); + private final OwserverDeviceParameter duty4Parameter = new OwserverDeviceParameter("/duty4"); + + private BitSet outcRegister = new BitSet(8); + private BitSet piocRegister = new BitSet(8); + private BitSet adccRegister = new BitSet(8); + private BitSet tpm1cRegister = new BitSet(8); + private BitSet tpm2cRegister = new BitSet(8); + + private double resolution1 = 8; // in µs + private double resolution2 = 8; // in µs + + public BAE0910(SensorId sensorId, OwBaseThingHandler callback) { + super(sensorId, callback); + } + + @Override + public void configureChannels() { + } + + public void configureChannels(OwserverBridgeHandler bridgeHandler) throws OwException { + outcRegister.clear(); + piocRegister.clear(); + adccRegister.clear(); + tpm1cRegister.clear(); + tpm2cRegister.clear(); + + if (enabledChannels.contains(CHANNEL_PWM_FREQ1)) { + Channel channel = callback.getThing().getChannel(CHANNEL_PWM_FREQ1); + if (channel != null) { + BAE091xPWMConfiguration channelConfig = channel.getConfiguration().as(BAE091xPWMConfiguration.class); + tpm1cRegister.set(TPMC_POL, channelConfig.reversePolarity); + tpm1cRegister.set(TPMC_PS2, (channelConfig.prescaler & 4) == 4); + tpm1cRegister.set(TPMC_PS1, (channelConfig.prescaler & 2) == 2); + tpm1cRegister.set(TPMC_PS0, (channelConfig.prescaler & 1) == 1); + resolution1 = 0.0625 * (1 << channelConfig.prescaler); + } else { + throw new OwException("trying to configure pwm but frequency channel is missing"); + } + } + + if (enabledChannels.contains(CHANNEL_PWM_FREQ2)) { + Channel channel = callback.getThing().getChannel(CHANNEL_PWM_FREQ2); + if (channel != null) { + BAE091xPWMConfiguration channelConfig = channel.getConfiguration().as(BAE091xPWMConfiguration.class); + tpm2cRegister.set(TPMC_POL, channelConfig.reversePolarity); + tpm2cRegister.set(TPMC_PS2, (channelConfig.prescaler & 4) == 4); + tpm2cRegister.set(TPMC_PS1, (channelConfig.prescaler & 2) == 2); + tpm2cRegister.set(TPMC_PS0, (channelConfig.prescaler & 1) == 1); + resolution2 = 0.0625 * (1 << channelConfig.prescaler); + } else { + throw new OwException("trying to configure pwm but frequency channel is missing"); + } + } + + // Pin 2 + if (enabledChannels.contains(CHANNEL_DIGITAL2)) { + outcRegister.set(OUTC_DS); + outcRegister.set(OUTC_OUTEN); + } + + // Pin 6 + if (enabledChannels.contains(CHANNEL_DIGITAL6)) { + piocRegister.set(PIOC_PIOEN); + piocRegister.set(PIOC_DS); + Channel channel = callback.getThing().getChannel(CHANNEL_DIGITAL6); + if (channel != null) { + BAE091xPIOConfiguration channelConfig = channel.getConfiguration().as(BAE091xPIOConfiguration.class); + piocRegister.set(PIOC_DD, channelConfig.mode.equals("output")); + switch (channelConfig.pulldevice) { + case "pullup": + piocRegister.set(PIOC_PE); + piocRegister.clear(PIOC_PD); + break; + case "pulldown": + piocRegister.set(PIOC_PE); + piocRegister.set(PIOC_PD); + break; + default: + } + } else { + throw new OwException("trying to configure pin 6 but channel is missing"); + } + } + + // Pin 7 + if (enabledChannels.contains(CHANNEL_VOLTAGE)) { + adccRegister.set(ADCC_ADCEN); + Channel channel = callback.getThing().getChannel(CHANNEL_VOLTAGE); + if (channel != null) { + BAE091xAnalogConfiguration channelConfig = channel.getConfiguration() + .as(BAE091xAnalogConfiguration.class); + adccRegister.set(ADCC_10BIT, channelConfig.hires); + } else { + throw new OwException("trying to configure pin 7 but channel is missing"); + } + } + + if (enabledChannels.contains(CHANNEL_DIGITAL7)) { + tpm2cRegister.set(TPMC_PWMDIS); + } + + // Pin 8 + if (enabledChannels.contains(CHANNEL_DIGITAL8)) { + tpm1cRegister.set(TPMC_PWMDIS); + Channel channel = callback.getThing().getChannel(CHANNEL_DIGITAL8); + if (channel != null) { + if ((new ChannelTypeUID(BINDING_ID, "bae-in")).equals(channel.getChannelTypeUID())) { + tpm1cRegister.set(TPMC_INENA); + } + } else { + throw new OwException("trying to configure pin 8 but channel is missing"); + } + } + + // write configuration + bridgeHandler.writeBitSet(sensorId, outcParameter, outcRegister); + bridgeHandler.writeBitSet(sensorId, piocParameter, piocRegister); + bridgeHandler.writeBitSet(sensorId, adccParameter, adccRegister); + bridgeHandler.writeBitSet(sensorId, tpm1cParameter, tpm1cRegister); + bridgeHandler.writeBitSet(sensorId, tpm2cParameter, tpm2cRegister); + + isConfigured = true; + } + + @Override + public void refresh(OwserverBridgeHandler bridgeHandler, Boolean forcedRefresh) throws OwException { + if (isConfigured) { + // Counter + if (enabledChannels.contains(CHANNEL_COUNTER)) { + State counterValue = bridgeHandler.readDecimalType(sensorId, pin1CounterParameter); + callback.postUpdate(CHANNEL_COUNTER, counterValue); + } + + // Digital Pins + if (enabledChannels.contains(CHANNEL_DIGITAL2)) { + BitSet value = bridgeHandler.readBitSet(sensorId, pin2OutParameter); + callback.postUpdate(CHANNEL_DIGITAL2, OnOffType.from(value.get(0))); + } + if (enabledChannels.contains(CHANNEL_DIGITAL6)) { + BitSet value = bridgeHandler.readBitSet(sensorId, pin6PIOParameter); + callback.postUpdate(CHANNEL_DIGITAL6, OnOffType.from(value.get(0))); + } + if (enabledChannels.contains(CHANNEL_DIGITAL7)) { + BitSet value = bridgeHandler.readBitSet(sensorId, tpm2cParameter); + callback.postUpdate(CHANNEL_DIGITAL7, OnOffType.from(value.get(TPMC_POL))); + } + if (enabledChannels.contains(CHANNEL_DIGITAL8)) { + BitSet value = bridgeHandler.readBitSet(sensorId, tpm1cParameter); + callback.postUpdate(CHANNEL_DIGITAL8, OnOffType.from(value.get(TPMC_POL))); + } + + // Analog + if (enabledChannels.contains(CHANNEL_VOLTAGE)) { + State analogValue = bridgeHandler.readDecimalType(sensorId, pin7AnalogParameter); + callback.postUpdate(CHANNEL_VOLTAGE, + new QuantityType<>((DecimalType) analogValue, SmartHomeUnits.VOLT)); + } + + // PWM + int period1 = 0; + int period2 = 0; + if (enabledChannels.contains(CHANNEL_PWM_FREQ1)) { + period1 = ((DecimalType) bridgeHandler.readDecimalType(sensorId, period1Parameter)).intValue(); + double frequency = (period1 > 0) ? 1 / (period1 * resolution1 * 1e-6) : 0; + callback.postUpdate(CHANNEL_PWM_FREQ1, new QuantityType(frequency, SmartHomeUnits.HERTZ)); + } + if (enabledChannels.contains(CHANNEL_PWM_FREQ2)) { + period2 = ((DecimalType) bridgeHandler.readDecimalType(sensorId, period2Parameter)).intValue(); + double frequency = (period2 > 0) ? 1 / (period2 * resolution2 * 1e-6) : 0; + callback.postUpdate(CHANNEL_PWM_FREQ2, new QuantityType(frequency, SmartHomeUnits.HERTZ)); + } + if (enabledChannels.contains(CHANNEL_PWM_DUTY1)) { + int dutyValue = ((DecimalType) bridgeHandler.readDecimalType(sensorId, duty1Parameter)).intValue(); + double duty = (period1 > 0 && dutyValue <= period1) ? 100 * dutyValue / period1 : 100; + callback.postUpdate(CHANNEL_PWM_DUTY1, new QuantityType(duty, SmartHomeUnits.PERCENT)); + } + if (enabledChannels.contains(CHANNEL_PWM_DUTY2)) { + int dutyValue = ((DecimalType) bridgeHandler.readDecimalType(sensorId, duty2Parameter)).intValue(); + double duty = (period2 > 0 && dutyValue <= period2) ? 100 * dutyValue / period2 : 100; + callback.postUpdate(CHANNEL_PWM_DUTY2, new QuantityType(duty, SmartHomeUnits.PERCENT)); + } + if (enabledChannels.contains(CHANNEL_PWM_DUTY3)) { + int dutyValue = ((DecimalType) bridgeHandler.readDecimalType(sensorId, duty3Parameter)).intValue(); + double duty = (period1 > 0 && dutyValue <= period1) ? 100 * dutyValue / period1 : 100; + callback.postUpdate(CHANNEL_PWM_DUTY3, new QuantityType(duty, SmartHomeUnits.PERCENT)); + } + if (enabledChannels.contains(CHANNEL_PWM_DUTY4)) { + int dutyValue = ((DecimalType) bridgeHandler.readDecimalType(sensorId, duty4Parameter)).intValue(); + double duty = (period2 > 0 && dutyValue <= period2) ? 100 * dutyValue / period2 : 100; + callback.postUpdate(CHANNEL_PWM_DUTY4, new QuantityType(duty, SmartHomeUnits.PERCENT)); + } + } + } + + public boolean writeChannel(OwserverBridgeHandler bridgeHandler, String channelId, Command command) { + try { + BitSet value = new BitSet(8); + switch (channelId) { + case CHANNEL_DIGITAL2: + // output + if (!outcRegister.get(OUTC_OUTEN)) { + return false; + } + value.set(0, ((OnOffType) command).equals(OnOffType.ON)); + bridgeHandler.writeBitSet(sensorId, pin2OutParameter, value); + break; + case CHANNEL_DIGITAL6: + // not input, pio + if (!piocRegister.get(PIOC_DD) || !piocRegister.get(PIOC_PIOEN)) { + return false; + } + value.set(0, ((OnOffType) command).equals(OnOffType.ON)); + bridgeHandler.writeBitSet(sensorId, pin6PIOParameter, value); + break; + case CHANNEL_DIGITAL7: + // not pwm, not analog + if (!tpm2cRegister.get(TPMC_PWMDIS) || adccRegister.get(ADCC_ADCEN)) { + return false; + } + tpm2cRegister.set(TPMC_POL, ((OnOffType) command).equals(OnOffType.ON)); + bridgeHandler.writeBitSet(sensorId, tpm2cParameter, tpm2cRegister); + break; + case CHANNEL_DIGITAL8: + // not input, not pwm + if (tpm1cRegister.get(TPMC_INENA) || !tpm1cRegister.get(TPMC_PWMDIS)) { + return false; + } + tpm1cRegister.set(TPMC_POL, ((OnOffType) command).equals(OnOffType.ON)); + bridgeHandler.writeBitSet(sensorId, tpm1cParameter, tpm1cRegister); + break; + case CHANNEL_PWM_FREQ1: + if (command instanceof QuantityType) { + bridgeHandler.writeDecimalType(sensorId, period1Parameter, + convertFrequencyToPeriod(command, resolution1)); + } + break; + case CHANNEL_PWM_FREQ2: + if (command instanceof QuantityType) { + bridgeHandler.writeDecimalType(sensorId, period2Parameter, + convertFrequencyToPeriod(command, resolution2)); + } + break; + case CHANNEL_PWM_DUTY1: + if (command instanceof QuantityType) { + bridgeHandler.writeDecimalType(sensorId, duty1Parameter, calculateDutyCycle(command, + (DecimalType) bridgeHandler.readDecimalType(sensorId, period1Parameter))); + } + break; + case CHANNEL_PWM_DUTY2: + if (command instanceof QuantityType) { + bridgeHandler.writeDecimalType(sensorId, duty2Parameter, calculateDutyCycle(command, + (DecimalType) bridgeHandler.readDecimalType(sensorId, period2Parameter))); + } + break; + case CHANNEL_PWM_DUTY3: + if (command instanceof QuantityType) { + bridgeHandler.writeDecimalType(sensorId, duty3Parameter, calculateDutyCycle(command, + (DecimalType) bridgeHandler.readDecimalType(sensorId, period1Parameter))); + } + break; + case CHANNEL_PWM_DUTY4: + if (command instanceof QuantityType) { + bridgeHandler.writeDecimalType(sensorId, duty4Parameter, calculateDutyCycle(command, + (DecimalType) bridgeHandler.readDecimalType(sensorId, period2Parameter))); + } + break; + default: + throw new OwException("unknown or invalid channel"); + } + return true; + } catch ( + + OwException e) { + logger.info("could not write {} to {}: {}", command, channelId, e.getMessage()); + return false; + } + } + + public static OwSensorType getDeviceSubType(OwserverBridgeHandler bridgeHandler, SensorId sensorId) + throws OwException { + OwserverDeviceParameter deviceTypeParameter = new OwserverDeviceParameter("/device_type"); + + String subDeviceType = bridgeHandler.readString(sensorId, deviceTypeParameter); + switch (subDeviceType) { + case "2": + return OwSensorType.BAE0910; + case "3": + return OwSensorType.BAE0911; + default: + return OwSensorType.UNKNOWN; + } + } + + private DecimalType convertFrequencyToPeriod(Command command, double resolution) throws OwException { + @SuppressWarnings("unchecked") + QuantityType fHz = ((QuantityType) command).toUnit(SmartHomeUnits.HERTZ); + if (fHz == null) { + throw new OwException("could not convert command to frequency"); + } + double f = fHz.doubleValue(); + int period = 0; + if (f > 0) { + period = (int) Math.min(Math.round(1 / (f * resolution * 1e-6)), 65535); + } + return new DecimalType(period); + } + + private DecimalType calculateDutyCycle(Command command, DecimalType period) throws OwException { + @SuppressWarnings("unchecked") + double dutyCycle = ((QuantityType) command).doubleValue(); + int dutyValue = 0; + if (dutyCycle > 0 && dutyCycle <= 100) { + dutyValue = (int) Math.round(dutyCycle / 100.0 * period.intValue()); + } else if (dutyCycle > 100) { + dutyValue = 65535; + } + return new DecimalType(dutyValue); + } +} diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/device/OwSensorType.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/device/OwSensorType.java index d55b01a85cd73..5585556d56722 100644 --- a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/device/OwSensorType.java +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/device/OwSensorType.java @@ -40,6 +40,9 @@ public enum OwSensorType { MS_TV, AMS, AMS_S, + BAE, + BAE0910, + BAE0911, BMS, BMS_S, EDS, diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/AdvancedMultisensorThingHandler.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/AdvancedMultisensorThingHandler.java index 6dfa47289d4f1..78f464b476e66 100644 --- a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/AdvancedMultisensorThingHandler.java +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/AdvancedMultisensorThingHandler.java @@ -14,7 +14,6 @@ import static org.openhab.binding.onewire.internal.OwBindingConstants.*; -import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -35,6 +34,7 @@ import org.openhab.binding.onewire.internal.OwDynamicStateDescriptionProvider; import org.openhab.binding.onewire.internal.OwException; import org.openhab.binding.onewire.internal.SensorId; +import org.openhab.binding.onewire.internal.config.AMSHandlerConfiguration; import org.openhab.binding.onewire.internal.device.AbstractOwDevice; import org.openhab.binding.onewire.internal.device.DS18x20; import org.openhab.binding.onewire.internal.device.DS2406_DS2413; @@ -83,7 +83,7 @@ public AdvancedMultisensorThingHandler(Thing thing, @Override public void initialize() { - Configuration configuration = getConfig(); + AMSHandlerConfiguration configuration = getConfig().as(AMSHandlerConfiguration.class); Map properties = editProperties(); if (!super.configureThingHandler()) { @@ -97,13 +97,7 @@ public void initialize() { if (THING_TYPE_AMS.equals(thingType)) { sensors.add(new DS2438(new SensorId(properties.get(PROPERTY_DS2438)), this)); sensors.add(new DS2406_DS2413(new SensorId(properties.get(PROPERTY_DS2413)), this)); - - if (configuration.containsKey(CONFIG_DIGITALREFRESH)) { - digitalRefreshInterval = ((BigDecimal) configuration.get(CONFIG_DIGITALREFRESH)).intValue() * 1000; - } else { - digitalRefreshInterval = 10 * 1000; - } - + digitalRefreshInterval = configuration.digitalRefresh * 1000; digitalLastRefresh = 0; } diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/BAE091xSensorThingHandler.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/BAE091xSensorThingHandler.java new file mode 100644 index 0000000000000..9aca52401d0cb --- /dev/null +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/BAE091xSensorThingHandler.java @@ -0,0 +1,247 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.onewire.internal.handler; + +import static org.openhab.binding.onewire.internal.OwBindingConstants.*; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.builder.ThingBuilder; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.onewire.internal.OwDynamicStateDescriptionProvider; +import org.openhab.binding.onewire.internal.OwException; +import org.openhab.binding.onewire.internal.config.BAE091xHandlerConfiguration; +import org.openhab.binding.onewire.internal.device.BAE0910; +import org.openhab.binding.onewire.internal.device.OwChannelConfig; +import org.openhab.binding.onewire.internal.device.OwSensorType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link BAE091xSensorThingHandler} is responsible for handling BAE0910 based multisensors + * + * @author Jan N. Klug - Initial contribution + */ +@NonNullByDefault +public class BAE091xSensorThingHandler extends OwBaseThingHandler { + public static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_BAE091X); + + private final Logger logger = LoggerFactory.getLogger(BAE091xSensorThingHandler.class); + + public static final Set SUPPORTED_SENSOR_TYPES = Collections.singleton(OwSensorType.BAE0910); + + public BAE091xSensorThingHandler(Thing thing, OwDynamicStateDescriptionProvider dynamicStateDescriptionProvider) { + super(thing, dynamicStateDescriptionProvider, SUPPORTED_SENSOR_TYPES); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof OnOffType) { + if (channelUID.getId().startsWith(CHANNEL_DIGITAL)) { + Bridge bridge = getBridge(); + if (bridge != null) { + OwserverBridgeHandler bridgeHandler = (OwserverBridgeHandler) bridge.getHandler(); + if (bridgeHandler != null) { + if (!((BAE0910) sensors.get(0)).writeChannel(bridgeHandler, channelUID.getId(), command)) { + logger.debug("writing to channel {} in thing {} not permitted (input channel)", channelUID, + this.thing.getUID()); + } + } else { + logger.warn("bridge handler not found"); + } + } else { + logger.warn("bridge not found"); + } + } + } + // TODO: PWM channels + super.handleCommand(channelUID, command); + } + + @Override + public void initialize() { + if (!super.configureThingHandler()) { + return; + } + + sensors.add(new BAE0910(sensorId, this)); + + scheduler.execute(() -> { + configureThingChannels(); + }); + } + + @Override + protected void configureThingChannels() { + ThingUID thingUID = getThing().getUID(); + logger.debug("configuring sensors for {}", thingUID); + + BAE091xHandlerConfiguration configuration = getConfig().as(BAE091xHandlerConfiguration.class); + + Set wantedChannel = new HashSet<>(); + wantedChannel.addAll(SENSOR_TYPE_CHANNEL_MAP.get(sensorType)); + + // Pin1: + switch (configuration.pin1) { + case CONFIG_BAE_PIN_DISABLED: + break; + case CONFIG_BAE_PIN_COUNTER: + wantedChannel.add(new OwChannelConfig(CHANNEL_COUNTER, CHANNEL_TYPE_UID_BAE_COUNTER)); + break; + default: + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "unknown configuration option for pin 1"); + return; + } + + // Pin2: + switch (configuration.pin2) { + case CONFIG_BAE_PIN_DISABLED: + break; + case CONFIG_BAE_PIN_OUT: + wantedChannel.add( + new OwChannelConfig(CHANNEL_DIGITAL2, CHANNEL_TYPE_UID_BAE_DIGITAL_OUT, "Digital Out Pin 2")); + break; + case CONFIG_BAE_PIN_PWM: + wantedChannel + .add(new OwChannelConfig(CHANNEL_PWM_DUTY3, CHANNEL_TYPE_UID_BAE_PWM_DUTY, "Duty Cycle PWM 3")); + wantedChannel.add(new OwChannelConfig(CHANNEL_PWM_FREQ1, CHANNEL_TYPE_UID_BAE_PWM_FREQUENCY, + "Frequency PWM 1/3")); + break; + default: + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "unknown configuration option for pin 2"); + return; + } + + // Pin6: + switch (configuration.pin6) { + case CONFIG_BAE_PIN_DISABLED: + break; + case CONFIG_BAE_PIN_PIO: + wantedChannel.add(new OwChannelConfig(CHANNEL_DIGITAL6, CHANNEL_TYPE_UID_BAE_PIO, "PIO Pin 6")); + break; + case CONFIG_BAE_PIN_PWM: + wantedChannel + .add(new OwChannelConfig(CHANNEL_PWM_DUTY4, CHANNEL_TYPE_UID_BAE_PWM_DUTY, "Duty Cycle PWM 4")); + wantedChannel.add(new OwChannelConfig(CHANNEL_PWM_FREQ2, CHANNEL_TYPE_UID_BAE_PWM_FREQUENCY, + "Frequency PWM 2/4")); + break; + default: + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "unknown configuration option for pin 6"); + return; + } + + // Pin7: + switch (configuration.pin7) { + case CONFIG_BAE_PIN_DISABLED: + break; + case CONFIG_BAE_PIN_ANALOG: + wantedChannel.add(new OwChannelConfig(CHANNEL_VOLTAGE, CHANNEL_TYPE_UID_BAE_ANALOG, "Analog Input")); + break; + case CONFIG_BAE_PIN_OUT: + wantedChannel.add( + new OwChannelConfig(CHANNEL_DIGITAL7, CHANNEL_TYPE_UID_BAE_DIGITAL_OUT, "Digital Out Pin 7")); + break; + case CONFIG_BAE_PIN_PWM: + wantedChannel + .add(new OwChannelConfig(CHANNEL_PWM_DUTY2, CHANNEL_TYPE_UID_BAE_PWM_DUTY, "Duty Cycle PWM 2")); + wantedChannel.add(new OwChannelConfig(CHANNEL_PWM_FREQ2, CHANNEL_TYPE_UID_BAE_PWM_FREQUENCY, + "Frequency PWM 2/4")); + break; + default: + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "unknown configuration option for pin 7"); + return; + } + + // Pin8: + switch (configuration.pin8) { + case CONFIG_BAE_PIN_DISABLED: + break; + case CONFIG_BAE_PIN_IN: + wantedChannel.add(new OwChannelConfig(CHANNEL_DIGITAL8, CHANNEL_TYPE_UID_BAE_DIN, "Digital In Pin 8")); + break; + case CONFIG_BAE_PIN_OUT: + wantedChannel + .add(new OwChannelConfig(CHANNEL_DIGITAL8, CHANNEL_TYPE_UID_BAE_DOUT, "Digital Out Pin 8")); + break; + case CONFIG_BAE_PIN_PWM: + wantedChannel + .add(new OwChannelConfig(CHANNEL_PWM_DUTY1, CHANNEL_TYPE_UID_BAE_PWM_DUTY, "Duty Cycle PWM 1")); + wantedChannel.add(new OwChannelConfig(CHANNEL_PWM_FREQ1, CHANNEL_TYPE_UID_BAE_PWM_FREQUENCY, + "Frequency PWM 1/3")); + + break; + default: + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "unknown configuration option for pin 8"); + return; + } + + ThingBuilder thingBuilder = editThing(); + + // remove unwanted channels + Set existingChannelIds = thing.getChannels().stream().map(channel -> channel.getUID().getId()) + .collect(Collectors.toSet()); + Set wantedChannelIds = wantedChannel.stream().map(channelConfig -> channelConfig.channelId) + .collect(Collectors.toSet()); + existingChannelIds.stream().filter(channelId -> !wantedChannelIds.contains(channelId)) + .forEach(channelId -> removeChannelIfExisting(thingBuilder, channelId)); + + // add or update wanted channels + wantedChannel.stream().forEach(channelConfig -> { + addChannelIfMissingAndEnable(thingBuilder, channelConfig); + }); + + updateThing(thingBuilder.build()); + + try { + sensors.get(0).configureChannels(); + } catch (OwException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); + return; + } + + validConfig = true; + updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE); + } + + @Override + public void updateSensorProperties(OwserverBridgeHandler bridgeHandler) throws OwException { + Map properties = editProperties(); + + sensorType = BAE0910.getDeviceSubType(bridgeHandler, sensorId); + + properties.put(PROPERTY_MODELID, sensorType.toString()); + properties.put(PROPERTY_VENDOR, "Brain4home"); + + updateProperties(properties); + + logger.trace("updated modelid/vendor to {} / {}", sensorType.name(), "Brain4home"); + } +} diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/BasicMultisensorThingHandler.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/BasicMultisensorThingHandler.java index 8f6815ca5437e..a8e39e09ba4b1 100644 --- a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/BasicMultisensorThingHandler.java +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/BasicMultisensorThingHandler.java @@ -26,11 +26,14 @@ import org.openhab.binding.onewire.internal.DS2438Configuration; import org.openhab.binding.onewire.internal.OwDynamicStateDescriptionProvider; import org.openhab.binding.onewire.internal.OwException; +import org.openhab.binding.onewire.internal.config.MstxHandlerConfiguration; import org.openhab.binding.onewire.internal.device.DS1923; import org.openhab.binding.onewire.internal.device.DS2438; import org.openhab.binding.onewire.internal.device.DS2438.CurrentSensorType; import org.openhab.binding.onewire.internal.device.DS2438.LightSensorType; import org.openhab.binding.onewire.internal.device.OwSensorType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The {@link BasicMultisensorThingHandler} is responsible for handling DS2438/DS1923 based multisensors (single @@ -40,6 +43,7 @@ */ @NonNullByDefault public class BasicMultisensorThingHandler extends OwBaseThingHandler { + public Logger logger = LoggerFactory.getLogger(BasicMultisensorThingHandler.class); public static final Set SUPPORTED_THING_TYPES = Collections.unmodifiableSet( Stream.of(THING_TYPE_MS_TX, THING_TYPE_MS_TH, THING_TYPE_MS_TV).collect(Collectors.toSet())); public static final Set SUPPORTED_SENSOR_TYPES = Collections @@ -53,7 +57,7 @@ public BasicMultisensorThingHandler(Thing thing, @Override public void initialize() { - // TODO: remove after 0.11.0 release + // TODO: remove after 2.5.0 release if (!thing.getThingTypeUID().equals(THING_TYPE_MS_TX)) { changeThingType(THING_TYPE_MS_TX, getConfig()); } @@ -62,6 +66,13 @@ public void initialize() { return; } + MstxHandlerConfiguration configuration = getConfig().as(MstxHandlerConfiguration.class); + if (configuration.manualsensor != null && sensorType != configuration.manualsensor) { + logger.debug("sensorType override for thing {}: old={}, new={}", thing.getUID(), sensorType, + configuration.manualsensor); + sensorType = configuration.manualsensor; + } + // add sensors if (sensorType == OwSensorType.DS1923) { sensors.add(new DS1923(sensorId, this)); @@ -112,4 +123,5 @@ public void updateSensorProperties(OwserverBridgeHandler bridgeHandler) throws O updateProperties(properties); } + } diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/BasicThingHandler.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/BasicThingHandler.java index 596451ac5554d..700985c51ae1a 100644 --- a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/BasicThingHandler.java +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/BasicThingHandler.java @@ -14,14 +14,12 @@ import static org.openhab.binding.onewire.internal.OwBindingConstants.*; -import java.math.BigDecimal; import java.util.Collections; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.ChannelUID; @@ -64,11 +62,10 @@ public BasicThingHandler(Thing thing, OwDynamicStateDescriptionProvider dynamicS @Override public void initialize() { - // TODO: remove after 0.11.0 release + // TODO: remove after 2.5.0 release if (!thing.getThingTypeUID().equals(THING_TYPE_BASIC)) { changeThingType(THING_TYPE_BASIC, getConfig()); } - Configuration configuration = getConfig(); if (!super.configureThingHandler()) { return; @@ -84,36 +81,25 @@ public void initialize() { case DS1420: case DS2401: sensors.add(new DS2401(sensorId, this)); - refreshInterval = 10 * 1000; break; case DS2405: sensors.add(new DS2405(sensorId, this)); - refreshInterval = 10 * 1000; break; case DS2406: case DS2413: sensors.add(new DS2406_DS2413(sensorId, this)); - refreshInterval = 10 * 1000; break; case DS2408: sensors.add(new DS2408(sensorId, this)); - refreshInterval = 10 * 1000; break; case DS2423: sensors.add(new DS2423(sensorId, this)); - refreshInterval = 10 * 1000; break; default: throw new IllegalArgumentException( "unsupported sensorType " + sensorType.name() + ", this should have been checked before!"); } - if (configuration.containsKey(CONFIG_REFRESH)) { - // override default with configured value - // TODO: remove default from OwBaseThingHandler - refreshInterval = ((BigDecimal) configuration.get(CONFIG_REFRESH)).intValue() * 1000; - } - scheduler.execute(() -> { configureThingChannels(); }); diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/OwBaseThingHandler.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/OwBaseThingHandler.java index 29c760c41039d..c98e4a6f5b23f 100644 --- a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/OwBaseThingHandler.java +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/OwBaseThingHandler.java @@ -14,7 +14,6 @@ import static org.openhab.binding.onewire.internal.OwBindingConstants.*; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -46,6 +45,7 @@ import org.openhab.binding.onewire.internal.OwDynamicStateDescriptionProvider; import org.openhab.binding.onewire.internal.OwException; import org.openhab.binding.onewire.internal.SensorId; +import org.openhab.binding.onewire.internal.config.BaseHandlerConfiguration; import org.openhab.binding.onewire.internal.device.AbstractOwDevice; import org.openhab.binding.onewire.internal.device.OwChannelConfig; import org.openhab.binding.onewire.internal.device.OwSensorType; @@ -115,7 +115,7 @@ public void initialize() { } protected boolean configureThingHandler() { - Configuration configuration = getConfig(); + BaseHandlerConfiguration configuration = getConfig().as(BaseHandlerConfiguration.class); Map properties = thing.getProperties(); if (getBridge() == null) { @@ -124,10 +124,9 @@ protected boolean configureThingHandler() { } sensors.clear(); - if (configuration.get(CONFIG_ID) != null) { - String sensorId = (String) configuration.get(CONFIG_ID); + if (configuration.id != null) { try { - this.sensorId = new SensorId(sensorId); + this.sensorId = new SensorId(configuration.id); } catch (IllegalArgumentException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "sensor id format mismatch"); return false; @@ -137,11 +136,7 @@ protected boolean configureThingHandler() { return false; } - if (configuration.get(CONFIG_REFRESH) != null) { - refreshInterval = ((BigDecimal) configuration.get(CONFIG_REFRESH)).intValue() * 1000; - } else { - refreshInterval = 300 * 1000; - } + refreshInterval = configuration.refresh * 1000; if (thing.getChannel(CHANNEL_PRESENT) != null) { showPresence = true; @@ -322,7 +317,7 @@ protected void updateSensorProperties() { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "required properties missing"); bridgeHandler.scheduleForPropertiesUpdate(thing); - + } /** diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/OwserverBridgeHandler.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/OwserverBridgeHandler.java index 671e50ae69424..97b362eecf4bc 100644 --- a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/OwserverBridgeHandler.java +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/handler/OwserverBridgeHandler.java @@ -62,8 +62,6 @@ public class OwserverBridgeHandler extends BaseBridgeHandler { public static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_OWSERVER); private final Logger logger = LoggerFactory.getLogger(OwserverBridgeHandler.class); - - // set by implementation when bridge is ready protected boolean refreshable = false; protected ScheduledFuture refreshTask = scheduler.scheduleWithFixedDelay(this::refresh, 1, 1000, diff --git a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/owserver/OwserverDeviceParameter.java b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/owserver/OwserverDeviceParameter.java index 92c84e24264f2..4b8477542f3be 100644 --- a/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/owserver/OwserverDeviceParameter.java +++ b/bundles/org.openhab.binding.onewire/src/main/java/org/openhab/binding/onewire/internal/owserver/OwserverDeviceParameter.java @@ -13,6 +13,7 @@ package org.openhab.binding.onewire.internal.owserver; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.onewire.internal.SensorId; /** @@ -67,4 +68,22 @@ public String getPath(SensorId sensorId) { public String toString() { return prefix + "/sensorId" + path; } + + @Override + public int hashCode() { + return toString().hashCode(); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof OwserverDeviceParameter)) { + return false; + } + + return ((OwserverDeviceParameter) o).toString().equals(toString()); + } } diff --git a/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/config/config.xml b/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/config/config.xml index 5b7bcdbc5fe68..a5ae319c62a7a 100644 --- a/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/config/config.xml +++ b/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/config/config.xml @@ -31,4 +31,32 @@ false + + + + Sensor ID in format: xx.xxxxxxxxxxxx or a full path including hubs/branches + true + + + + Time in seconds after which the thing is refreshed + 300 + s + false + + + + Overrides detected sensor type + + + + + + + + true + false + true + + diff --git a/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/DigitalIO.xml b/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/DigitalIO.xml deleted file mode 100644 index 3ca1464aab776..0000000000000 --- a/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/DigitalIO.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - Digital I/O device (DS2405: single, DS2406/DS2413: dual, DS2408: octal) - - - - - - - - A dual digital I/O device (DS2406, DS2413) - - - - - - - - A octal digital I/O device (DS2408) - - - diff --git a/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/bae09x.xml b/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/bae09x.xml new file mode 100644 index 0000000000000..25e075bb27b62 --- /dev/null +++ b/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/bae09x.xml @@ -0,0 +1,168 @@ + + + + + + + + 1-wire multisensor (BAE0910-based) + + 1 + + + + + Sensor ID in format: xx.xxxxxxxxxxxx) + true + + + + Time in seconds after which the thing is refreshed + 300 + s + + + + + + + + counter + true + + + + + + + + + output + true + + + + + + + + + pio + true + + + + + + + + + + analog + true + + + + + + + + + + output + true + + + + + + Number:Frequency + + Frequency of PWM output in Hz + + + + defines the frequency range of PWM output + + + + + + + + + + + 0 + true + + + + false + + + + + Number:Dimensionless + + Duty cycle of PWM output in % + + + + Switch + + Programmable I/O channel + + + + + + + + input + true + + + + + + + + + disabled + true + + + + + Switch + + + + Switch + + + + + Number:ElectricPotential + + Analog input (ADC) + + + + + High resolution Mode (10bit) + false + + + + + + Number + + + + diff --git a/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/deprecated.xml b/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/deprecated.xml index 44ca15b764759..77a368e22f54c 100644 --- a/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/deprecated.xml +++ b/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/deprecated.xml @@ -46,4 +46,28 @@ A 1-wire multisensor (DS2438-based) + + + + + + Digital I/O device (DS2405: single, DS2406/DS2413: dual, DS2408: octal) + + + + + + + + A dual digital I/O device (DS2406, DS2413) + + + + + + + + A octal digital I/O device (DS2408) + + diff --git a/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/multisensor.xml b/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/multisensor.xml index 868ca90e21f27..00018ee7d90e3 100644 --- a/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/multisensor.xml +++ b/bundles/org.openhab.binding.onewire/src/main/resources/ESH-INF/thing/multisensor.xml @@ -9,7 +9,7 @@ A 1-wire multisensor (DS1923/DS2438-based) - + @@ -84,7 +84,7 @@ - + A 1-wire multisensor (EDS00xx-based) 1 diff --git a/bundles/org.openhab.binding.onewire/src/main/resources/accepted_itemtypes.properties b/bundles/org.openhab.binding.onewire/src/main/resources/accepted_itemtypes.properties index bc6e6feb519d0..e4f8dfd8f2549 100644 --- a/bundles/org.openhab.binding.onewire/src/main/resources/accepted_itemtypes.properties +++ b/bundles/org.openhab.binding.onewire/src/main/resources/accepted_itemtypes.properties @@ -19,4 +19,11 @@ digital4 = Switch digital5 = Switch digital6 = Switch digital7 = Switch +digital8 = Switch present = Switch +pwmduty1 = Number:Dimensionsless +pwmduty2 = Number:Dimensionsless +pwmduty3 = Number:Dimensionsless +pwmduty4 = Number:Dimensionsless +pwmfreq1 = Number:Frequency +pwmfreq2 = Number:Frequency diff --git a/bundles/org.openhab.binding.onewire/src/main/resources/sensor.properties b/bundles/org.openhab.binding.onewire/src/main/resources/sensor.properties index 05ac5434e9adb..bc60abd5e3059 100644 --- a/bundles/org.openhab.binding.onewire/src/main/resources/sensor.properties +++ b/bundles/org.openhab.binding.onewire/src/main/resources/sensor.properties @@ -83,3 +83,7 @@ EDS0067.thingtype = edsenv EDS0068.channels = temperature:temperature:,humidity:humidity:,absolutehumidity:abshumidity:,dewpoint:temperature:Dewpoint,pressure:pressure:,light:light: EDS0068.label = Multisensor EDS EDS0068.thingtype = edsenv +# BAE091x +BAE0910.channels = +BAE0910.label = Multisensor BAE0910 +BAE0910.thingtype = bae091x \ No newline at end of file diff --git a/bundles/org.openhab.binding.onewire/src/test/java/org/openhab/binding/onewire/CompletenessTest.java b/bundles/org.openhab.binding.onewire/src/test/java/org/openhab/binding/onewire/CompletenessTest.java index 1b674ac31594f..5ac7d3caf0599 100644 --- a/bundles/org.openhab.binding.onewire/src/test/java/org/openhab/binding/onewire/CompletenessTest.java +++ b/bundles/org.openhab.binding.onewire/src/test/java/org/openhab/binding/onewire/CompletenessTest.java @@ -27,6 +27,7 @@ import org.openhab.binding.onewire.internal.OwBindingConstants; import org.openhab.binding.onewire.internal.device.OwSensorType; import org.openhab.binding.onewire.internal.handler.AdvancedMultisensorThingHandler; +import org.openhab.binding.onewire.internal.handler.BAE091xSensorThingHandler; import org.openhab.binding.onewire.internal.handler.BasicMultisensorThingHandler; import org.openhab.binding.onewire.internal.handler.BasicThingHandler; import org.openhab.binding.onewire.internal.handler.EDSSensorThingHandler; @@ -38,16 +39,15 @@ */ public class CompletenessTest { // internal/temporary types, DS2409 (MicroLAN Coupler), DS2431 (EEPROM) - private static final Set IGNORED_SENSOR_TYPES = Collections.unmodifiableSet(Stream - .of(OwSensorType.DS2409, OwSensorType.DS2431, OwSensorType.EDS, OwSensorType.MS_TH_S, OwSensorType.UNKNOWN) - .collect(Collectors.toSet())); + private static final Set IGNORED_SENSOR_TYPES = Collections + .unmodifiableSet(Stream.of(OwSensorType.DS2409, OwSensorType.DS2431, OwSensorType.EDS, OwSensorType.MS_TH_S, + OwSensorType.BAE, OwSensorType.BAE0911, OwSensorType.UNKNOWN).collect(Collectors.toSet())); - private static final Set THINGHANDLER_SENSOR_TYPES = Collections - .unmodifiableSet(Stream - .of(AdvancedMultisensorThingHandler.SUPPORTED_SENSOR_TYPES, - BasicMultisensorThingHandler.SUPPORTED_SENSOR_TYPES, - BasicThingHandler.SUPPORTED_SENSOR_TYPES, EDSSensorThingHandler.SUPPORTED_SENSOR_TYPES) - .flatMap(Set::stream).collect(Collectors.toSet())); + private static final Set THINGHANDLER_SENSOR_TYPES = Collections.unmodifiableSet(Stream + .of(AdvancedMultisensorThingHandler.SUPPORTED_SENSOR_TYPES, + BasicMultisensorThingHandler.SUPPORTED_SENSOR_TYPES, BasicThingHandler.SUPPORTED_SENSOR_TYPES, + EDSSensorThingHandler.SUPPORTED_SENSOR_TYPES, BAE091xSensorThingHandler.SUPPORTED_SENSOR_TYPES) + .flatMap(Set::stream).collect(Collectors.toSet())); private static final Set DEPRECATED_THING_TYPES = Collections.unmodifiableSet(Stream .of(OwBindingConstants.THING_TYPE_MS_TH, OwBindingConstants.THING_TYPE_MS_TV, diff --git a/bundles/org.openhab.binding.onewire/src/test/java/org/openhab/binding/onewire/device/BAE0910Test.java b/bundles/org.openhab.binding.onewire/src/test/java/org/openhab/binding/onewire/device/BAE0910Test.java new file mode 100644 index 0000000000000..2f3b2ead290c0 --- /dev/null +++ b/bundles/org.openhab.binding.onewire/src/test/java/org/openhab/binding/onewire/device/BAE0910Test.java @@ -0,0 +1,339 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.onewire.device; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.openhab.binding.onewire.internal.OwBindingConstants.*; + +import java.util.Arrays; +import java.util.BitSet; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.State; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.openhab.binding.onewire.internal.OwException; +import org.openhab.binding.onewire.internal.device.BAE0910; +import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter; + +/** + * Tests cases for {@link BAE0910}. + * + * @author Jan N. Klug - Initial contribution + */ +public class BAE0910Test extends DeviceTestParent { + @Before + public void setupMocks() { + setupMocks(THING_TYPE_BAE091X); + deviceTestClazz = BAE0910.class; + } + + // pin 1: counter + + @Test + public void counter() { + addChannel(CHANNEL_COUNTER, "Number"); + instantiateDevice(); + + try { + Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON); + Mockito.when( + mockBridgeHandler.readDecimalType(eq(testSensorId), eq(new OwserverDeviceParameter("/counter")))) + .thenReturn(new DecimalType(34567)); + + testDevice.enableChannel(CHANNEL_COUNTER); + ((BAE0910) testDevice).configureChannels(mockBridgeHandler); + + // refresh + ArgumentCaptor stateArgumentCaptor = ArgumentCaptor.forClass(State.class); + testDevice.refresh(mockBridgeHandler, true); + inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), + eq(new OwserverDeviceParameter("/counter"))); + inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_COUNTER), stateArgumentCaptor.capture()); + assertEquals(new DecimalType(34567), stateArgumentCaptor.getValue()); + + // write + assertFalse( + ((BAE0910) testDevice).writeChannel(mockBridgeHandler, CHANNEL_COUNTER, new DecimalType(12345))); + + inOrder.verifyNoMoreInteractions(); + } catch (OwException e) { + Assert.fail("caught unexpected OwException"); + } + } + + // pin 2: digital2 or pwm1 + + @Test + public void digitalOut2() { + addChannel(CHANNEL_DIGITAL2, "Switch"); + digitalBaseChannel(CHANNEL_DIGITAL2, bitSet(3, 4), 0, "/out", bitSet(0), true); + } + + @Test + public void pwm4() { + pwmBaseChannel(CHANNEL_PWM_FREQ2, CHANNEL_PWM_DUTY4, "/period2", "/duty4", 2); + } + + // pin 6: pio or pwm 3 + + @Test + public void digital6PioIn() { + Map channelConfig = new HashMap<>(); + channelConfig.put("pulldevice", "pulldown"); + channelConfig.put("mode", "input"); + addChannel(CHANNEL_DIGITAL6, "Switch", new Configuration(channelConfig)); + digitalBaseChannel(CHANNEL_DIGITAL6, bitSet(1, 2, 3, 4), 1, "/pio", bitSet(0), false); + } + + @Test + public void digital6PioOut() { + Map channelConfig = new HashMap<>(); + channelConfig.put("mode", "output"); + addChannel(CHANNEL_DIGITAL6, "Switch", new Configuration(channelConfig)); + digitalBaseChannel(CHANNEL_DIGITAL6, bitSet(0, 3, 4), 1, "/pio", bitSet(0), true); + } + + @Test + public void pwm3() { + pwmBaseChannel(CHANNEL_PWM_FREQ1, CHANNEL_PWM_DUTY3, "/period1", "/duty3", 1); + } + + // pin 7: analog, output, pwm2 + + @Test + public void analog() { + Map channelConfig = new HashMap<>(); + channelConfig.put("hires", "true"); + addChannel(CHANNEL_VOLTAGE, "Number:ElectricPotential", new Configuration(channelConfig)); + instantiateDevice(); + + try { + Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON); + Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), eq(new OwserverDeviceParameter("/adc")))) + .thenReturn(new DecimalType(5.2)); + + testDevice.enableChannel(CHANNEL_VOLTAGE); + ((BAE0910) testDevice).configureChannels(mockBridgeHandler); + + // test configuration + assertEquals(bitSet(3, 4), checkConfiguration(2)); + + // refresh + ArgumentCaptor stateArgumentCaptor = ArgumentCaptor.forClass(State.class); + testDevice.refresh(mockBridgeHandler, true); + inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), + eq(new OwserverDeviceParameter("/adc"))); + inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_VOLTAGE), stateArgumentCaptor.capture()); + assertEquals(new QuantityType<>("5.2 V"), stateArgumentCaptor.getValue()); + + // write (should fail) + assertFalse( + ((BAE0910) testDevice).writeChannel(mockBridgeHandler, CHANNEL_VOLTAGE, new QuantityType<>("3 V"))); + + inOrder.verifyNoMoreInteractions(); + } catch (OwException e) { + Assert.fail("caught unexpected OwException"); + } + } + + @Test + public void digitalOut7() { + addChannel(CHANNEL_DIGITAL7, "Switch"); + digitalBaseChannel(CHANNEL_DIGITAL7, bitSet(4), 4, "/tpm2c", bitSet(4, 7), true); + } + + @Test + public void pwm2() { + pwmBaseChannel(CHANNEL_PWM_FREQ2, CHANNEL_PWM_DUTY2, "/period2", "/duty2", 2); + } + + // pin 8: digital in, digital out or pwm + + @Test + public void digitalIn8() { + addChannel(CHANNEL_DIGITAL8, "Switch", new ChannelTypeUID(BINDING_ID, "bae-in")); + digitalBaseChannel(CHANNEL_DIGITAL8, bitSet(4, 5), 3, "/tpm1c", bitSet(4, 5, 7), false); + } + + @Test + public void digitalOut8() { + addChannel(CHANNEL_DIGITAL8, "Switch"); + digitalBaseChannel(CHANNEL_DIGITAL8, bitSet(4), 3, "/tpm1c", bitSet(4, 7), true); + } + + @Test + public void pwm1() { + pwmBaseChannel(CHANNEL_PWM_FREQ1, CHANNEL_PWM_DUTY1, "/period1", "/duty1", 1); + } + + /** + * base test for digital channels + * + * @param channel channel name + * @param channelConfig channel config (or null) + * @param configBitSet expected config register + * @param configRegister config register number + * @param channelParam channel parameter + * @param returnBitSet which bitset should be returned on read + * @param isOutput if this channel is an output + */ + private void digitalBaseChannel(String channel, BitSet configBitSet, int configRegister, String channelParam, + BitSet returnBitSet, boolean isOutput) { + instantiateDevice(); + + try { + Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON); + Mockito.when(mockBridgeHandler.readBitSet(eq(testSensorId), eq(new OwserverDeviceParameter(channelParam)))) + .thenReturn(returnBitSet); + + testDevice.enableChannel(channel); + ((BAE0910) testDevice).configureChannels(mockBridgeHandler); + + // test configuration + assertEquals(configBitSet, checkConfiguration(configRegister)); + + // refresh + ArgumentCaptor stateArgumentCaptor = ArgumentCaptor.forClass(State.class); + testDevice.refresh(mockBridgeHandler, true); + inOrder.verify(mockBridgeHandler).readBitSet(eq(testSensorId), + eq(new OwserverDeviceParameter(channelParam))); + inOrder.verify(mockThingHandler).postUpdate(eq(channel), stateArgumentCaptor.capture()); + assertEquals(OnOffType.ON, stateArgumentCaptor.getValue()); + + // write + if (isOutput) { + ArgumentCaptor bitSetArgumentCaptor = ArgumentCaptor.forClass(BitSet.class); + assertTrue(((BAE0910) testDevice).writeChannel(mockBridgeHandler, channel, OnOffType.ON)); + inOrder.verify(mockBridgeHandler).writeBitSet(eq(testSensorId), + eq(new OwserverDeviceParameter(channelParam)), bitSetArgumentCaptor.capture()); + assertEquals(returnBitSet, bitSetArgumentCaptor.getValue()); + } else { + assertFalse(((BAE0910) testDevice).writeChannel(mockBridgeHandler, channel, OnOffType.ON)); + } + + inOrder.verifyNoMoreInteractions(); + } catch (OwException e) { + Assert.fail("caught unexpected OwException"); + } + } + + /** + * base test case for PWM channels + * + * @param freqChannel channel name for frequency + * @param dutyChannel channel name for duty cycle + * @param freqParam owfs parameter for frequency + * @param dutyParam owfs parameter for duty cycle + * @param registerIndex index for TPM configuration register + */ + private void pwmBaseChannel(String freqChannel, String dutyChannel, String freqParam, String dutyParam, + int registerIndex) { + Map channelConfig = new HashMap<>(); + channelConfig.put("prescaler", 5); + addChannel(freqChannel, "Number:Frequency", new Configuration(channelConfig)); + addChannel(dutyChannel, "Number:Dimensionless"); + instantiateDevice(); + + try { + Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON); + Mockito.when( + mockBridgeHandler.readDecimalType(eq(testSensorId), eq(new OwserverDeviceParameter(freqParam)))) + .thenReturn(new DecimalType(32768)); + Mockito.when( + mockBridgeHandler.readDecimalType(eq(testSensorId), eq(new OwserverDeviceParameter(dutyParam)))) + .thenReturn(new DecimalType(16384)); + + testDevice.enableChannel(freqChannel); + testDevice.enableChannel(dutyChannel); + ((BAE0910) testDevice).configureChannels(mockBridgeHandler); + + // test configuration + assertEquals(bitSet(0, 2), checkConfiguration(registerIndex + 2)); + + // refresh + ArgumentCaptor stateArgumentCaptor = ArgumentCaptor.forClass(State.class); + testDevice.refresh(mockBridgeHandler, true); + inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), + eq(new OwserverDeviceParameter(freqParam))); + inOrder.verify(mockThingHandler).postUpdate(eq(freqChannel), stateArgumentCaptor.capture()); + assertEquals(new QuantityType<>("15.2587890625 Hz"), stateArgumentCaptor.getValue()); + inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), + eq(new OwserverDeviceParameter(dutyParam))); + inOrder.verify(mockThingHandler).postUpdate(eq(dutyChannel), stateArgumentCaptor.capture()); + assertEquals(new QuantityType<>("50 %"), stateArgumentCaptor.getValue()); + + // write + ArgumentCaptor decimalTypeArgumentCaptor = ArgumentCaptor.forClass(DecimalType.class); + assertTrue(((BAE0910) testDevice).writeChannel(mockBridgeHandler, freqChannel, + new QuantityType<>("50000 Hz"))); + inOrder.verify(mockBridgeHandler).writeDecimalType(eq(testSensorId), + eq(new OwserverDeviceParameter(freqParam)), decimalTypeArgumentCaptor.capture()); + assertEquals(new DecimalType(10), decimalTypeArgumentCaptor.getValue()); + ((BAE0910) testDevice).writeChannel(mockBridgeHandler, dutyChannel, new QuantityType<>("25 %")); + inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), + eq(new OwserverDeviceParameter(freqParam))); + inOrder.verify(mockBridgeHandler).writeDecimalType(eq(testSensorId), + eq(new OwserverDeviceParameter(dutyParam)), decimalTypeArgumentCaptor.capture()); + assertEquals(new DecimalType(8192), decimalTypeArgumentCaptor.getValue()); + + inOrder.verifyNoMoreInteractions(); + } catch (OwException e) { + Assert.fail("caught unexpected OwException"); + } + } + + /** + * check if all registers are written and return one + * + * @param registerIndex number of register to return + * @return this register's BitSet + * @throws OwException + */ + private BitSet checkConfiguration(int registerIndex) throws OwException { + ArgumentCaptor configArgumentCaptor = ArgumentCaptor.forClass(BitSet.class); + inOrder.verify(mockBridgeHandler).writeBitSet(eq(testSensorId), eq(new OwserverDeviceParameter("/outc")), + configArgumentCaptor.capture()); + inOrder.verify(mockBridgeHandler).writeBitSet(eq(testSensorId), eq(new OwserverDeviceParameter("/pioc")), + configArgumentCaptor.capture()); + inOrder.verify(mockBridgeHandler).writeBitSet(eq(testSensorId), eq(new OwserverDeviceParameter("/adcc")), + configArgumentCaptor.capture()); + inOrder.verify(mockBridgeHandler).writeBitSet(eq(testSensorId), eq(new OwserverDeviceParameter("/tpm1c")), + configArgumentCaptor.capture()); + inOrder.verify(mockBridgeHandler).writeBitSet(eq(testSensorId), eq(new OwserverDeviceParameter("/tpm2c")), + configArgumentCaptor.capture()); + return configArgumentCaptor.getAllValues().get(registerIndex); + } + + /** + * BitSet with pre-set bits + * + * @param bits which bits to set + * @return the BitSet + */ + private BitSet bitSet(int... bits) { + BitSet bitSet = new BitSet(8); + Arrays.stream(bits).forEach(b -> bitSet.set(b)); + return bitSet; + } +} diff --git a/bundles/org.openhab.binding.onewire/src/test/java/org/openhab/binding/onewire/device/DeviceTestParent.java b/bundles/org.openhab.binding.onewire/src/test/java/org/openhab/binding/onewire/device/DeviceTestParent.java index 6a31b5343f15a..a8d204c7cd4c2 100644 --- a/bundles/org.openhab.binding.onewire/src/test/java/org/openhab/binding/onewire/device/DeviceTestParent.java +++ b/bundles/org.openhab.binding.onewire/src/test/java/org/openhab/binding/onewire/device/DeviceTestParent.java @@ -12,7 +12,6 @@ * SPDX-License-Identifier: EPL-2.0 */ - import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.openhab.binding.onewire.internal.OwBindingConstants.CHANNEL_PRESENT; @@ -28,6 +27,7 @@ import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; import org.junit.Assert; import org.mockito.InOrder; import org.mockito.Mockito; @@ -35,8 +35,8 @@ import org.openhab.binding.onewire.internal.SensorId; import org.openhab.binding.onewire.internal.device.AbstractOwDevice; import org.openhab.binding.onewire.internal.device.OwSensorType; -import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler; import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler; +import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler; /** * Abtract test class for onewire devices. @@ -80,6 +80,12 @@ public void addChannel(String channelId, String itemType, Configuration channelC Mockito.when(mockThing.getChannel(channelId)).thenReturn(channel); } + public void addChannel(String channelId, String itemType, ChannelTypeUID channelTypeUID) { + Channel channel = ChannelBuilder.create(new ChannelUID(mockThing.getUID(), channelId), itemType) + .withType(channelTypeUID).build(); + Mockito.when(mockThing.getChannel(channelId)).thenReturn(channel); + } + public @Nullable AbstractOwDevice instantiateDevice() { try { Constructor constructor = deviceTestClazz.getConstructor(SensorId.class, OwBaseThingHandler.class); diff --git a/bundles/org.openhab.binding.onewiregpio/.classpath b/bundles/org.openhab.binding.onewiregpio/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.onewiregpio/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.onewiregpio/.project b/bundles/org.openhab.binding.onewiregpio/.project new file mode 100644 index 0000000000000..e80f7109953c4 --- /dev/null +++ b/bundles/org.openhab.binding.onewiregpio/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.onewiregpio + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.feed.test/NOTICE b/bundles/org.openhab.binding.onewiregpio/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.feed.test/NOTICE rename to bundles/org.openhab.binding.onewiregpio/NOTICE diff --git a/addons/binding/org.openhab.binding.onewiregpio/README.md b/bundles/org.openhab.binding.onewiregpio/README.md similarity index 100% rename from addons/binding/org.openhab.binding.onewiregpio/README.md rename to bundles/org.openhab.binding.onewiregpio/README.md diff --git a/bundles/org.openhab.binding.onewiregpio/pom.xml b/bundles/org.openhab.binding.onewiregpio/pom.xml new file mode 100644 index 0000000000000..e1df19b6cc82e --- /dev/null +++ b/bundles/org.openhab.binding.onewiregpio/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.onewiregpio + + openHAB Add-ons :: Bundles :: OneWireGPIO Binding + + diff --git a/addons/binding/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/OneWireGPIOBindingConstants.java b/bundles/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/OneWireGPIOBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/OneWireGPIOBindingConstants.java rename to bundles/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/OneWireGPIOBindingConstants.java diff --git a/addons/binding/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/OneWireGPIOHandlerFactory.java b/bundles/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/OneWireGPIOHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/OneWireGPIOHandlerFactory.java rename to bundles/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/OneWireGPIOHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/handler/OneWireGPIOHandler.java b/bundles/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/handler/OneWireGPIOHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/handler/OneWireGPIOHandler.java rename to bundles/org.openhab.binding.onewiregpio/src/main/java/org/openhab/binding/onewiregpio/internal/handler/OneWireGPIOHandler.java diff --git a/addons/binding/org.openhab.binding.onewiregpio/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.onewiregpio/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.onewiregpio/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.onewiregpio/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.onewiregpio/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.onewiregpio/src/main/resources/ESH-INF/thing/thing-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.onewiregpio/ESH-INF/thing/thing-types.xml rename to bundles/org.openhab.binding.onewiregpio/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/bundles/org.openhab.binding.onkyo/.classpath b/bundles/org.openhab.binding.onkyo/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.onkyo/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.onkyo/.project b/bundles/org.openhab.binding.onkyo/.project new file mode 100644 index 0000000000000..547d58799bbf0 --- /dev/null +++ b/bundles/org.openhab.binding.onkyo/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.onkyo + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.feed/NOTICE b/bundles/org.openhab.binding.onkyo/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.feed/NOTICE rename to bundles/org.openhab.binding.onkyo/NOTICE diff --git a/addons/binding/org.openhab.binding.onkyo/README.md b/bundles/org.openhab.binding.onkyo/README.md similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/README.md rename to bundles/org.openhab.binding.onkyo/README.md diff --git a/bundles/org.openhab.binding.onkyo/pom.xml b/bundles/org.openhab.binding.onkyo/pom.xml new file mode 100644 index 0000000000000..026073d13038a --- /dev/null +++ b/bundles/org.openhab.binding.onkyo/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.onkyo + + openHAB Add-ons :: Bundles :: Onkyo Binding + + diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoAlbumArt.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoAlbumArt.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoAlbumArt.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoAlbumArt.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoBindingConstants.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoBindingConstants.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoBindingConstants.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoConnection.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoConnection.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoConnection.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoConnection.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoEventListener.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoEventListener.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoEventListener.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoEventListener.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoHandlerFactory.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoHandlerFactory.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/OnkyoHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/ServiceType.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/ServiceType.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/ServiceType.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/ServiceType.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/automation/modules/OnkyoThingActionsService.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/automation/modules/OnkyoThingActionsService.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/automation/modules/OnkyoThingActionsService.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/automation/modules/OnkyoThingActionsService.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/config/OnkyoDeviceConfiguration.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/config/OnkyoDeviceConfiguration.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/config/OnkyoDeviceConfiguration.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/config/OnkyoDeviceConfiguration.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/discovery/OnkyoUpnpDiscoveryParticipant.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/discovery/OnkyoUpnpDiscoveryParticipant.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/discovery/OnkyoUpnpDiscoveryParticipant.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/discovery/OnkyoUpnpDiscoveryParticipant.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpCommand.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpCommand.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpCommand.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpCommand.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpException.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpException.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpException.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpException.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpMessage.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpMessage.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpMessage.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpMessage.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpProtocol.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpProtocol.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpProtocol.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/eiscp/EiscpProtocol.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/handler/OnkyoHandler.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/handler/OnkyoHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/handler/OnkyoHandler.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/handler/OnkyoHandler.java diff --git a/addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/handler/UpnpAudioSinkHandler.java b/bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/handler/UpnpAudioSinkHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/handler/UpnpAudioSinkHandler.java rename to bundles/org.openhab.binding.onkyo/src/main/java/org/openhab/binding/onkyo/internal/handler/UpnpAudioSinkHandler.java diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/config/config.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/config/config.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/config/config.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/config/config.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/channel-groups.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/channel-groups.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/channel-groups.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/channel-groups.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/channels.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/channels.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/channels.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/ht-rc560.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/ht-rc560.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/ht-rc560.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/ht-rc560.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/onkyoAVR.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/onkyoAVR.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/onkyoAVR.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/onkyoAVR.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/onkyoUnsupported.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/onkyoUnsupported.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/onkyoUnsupported.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/onkyoUnsupported.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr414.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr414.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr414.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr414.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr474.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr474.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr474.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr474.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr509.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr509.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr509.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr509.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr515.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr515.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr515.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr515.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr525.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr525.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr525.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr525.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr535.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr535.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr535.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr535.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr545.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr545.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr545.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr545.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr555.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr555.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr555.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr555.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr575.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr575.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr575.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr575.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr575e.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr575e.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr575e.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr575e.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr616.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr616.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr616.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr616.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr626.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr626.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr626.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr626.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr636.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr636.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr636.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr636.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr646.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr646.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr646.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr646.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr656.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr656.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr656.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr656.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr676.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr676.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr676.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr676.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr686.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr686.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr686.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr686.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr708.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr708.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr708.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr708.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr717.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr717.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr717.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr717.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr727.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr727.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr727.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr727.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr737.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr737.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr737.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr737.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr747.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr747.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr747.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr747.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr818.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr818.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr818.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr818.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr828.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr828.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr828.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr828.xml diff --git a/addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr838.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr838.xml similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/ESH-INF/thing/tx-nr838.xml rename to bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr838.xml diff --git a/bundles/org.openhab.binding.openuv/README.md b/bundles/org.openhab.binding.openuv/README.md index a278c4eaf60df..8326aa84a374c 100644 --- a/bundles/org.openhab.binding.openuv/README.md +++ b/bundles/org.openhab.binding.openuv/README.md @@ -26,6 +26,8 @@ The bridge has only one configuration parameter : |-----------|--------------------------------------------------------------| | apikey | Data-platform token to access the OpenUV service. Mandatory. | +Will accept a Refresh command in order to reinitiate connexion (eg in case of Quota exceeded). + ## Thing Configuration The thing has a few configuration parameters : @@ -64,7 +66,15 @@ demo.things: ```xtend Bridge openuv:openuvapi:local "OpenUV Api" [ apikey="xxxxYYYxxxx" ] { - Thing uvreport city1 "UV In My City" [ location="52.5200066,13.4049540", refresh=10 ] + Thing uvreport city1 "UV In My City" [ location="52.5200066,13.4049540", refresh=10 ]{ + Channels: + Type SafeExposure : Parents [ + index=3 + ] + Type SafeExposure : Childs [ + index=2 + ] + } } ``` diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVBindingConstants.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVBindingConstants.java index 19af868907ddf..a4940925b394f 100644 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVBindingConstants.java +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVBindingConstants.java @@ -52,8 +52,6 @@ public class OpenUVBindingConstants { public static final String UVTIME = "UVTime"; public static final String SAFEEXPOSURE = "SafeExposure"; - public static final String PROPERTY_INDEX = "Index"; - public static final Set BRIDGE_THING_TYPES_UIDS = Collections.singleton(APIBRIDGE_THING_TYPE); public static final Set SUPPORTED_THING_TYPES_UIDS = new HashSet<>( Arrays.asList(LOCATION_REPORT_THING_TYPE)); diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVConfiguration.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVConfiguration.java deleted file mode 100644 index e533420a6111a..0000000000000 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/OpenUVConfiguration.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.openuv.internal; - -/** - * The {@link OpenUVConfiguration} is the class used to match the - * thing configuration. - * - * @author Gaël L"hopital - Initial contribution - */ -public class OpenUVConfiguration { - String[] elements = null; - - public String apikey; - private String location; - public Integer refresh; - - public String getLatitude() { - return getElement(0); - } - - public String getLongitude() { - return getElement(1); - } - - public String getAltitude() { - return getElement(2); - } - - private String getElement(int index) { - if (elements == null) { - elements = location.split(","); - } - if (index < elements.length) { - return elements[index].trim(); - } else { - return null; - } - } -} diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/ReportConfiguration.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/ReportConfiguration.java new file mode 100644 index 0000000000000..ac7c67a72a630 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/ReportConfiguration.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.openuv.internal; + +/** + * The {@link ReportConfiguration} is the class used to match the + * thing configuration. + * + * @author Gaël L"hopital - Initial contribution + */ +public class ReportConfiguration { + String[] elements = null; + + private String location; + public Integer refresh; + + public String getLatitude() { + return getElement(0); + } + + public String getLongitude() { + return getElement(1); + } + + public String getAltitude() { + return getElement(2); + } + + private String getElement(int index) { + if (elements == null) { + elements = location.split(","); + } + if (index < elements.length) { + return elements[index].trim(); + } else { + return null; + } + } +} diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/SafeExposureConfiguration.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/SafeExposureConfiguration.java new file mode 100644 index 0000000000000..d7ae6812b099b --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/SafeExposureConfiguration.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.openuv.internal; + +/** + * The {@link SafeExposureConfiguration} is the class used to match the + * SafeExposure channel configuration. + * + * @author Gaël L"hopital - Initial contribution + */ +public class SafeExposureConfiguration { + public Integer index; +} diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/discovery/OpenUVDiscoveryService.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/discovery/OpenUVDiscoveryService.java index bcbdf07c89eb1..05d126b21d55b 100644 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/discovery/OpenUVDiscoveryService.java +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/discovery/OpenUVDiscoveryService.java @@ -106,6 +106,7 @@ public void createResults(PointType location) { .build()); } + @SuppressWarnings("null") @Override protected void stopBackgroundDiscovery() { logger.debug("Stopping OpenUV background discovery"); diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVBridgeHandler.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVBridgeHandler.java index 57879b7b188e9..dd76a8ba9ba97 100644 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVBridgeHandler.java +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVBridgeHandler.java @@ -15,6 +15,9 @@ import static org.openhab.binding.openuv.internal.OpenUVBindingConstants.BASE_URL; import java.io.IOException; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; @@ -33,9 +36,11 @@ import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; import org.eclipse.smarthome.io.net.http.HttpUtil; import org.openhab.binding.openuv.internal.OpenUVBindingConstants; -import org.openhab.binding.openuv.internal.json.OpenUVJsonResponse; +import org.openhab.binding.openuv.internal.json.OpenUVResponse; +import org.openhab.binding.openuv.internal.json.OpenUVResult; import org.osgi.framework.ServiceRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,6 +60,10 @@ @NonNullByDefault public class OpenUVBridgeHandler extends BaseBridgeHandler { private final Logger logger = LoggerFactory.getLogger(OpenUVBridgeHandler.class); + private static final String ERROR_QUOTA_EXCEEDED = "Daily API quota exceeded"; + private static final String ERROR_NO_API_KEY = "No API Key provided"; + private static final String ERROR_WRONG_KEY = "User with API Key not found"; + private static final int REQUEST_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(30); private final Gson gson = new GsonBuilder() @@ -75,44 +84,30 @@ public OpenUVBridgeHandler(Bridge bridge) { @Override public void initialize() { - String error = ""; - String errorDetail = null; - logger.debug("Initializing OpenUV API bridge handler."); Configuration config = getThing().getConfiguration(); - header.put("x-access-token", config.get(OpenUVBindingConstants.APIKEY)); - - // Check if an api key has been provided during the bridge creation - if (StringUtils.trimToNull((String) config.get(OpenUVBindingConstants.APIKEY)) == null) { - error += " Parameter 'apikey' must be configured."; + String apiKey = (String) config.get(OpenUVBindingConstants.APIKEY); + if (StringUtils.trimToNull(apiKey) == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Parameter 'apikey' must be configured."); } else { - // Check if the provided api key is valid for use with the OpenUV service - try { - // Run the HTTP request and get the JSON response - OpenUVJsonResponse response = getUVData("0", "0", null); - if ("Invalid API Key".equalsIgnoreCase(response.getError())) { - error = "API key has to be fixed"; - errorDetail = response.getError(); - } else { - updateStatus(ThingStatus.ONLINE); - } - } catch (IOException | IllegalArgumentException e) { - error = "Error running OpenUV API request"; - errorDetail = e.getMessage(); - } - } - - // Updates the thing status accordingly - if (!"".equals(error)) { - error = error.trim(); - logger.debug("Disabling thing '{}': Error '{}': {}", getThing().getUID(), error, errorDetail); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, error); + header.put("x-access-token", apiKey); + initiateConnexion(); } } @Override public void handleCommand(ChannelUID channelUID, Command command) { - // not needed + if (command instanceof RefreshType) { + initiateConnexion(); + } else { + logger.debug("The OpenUV bridge only handles Refresh command and not '{}'", command); + } + } + + private void initiateConnexion() { + // Check if the provided api key is valid for use with the OpenUV service + getUVData("0", "0", null); } public Map> getDiscoveryServiceRegs() { @@ -126,25 +121,48 @@ public void setDiscoveryServiceRegs(Map dis = getDiscoveryServiceRegs().get(this.getThing().getUID()); + ServiceRegistration dis = getDiscoveryServiceRegs().get(getThing().getUID()); if (dis != null) { dis.unregister(); } super.handleRemoval(); } - public OpenUVJsonResponse getUVData(String latitude, String longitude, @Nullable String altitude) - throws IOException { + public @Nullable OpenUVResult getUVData(String latitude, String longitude, @Nullable String altitude) { StringBuilder urlBuilder = new StringBuilder(BASE_URL).append("?lat=").append(latitude).append("&lng=") .append(longitude); if (altitude != null) { urlBuilder.append("&alt=").append(altitude); } - String jsonData = HttpUtil.executeUrl("GET", urlBuilder.toString(), header, null, null, REQUEST_TIMEOUT); - logger.debug("URL = {}", jsonData); + String errorMessage = null; + try { + String jsonData = HttpUtil.executeUrl("GET", urlBuilder.toString(), header, null, null, REQUEST_TIMEOUT); + OpenUVResponse uvResponse = gson.fromJson(jsonData, OpenUVResponse.class); + if (uvResponse.getError() == null) { + updateStatus(ThingStatus.ONLINE); + return uvResponse.getResult(); + } else { + errorMessage = uvResponse.getError(); + } + } catch (IOException e) { + errorMessage = e.getMessage(); + } - return gson.fromJson(jsonData, OpenUVJsonResponse.class); - } + if (errorMessage.startsWith(ERROR_QUOTA_EXCEEDED)) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorMessage); + LocalDate today = LocalDate.now(); + LocalDate tomorrow = today.plusDays(1); + LocalDateTime tomorrowMidnight = tomorrow.atStartOfDay().plusMinutes(2); + + logger.warn("Quota Exceeded, going OFFLINE for today, will retry at : {} ", tomorrowMidnight); + scheduler.schedule(this::initiateConnexion, + Duration.between(LocalDateTime.now(), tomorrowMidnight).toMinutes(), TimeUnit.MINUTES); + } else if (errorMessage.startsWith(ERROR_WRONG_KEY)) { + logger.error("Error occured during API query : {}", errorMessage); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMessage); + } + return null; + } } diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVReportHandler.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVReportHandler.java index ee05bcaa02b09..6ac59f9d0a8a4 100644 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVReportHandler.java +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/handler/OpenUVReportHandler.java @@ -14,7 +14,6 @@ import static org.openhab.binding.openuv.internal.OpenUVBindingConstants.*; -import java.io.IOException; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; @@ -22,7 +21,7 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.library.types.DateTimeType; import org.eclipse.smarthome.core.library.types.HSBType; import org.eclipse.smarthome.core.library.types.QuantityType; import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; @@ -32,12 +31,15 @@ import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.ThingStatusInfo; import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.openuv.internal.OpenUVConfiguration; -import org.openhab.binding.openuv.internal.json.OpenUVJsonResult; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.openuv.internal.ReportConfiguration; +import org.openhab.binding.openuv.internal.SafeExposureConfiguration; +import org.openhab.binding.openuv.internal.json.OpenUVResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,15 +52,12 @@ @NonNullByDefault public class OpenUVReportHandler extends BaseThingHandler { private static final int DEFAULT_REFRESH_PERIOD = 30; - private final Logger logger = LoggerFactory.getLogger(OpenUVReportHandler.class); - @NonNullByDefault({}) - private OpenUVBridgeHandler bridgeHandler; + private final Logger logger = LoggerFactory.getLogger(OpenUVReportHandler.class); - @NonNullByDefault({}) - private ScheduledFuture refreshJob; - @NonNullByDefault({}) - private ScheduledFuture uvMaxJob; + private @NonNullByDefault({}) OpenUVBridgeHandler bridgeHandler; + private @NonNullByDefault({}) ScheduledFuture refreshJob; + private @NonNullByDefault({}) ScheduledFuture uvMaxJob; public OpenUVReportHandler(Thing thing) { super(thing); @@ -68,26 +67,20 @@ public OpenUVReportHandler(Thing thing) { public void initialize() { logger.debug("Initializing OpenUV handler."); - OpenUVConfiguration config = getConfigAs(OpenUVConfiguration.class); - - String errorMsg = null; + ReportConfiguration config = getConfigAs(ReportConfiguration.class); if (config.refresh != null && config.refresh < 3) { - errorMsg = "Parameter 'refresh' must be higher than 3 minutes to stay in free API plan"; - } - - Bridge bridge = getBridge(); - if (bridge != null) { - bridgeHandler = (OpenUVBridgeHandler) bridge.getHandler(); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "Parameter 'refresh' must be higher than 3 minutes to stay in free API plan"); } else { - errorMsg = "Invalid bridge"; - } - - if (errorMsg == null) { - updateStatus(ThingStatus.UNKNOWN); - startAutomaticRefresh(); - } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, errorMsg); + Bridge bridge = getBridge(); + if (bridge == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid bridge"); + } else { + bridgeHandler = (OpenUVBridgeHandler) bridge.getHandler(); + updateStatus(ThingStatus.UNKNOWN); + startAutomaticRefresh(); + } } } @@ -96,17 +89,19 @@ public void initialize() { * * @param openUVData */ - private void scheduleUVMaxEvent(OpenUVJsonResult openUVData) { + private void scheduleUVMaxEvent(OpenUVResult openUVData) { if ((uvMaxJob == null || uvMaxJob.isCancelled())) { - long timeDiff = ChronoUnit.MINUTES.between(ZonedDateTime.now(ZoneId.systemDefault()), - openUVData.getUVMaxTimeAsZDT()); - - if (timeDiff > 0) { - logger.debug("Scheduling {} in {} minutes", UVMAXEVENT, timeDiff); - uvMaxJob = scheduler.schedule(() -> { - triggerChannel(UVMAXEVENT); - uvMaxJob = null; - }, timeDiff, TimeUnit.MINUTES); + State uvMaxTime = openUVData.getUVMaxTime(); + if (uvMaxTime != UnDefType.NULL) { + ZonedDateTime uvMaxZdt = ((DateTimeType) uvMaxTime).getZonedDateTime(); + long timeDiff = ChronoUnit.MINUTES.between(ZonedDateTime.now(ZoneId.systemDefault()), uvMaxZdt); + if (timeDiff > 0) { + logger.debug("Scheduling {} in {} minutes", UVMAXEVENT, timeDiff); + uvMaxJob = scheduler.schedule(() -> { + triggerChannel(UVMAXEVENT); + uvMaxJob = null; + }, timeDiff, TimeUnit.MINUTES); + } } } } @@ -116,25 +111,29 @@ private void scheduleUVMaxEvent(OpenUVJsonResult openUVData) { */ private void startAutomaticRefresh() { if (refreshJob == null || refreshJob.isCancelled()) { - OpenUVConfiguration config = getConfigAs(OpenUVConfiguration.class); + ReportConfiguration config = getConfigAs(ReportConfiguration.class); int delay = (config.refresh != null) ? config.refresh.intValue() : DEFAULT_REFRESH_PERIOD; refreshJob = scheduler.scheduleWithFixedDelay(() -> { - updateChannels(); + updateChannels(config); }, 0, delay, TimeUnit.MINUTES); } } - private void updateChannels() { - try { - OpenUVJsonResult openUVData = getOpenUVData(); - scheduleUVMaxEvent(openUVData); - for (Channel channel : getThing().getChannels()) { - updateChannel(channel.getUID(), openUVData); + private void updateChannels(ReportConfiguration config) { + ThingStatusInfo bridgeStatusInfo = bridgeHandler.getThing().getStatusInfo(); + if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) { + OpenUVResult openUVData = bridgeHandler.getUVData(config.getLatitude(), config.getLongitude(), + config.getAltitude()); + if (openUVData != null) { + scheduleUVMaxEvent(openUVData); + getThing().getChannels().forEach(channel -> { + updateChannel(channel.getUID(), openUVData); + }); + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE, bridgeStatusInfo.getStatusDetail(), + bridgeStatusInfo.getDescription()); } - updateStatus(ThingStatus.ONLINE); - } catch (IOException e) { - logger.error("Exception occurred during execution: {}", e.getMessage(), e); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); } } @@ -151,17 +150,17 @@ public void dispose() { uvMaxJob.cancel(true); uvMaxJob = null; } - } @Override public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof RefreshType) { scheduler.execute(() -> { - updateChannels(); + ReportConfiguration config = getConfigAs(ReportConfiguration.class); + updateChannels(config); }); } else { - logger.debug("The OpenUV binding is read-only and can not handle command {}", command); + logger.debug("The OpenUV Report Thing only handles Refresh command and not '{}'", command); } } @@ -172,13 +171,14 @@ public void handleCommand(ChannelUID channelUID, Command command) { * @param openUVData * */ - private void updateChannel(ChannelUID channelUID, OpenUVJsonResult openUVData) { - switch (channelUID.getId()) { + private void updateChannel(ChannelUID channelUID, OpenUVResult openUVData) { + Channel channel = getThing().getChannel(channelUID.getId()); + switch (channel.getChannelTypeUID().getId()) { case UVINDEX: updateState(channelUID, openUVData.getUv()); break; case UVCOLOR: - updateState(channelUID, getAsHSB(openUVData.getUv().floatValue())); + updateState(channelUID, getAsHSB(openUVData.getUv().intValue())); break; case UVMAX: updateState(channelUID, openUVData.getUvMax()); @@ -196,24 +196,16 @@ private void updateChannel(ChannelUID channelUID, OpenUVJsonResult openUVData) { updateState(channelUID, openUVData.getUVTime()); break; case SAFEEXPOSURE: - Channel channel = getThing().getChannel(channelUID.getId()); - if (channel != null) { - Configuration configuration = channel.getConfiguration(); - int index = 1; - Object skinIndex = configuration.get(PROPERTY_INDEX); - if (skinIndex != null) { - try { - index = Integer.parseInt(skinIndex.toString()); - } finally { - updateState(channelUID, openUVData.getSafeExposureTime().getSafeExposure(index)); - } - } + SafeExposureConfiguration configuration = channel.getConfiguration() + .as(SafeExposureConfiguration.class); + if (configuration.index != null) { + updateState(channelUID, openUVData.getSafeExposureTime().getSafeExposure(configuration.index)); } break; } } - private State getAsHSB(float uv) { + private State getAsHSB(int uv) { if (uv >= 11) { return HSBType.fromRGB(106, 27, 154); } else if (uv >= 8) { @@ -226,15 +218,4 @@ private State getAsHSB(float uv) { return HSBType.fromRGB(85, 139, 47); } } - - /** - * Request new UV Index data to the service - * - * @throws IOException - */ - private OpenUVJsonResult getOpenUVData() throws IOException { - OpenUVConfiguration config = getConfigAs(OpenUVConfiguration.class); - return bridgeHandler.getUVData(config.getLatitude(), config.getLongitude(), config.getAltitude()).getResult(); - } - } diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVJsonResponse.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVJsonResponse.java deleted file mode 100644 index 6ac9eaf7cbc89..0000000000000 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVJsonResponse.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.openuv.internal.json; - -/** - * The {@link OpenUVJsonResponse} is the Java class used to map the JSON - * response to the OpenUV request. - * - * @author Gaël L'hopital - Initial contribution - */ -public class OpenUVJsonResponse { - - private OpenUVJsonResult result; - private String error; - - public OpenUVJsonResponse() { - } - - public OpenUVJsonResult getResult() { - return result; - } - - public String getError() { - return error; - } - -} diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVJsonResult.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVJsonResult.java deleted file mode 100644 index e395274642dad..0000000000000 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVJsonResult.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.openuv.internal.json; - -import java.time.ZoneId; -import java.time.ZonedDateTime; - -import org.eclipse.smarthome.core.library.types.DateTimeType; -import org.eclipse.smarthome.core.library.types.DecimalType; - -/** - * The {@link OpenUVJsonResult} is responsible for storing - * the "result" node from the OpenUV JSON response - * - * @author Gaël L'hopital - Initial contribution - */ -public class OpenUVJsonResult { - - private DecimalType uv; - private ZonedDateTime uvTime; - private DecimalType uvMax; - private ZonedDateTime uvMaxTime; - private DecimalType ozone; - private ZonedDateTime ozoneTime; - private OpenUVSafeExposureTime safeExposureTime; - - public OpenUVJsonResult() { - } - - public DecimalType getUv() { - return uv; - } - - public DecimalType getUvMax() { - return uvMax; - } - - public DecimalType getOzone() { - return ozone; - } - - public DateTimeType getUVTime() { - return new DateTimeType(uvTime.withZoneSameInstant(ZoneId.systemDefault())); - } - - public DateTimeType getUVMaxTime() { - return new DateTimeType(getUVMaxTimeAsZDT()); - } - - public ZonedDateTime getUVMaxTimeAsZDT() { - return uvMaxTime.withZoneSameInstant(ZoneId.systemDefault()); - } - - public DateTimeType getOzoneTime() { - return new DateTimeType(ozoneTime.withZoneSameInstant(ZoneId.systemDefault())); - } - - public OpenUVSafeExposureTime getSafeExposureTime() { - return safeExposureTime; - } - -} diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVResponse.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVResponse.java new file mode 100644 index 0000000000000..650de56cbf690 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVResponse.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.openuv.internal.json; + +/** + * The {@link OpenUVResponse} is the Java class used to map the JSON + * response to the OpenUV request. + * + * @author Gaël L'hopital - Initial contribution + */ +public class OpenUVResponse { + private String error; + private OpenUVResult result; + + public OpenUVResult getResult() { + return result; + } + + public String getError() { + return error; + } +} diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVResult.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVResult.java new file mode 100644 index 0000000000000..a5b18603b67ba --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVResult.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.openuv.internal.json; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.DateTimeType; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; + +/** + * The {@link OpenUVResult} is responsible for storing + * the "result" node from the OpenUV JSON response + * + * @author Gaël L'hopital - Initial contribution + */ +@NonNullByDefault +public class OpenUVResult { + private final ZonedDateTime DEFAULT_ZDT = ZonedDateTime.of(LocalDateTime.MIN, ZoneId.systemDefault()); + private DecimalType uv = new DecimalType(0); + private ZonedDateTime uvTime = DEFAULT_ZDT; + private DecimalType uvMax = new DecimalType(0); + private ZonedDateTime uvMaxTime = DEFAULT_ZDT; + private DecimalType ozone = new DecimalType(0); + private ZonedDateTime ozoneTime = DEFAULT_ZDT; + private SafeExposureTime safeExposureTime = new SafeExposureTime(); + + public DecimalType getUv() { + return uv; + } + + public DecimalType getUvMax() { + return uvMax; + } + + public DecimalType getOzone() { + return ozone; + } + + public State getUVTime() { + return uvTime != DEFAULT_ZDT ? new DateTimeType(uvTime.withZoneSameInstant(ZoneId.systemDefault())) + : UnDefType.NULL; + } + + public State getUVMaxTime() { + return uvMaxTime != DEFAULT_ZDT ? new DateTimeType(uvMaxTime.withZoneSameInstant(ZoneId.systemDefault())) + : UnDefType.NULL; + } + + public State getOzoneTime() { + return ozoneTime != DEFAULT_ZDT ? new DateTimeType(ozoneTime.withZoneSameInstant(ZoneId.systemDefault())) + : UnDefType.NULL; + } + + public SafeExposureTime getSafeExposureTime() { + return safeExposureTime; + } + +} diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVSafeExposureTime.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVSafeExposureTime.java deleted file mode 100644 index 3df378d017395..0000000000000 --- a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/OpenUVSafeExposureTime.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) 2010-2019 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.openuv.internal.json; - -import org.eclipse.smarthome.core.library.types.QuantityType; -import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; - -/** - * Wrapper type around values reported by OpenUV safe exposure time. - * - * @author Gaël L'hopital - Initial contribution - */ -public class OpenUVSafeExposureTime { - - private int st1; - private int st2; - private int st3; - private int st4; - private int st5; - private int st6; - - public QuantityType getSafeExposure(int index) { - int result; - switch (index) { - case 1: - result = st1; - case 2: - result = st2; - case 3: - result = st3; - case 4: - result = st4; - case 5: - result = st5; - default: - result = st6; - } - return new QuantityType<>(result, SmartHomeUnits.MINUTE); - } -} diff --git a/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/SafeExposureTime.java b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/SafeExposureTime.java new file mode 100644 index 0000000000000..075cfc3352e92 --- /dev/null +++ b/bundles/org.openhab.binding.openuv/src/main/java/org/openhab/binding/openuv/internal/json/SafeExposureTime.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.openuv.internal.json; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; + +/** + * Wrapper type around values reported by OpenUV safe exposure time. + * + * @author Gaël L'hopital - Initial contribution + */ +@NonNullByDefault +public class SafeExposureTime { + private BigInteger st1 = BigInteger.ZERO; + private BigInteger st2 = BigInteger.ZERO; + private BigInteger st3 = BigInteger.ZERO; + private BigInteger st4 = BigInteger.ZERO; + private BigInteger st5 = BigInteger.ZERO; + private BigInteger st6 = BigInteger.ZERO; + + public State getSafeExposure(int index) { + BigInteger result; + switch (index) { + case 1: + result = st1; + break; + case 2: + result = st2; + break; + case 3: + result = st3; + break; + case 4: + result = st4; + break; + case 5: + result = st5; + break; + default: + result = st6; + } + return result != BigInteger.ZERO ? new QuantityType<>(result, SmartHomeUnits.MINUTE) : UnDefType.NULL; + } +} diff --git a/bundles/org.openhab.binding.openuv/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.openuv/src/main/resources/ESH-INF/thing/thing-types.xml index 7cb03d24e66e3..963826fed6a87 100644 --- a/bundles/org.openhab.binding.openuv/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.openuv/src/main/resources/ESH-INF/thing/thing-types.xml @@ -114,7 +114,7 @@ Safe exposure time for Fitzpatrick Skin Types - + Fitzpatrick Skin Type. @@ -125,7 +125,7 @@ - 1 + 2 diff --git a/bundles/org.openhab.binding.orvibo/.classpath b/bundles/org.openhab.binding.orvibo/.classpath new file mode 100644 index 0000000000000..971c77fbc235d --- /dev/null +++ b/bundles/org.openhab.binding.orvibo/.classpath @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.orvibo/.project b/bundles/org.openhab.binding.orvibo/.project new file mode 100644 index 0000000000000..0a9ed4f1ec785 --- /dev/null +++ b/bundles/org.openhab.binding.orvibo/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.orvibo + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.orvibo/NOTICE b/bundles/org.openhab.binding.orvibo/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.orvibo/NOTICE rename to bundles/org.openhab.binding.orvibo/NOTICE diff --git a/addons/binding/org.openhab.binding.orvibo/README.md b/bundles/org.openhab.binding.orvibo/README.md similarity index 100% rename from addons/binding/org.openhab.binding.orvibo/README.md rename to bundles/org.openhab.binding.orvibo/README.md diff --git a/addons/binding/org.openhab.binding.orvibo/lib/s20-sdk-0.0.6-SNAPSHOT.jar b/bundles/org.openhab.binding.orvibo/lib/s20-sdk-0.0.6-SNAPSHOT.jar similarity index 100% rename from addons/binding/org.openhab.binding.orvibo/lib/s20-sdk-0.0.6-SNAPSHOT.jar rename to bundles/org.openhab.binding.orvibo/lib/s20-sdk-0.0.6-SNAPSHOT.jar diff --git a/bundles/org.openhab.binding.orvibo/pom.xml b/bundles/org.openhab.binding.orvibo/pom.xml new file mode 100644 index 0000000000000..878d1a0bb5eb1 --- /dev/null +++ b/bundles/org.openhab.binding.orvibo/pom.xml @@ -0,0 +1,26 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.orvibo + + openHAB Add-ons :: Bundles :: Orvibo Binding + + + + org.slf4j + slf4j-simple + ${slf4j.version} + provided + + + + diff --git a/addons/binding/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/OrviboActivator.java b/bundles/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/OrviboActivator.java similarity index 100% rename from addons/binding/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/OrviboActivator.java rename to bundles/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/OrviboActivator.java diff --git a/addons/binding/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/OrviboBindingConstants.java b/bundles/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/OrviboBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/OrviboBindingConstants.java rename to bundles/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/OrviboBindingConstants.java diff --git a/addons/binding/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/OrviboHandlerFactory.java b/bundles/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/OrviboHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/OrviboHandlerFactory.java rename to bundles/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/OrviboHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/discovery/SocketDiscoveryService.java b/bundles/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/discovery/SocketDiscoveryService.java similarity index 100% rename from addons/binding/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/discovery/SocketDiscoveryService.java rename to bundles/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/discovery/SocketDiscoveryService.java diff --git a/addons/binding/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/handler/S20Handler.java b/bundles/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/handler/S20Handler.java similarity index 100% rename from addons/binding/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/handler/S20Handler.java rename to bundles/org.openhab.binding.orvibo/src/main/java/org/openhab/binding/orvibo/internal/handler/S20Handler.java diff --git a/addons/binding/org.openhab.binding.orvibo/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.orvibo/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.orvibo/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.orvibo/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.orvibo/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.orvibo/src/main/resources/ESH-INF/thing/thing-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.orvibo/ESH-INF/thing/thing-types.xml rename to bundles/org.openhab.binding.orvibo/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/bundles/org.openhab.binding.pioneeravr/README.md b/bundles/org.openhab.binding.pioneeravr/README.md index a3a2acf1c19f7..6b02cfca8d172 100644 --- a/bundles/org.openhab.binding.pioneeravr/README.md +++ b/bundles/org.openhab.binding.pioneeravr/README.md @@ -86,6 +86,170 @@ Here after are the ID values of the input sources (depending on you AVR input so * 57: Spotify * 31: HDMI (cyclic) +## Listening Modes + +The *Listening Mode* is set by user to instruct the AVR how to treat the audio signal and do upscaling, downscaling and amplification. This settings corresponds to the settings made with the remote control or front panel. What the AVR actually does with each setting/input-signal-combination can be read out using the *Playing Listening Mode* channel. + +* 0001: STEREO (cyclic) +* 0010: STANDARD (cyclic) +* 0009: STEREO (direct set) +* 0011: (2ch source) +* 0013: PRO LOGIC2 MOVIE +* 0018: PRO LOGIC2x MOVIE +* 0014: PRO LOGIC2 MUSIC +* 0019: PRO LOGIC2x MUSIC +* 0015: PRO LOGIC2 GAME +* 0020: PRO LOGIC2x GAME +* 0031: PRO LOGIC2z HEIGHT +* 0032: WIDE SURROUND MOVIE +* 0033: WIDE SURROUND MUSIC +* 0012: PRO LOGIC +* 0016: Neo:6 CINEMA +* 0017: Neo:6 MUSIC +* 0037: Neo:X CINEMA +* 0038: Neo:X MUSIC +* 0039: Neo:X GAME +* 0040: Dolby Surround +* 0041: EXTENDED STEREO +* 0021: (Multi ch source) Channel base straight decode +* 0022: (Multi ch source)+DOLBY EX +* 0023: (Multi ch source)+PRO LOGIC2x MOVIE +* 0024: (Multi ch source)+PRO LOGIC2x MUSIC +* 0034: (Multi-ch Source)+PRO LOGIC2z HEIGHT +* 0035: (Multi-ch Source)+WIDE SURROUND MOVIE +* 0036: (Multi-ch Source)+WIDE SURROUND MUSIC +* 0025: (Multi ch source)DTS-ES Neo:6 +* 0026: (Multi ch source)DTS-ES matrix +* 0027: (Multi ch source)DTS-ES discrete +* 0030: (Multi ch source)DTS-ES 8ch discrete +* 0043: (Multi ch source)+Neo:X CINEMA +* 0044: (Multi ch source)+Neo:X MUSIC +* 0045: (Multi ch source)+Neo:X GAME +* 0050: (Multi ch source)+Dolby Surround +* 0100: ADVANCED SURROUND (cyclic) +* 0101: ACTION +* 0103: DRAMA +* 0118: ADVANCED GAME +* 0117: SPORTS +* 0107: CLASSICAL +* 0110: ROCK/POP +* 0003: Front Stage Surround Advance +* 0200: ECO MODE (cyclic) +* 0212: ECO MODE 1 +* 0213: ECO MODE 2 +* 0153: RETRIEVER AIR +* 0113: PHONES SURROUND +* 0005: AUTO SURR/STREAM DIRECT (cyclic) +* 0006: AUTO SURROUND +* 0151: Auto Level Control (A.L.C.) +* 0007: DIRECT +* 0008: PURE DIRECT +* 0152: OPTIMUM SURROUND + +## Playing Listening Modes + +The *Playing Listening Mode* is the Listening Mode that is actually playing as opposed to the *Listening Mode* set by the user. The *Playing Listening Mode* is what the display on the device shows. + +* 0101: [)(]PLIIx MOVIE +* 0102: [)(]PLII MOVIE +* 0103: [)(]PLIIx MUSIC +* 0104: [)(]PLII MUSIC +* 0105: [)(]PLIIx GAME +* 0106: [)(]PLII GAME +* 0107: [)(]PROLOGIC +* 0108: Neo:6 CINEMA +* 0109: Neo:6 MUSIC +* 010c: 2ch Straight Decode +* 010d: [)(]PLIIz HEIGHT +* 010e: WIDE SURR MOVIE +* 010f: WIDE SURR MUSIC +* 0110: STEREO +* 0111: Neo:X CINEMA +* 0112: Neo:X MUSIC +* 0113: Neo:X GAME +* 0117: Dolby Surround +* 0118: EXTENDED STEREO +* 1101: [)(]PLIIx MOVIE +* 1102: [)(]PLIIx MUSIC +* 1103: [)(]DIGITAL EX +* 1104: DTS Neo:6 +* 1105: ES MATRIX +* 1106: ES DISCRETE +* 1107: DTS-ES 8ch +* 1108: multi ch Channel base Straight Decode +* 1109: [)(]PLIIz HEIGHT +* 110a: WIDE SURR MOVIE +* 110b: WIDE SURR MUSIC +* 110c: Neo:X CINEMA +* 110d: Neo:X MUSIC +* 110e: Neo:X GAME +* 110f: Dolby Surround +* 0201: ACTION +* 0202: DRAMA +* 0208: ADVANCEDGAME +* 0209: SPORTS +* 020a: CLASSICAL +* 020b: ROCK/POP +* 020e: PHONES SURR. +* 020f: FRONT STAGE SURROUND ADVANCE +* 0211: SOUND RETRIEVER AIR +* 0212: ECO MODE 1 +* 0213: ECO MODE 2 +* 0401: STEREO +* 0402: [)(]PLII MOVIE +* 0403: [)(]PLIIx MOVIE +* 0405: AUTO SURROUND Straight Decode +* 0406: [)(]DIGITAL EX +* 0407: [)(]PLIIx MOVIE +* 0408: DTS +Neo:6 +* 0409: ES MATRIX +* 040a: ES DISCRETE +* 040b: DTS-ES 8ch +* 040e: RETRIEVER AIR +* 040f: Neo:X CINEMA +* 0411: Dolby Surround +* 0501: STEREO +* 0502: [)(]PLII MOVIE +* 0503: [)(]PLIIx MOVIE +* 0504: DTS/DTS-HD +* 0505: ALC Straight Decode +* 0506: [)(]DIGITAL EX +* 0507: [)(]PLIIx MOVIE +* 0508: DTS +Neo:6 +* 0509: ES MATRIX +* 050a: ES DISCRETE +* 050b: DTS-ES 8ch +* 050e: RETRIEVER AIR +* 050f: Neo:X CINEMA +* 0601: STEREO +* 0602: [)(]PLII MOVIE +* 0603: [)(]PLIIx MOVIE +* 0604: Neo:6 CINEMA +* 0605: STREAM DIRECT NORMAL Straight Decode +* 0606: [)(]DIGITAL EX +* 0607: [)(]PLIIx MOVIE +* 0609: ES MATRIX +* 060a: ES DISCRETE +* 060b: DTS-ES 8ch +* 060c: Neo:X CINEMA +* 060e: NORMAL DIRECT Dolby Surround +* 0701: STREAM DIRECT PURE 2ch +* 0702: [)(]PLII MOVIE +* 0703: [)(]PLIIx MOVIE +* 0704: Neo:6 CINEMA +* 0705: STREAM DIRECT PURE Straight Decode +* 0706: [)(]DIGITAL EX +* 0707: [)(]PLIIx MOVIE +* 0708: (nothing) +* 0709: ES MATRIX +* 070a: ES DISCRETE +* 070b: DTS-ES 8ch +* 070c: Neo:X CINEMA +* 070e: PURE DIRECT Dolby Surround +* 0881: OPTIMUM +* 0e01: HDMI THROUGH +* 0f01: MULTI CH IN + ## Example *demo.Things: @@ -104,6 +268,8 @@ Dimmer vsx921VolumeDimmer "Volume [%.1f] %" (All) Number vsx921VolumeNumber "Volume [%.1f] dB" (All) { channel="pioneeravr:ipAvr:vsx921:volumeDb" } String vsx921InputSourceSet "Input" (All) { channel="pioneeravr:ipAvr:vsx921:setInputSource" } String vsx921InformationDisplay "Information [%s]" (All) { channel="pioneeravr:ipAvr:vsx921:displayInformation" } +String vsx921ListeningMode "Listening Mode [%s]" (All) { channel="pioneeravr:ipAvr:vsx921:listeningMode" } +String vsx921PlayingListeningMode "Playing Listening Mode [%s]" (All) { channel="pioneeravr:ipAvr:vsx921:playingListeningMode" } ``` *demo.sitemap: @@ -118,6 +284,8 @@ sitemap demo label="Main Menu" Setpoint item=vsx921VolumeNumber minValue="-80" maxValue="12" step="0.5" Switch item=vsx921InputSourceSet mappings=[04="DVD", 15="DVR/BDR", 25="BD"] Text item=vsx921InformationDisplay + Switch item=vsx921ListeningMode mappings=["0009"="Stereo", "0040"="Dolby Surround", "0010"="next"] + Text item=vsx921PlayingListeningMode } } ``` diff --git a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/PioneerAvrBindingConstants.java b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/PioneerAvrBindingConstants.java index 01c5a51bc5792..2c0c9ea56e490 100644 --- a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/PioneerAvrBindingConstants.java +++ b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/PioneerAvrBindingConstants.java @@ -25,6 +25,7 @@ * The {@link PioneerAvrBinding} class defines common constants, which are used across the whole binding. * * @author Antoine Besnard - Initial contribution + * @author Leroy Foerster - Listening Mode, Playing Listening Mode */ @NonNullByDefault public class PioneerAvrBindingConstants { @@ -45,7 +46,7 @@ public class PioneerAvrBindingConstants { .collect(Collectors.toSet())); public static final Set SUPPORTED_DEVICE_MODELS2016 = Collections.unmodifiableSet( - Stream.of("SC-99", "SC-LX89", "SC-97", "SC-LX79", "SC-95", "SC-LX59", "SC-92", "SC-91", "VSX-90", "VSX-45") + Stream.of("SC-99", "SC-LX89", "SC-97", "SC-LX79", "SC-95", "SC-LX59", "SC-92", "SC-91", "VSX-90", "VSX-45", "VSX-830") .collect(Collectors.toSet())); // List of all Thing Type UIDs @@ -71,6 +72,8 @@ public class PioneerAvrBindingConstants { public static final String VOLUME_DB_CHANNEL = "volumeDb"; public static final String MUTE_CHANNEL = "mute"; public static final String SET_INPUT_SOURCE_CHANNEL = "setInputSource"; + public static final String LISTENING_MODE_CHANNEL = "listeningMode"; + public static final String PLAYING_LISTENING_MODE_CHANNEL = "playingListeningMode"; public static final String DISPLAY_INFORMATION_CHANNEL = "displayInformation#displayInformation"; public static final String GROUP_CHANNEL_PATTERN = "zone%s#%s"; diff --git a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/handler/AbstractAvrHandler.java b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/handler/AbstractAvrHandler.java index 3809113d11e5e..82d91b144a997 100644 --- a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/handler/AbstractAvrHandler.java +++ b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/handler/AbstractAvrHandler.java @@ -48,6 +48,7 @@ * AVR connection. * * @author Antoine Besnard - Initial contribution + * @author Leroy Foerster - Listening Mode, Playing Listening Mode */ public abstract class AbstractAvrHandler extends BaseThingHandler implements AvrUpdateListener, AvrDisconnectionListener { @@ -120,6 +121,7 @@ public void onPowerOn(int zone) { connection.sendVolumeQuery(zone); connection.sendMuteQuery(zone); connection.sendSourceInputQuery(zone); + connection.sendListeningModeQuery(zone); } /** @@ -131,6 +133,8 @@ public void onPowerOff(int zone) { updateState(getChannelUID(PioneerAvrBindingConstants.VOLUME_DB_CHANNEL, zone), UnDefType.UNDEF); updateState(getChannelUID(PioneerAvrBindingConstants.VOLUME_DIMMER_CHANNEL, zone), UnDefType.UNDEF); updateState(getChannelUID(PioneerAvrBindingConstants.SET_INPUT_SOURCE_CHANNEL, zone), UnDefType.UNDEF); + updateState(getChannelUID(PioneerAvrBindingConstants.LISTENING_MODE_CHANNEL, zone), UnDefType.UNDEF); + updateState(getChannelUID(PioneerAvrBindingConstants.PLAYING_LISTENING_MODE_CHANNEL, zone), UnDefType.UNDEF); } /** @@ -168,6 +172,8 @@ public void handleCommand(ChannelUID channelUID, Command command) { commandSent = connection.sendVolumeCommand(command, getZoneFromChannelUID(channelUID.getId())); } else if (channelUID.getId().contains(PioneerAvrBindingConstants.SET_INPUT_SOURCE_CHANNEL)) { commandSent = connection.sendInputSourceCommand(command, getZoneFromChannelUID(channelUID.getId())); + } else if (channelUID.getId().contains(PioneerAvrBindingConstants.LISTENING_MODE_CHANNEL)) { + commandSent = connection.sendListeningModeCommand(command, getZoneFromChannelUID(channelUID.getId())); } else if (channelUID.getId().contains(PioneerAvrBindingConstants.MUTE_CHANNEL)) { commandSent = connection.sendMuteCommand(command, getZoneFromChannelUID(channelUID.getId())); } else { @@ -208,6 +214,14 @@ public void statusUpdateReceived(AvrStatusUpdateEvent event) { manageInputSourceChannelUpdate(response); break; + case LISTENING_MODE: + manageListeningModeUpdate(response); + break; + + case PLAYING_LISTENING_MODE: + managePlayingListeningModeUpdate(response); + break; + case DISPLAY_INFORMATION: manageDisplayedInformationUpdate(response); break; @@ -288,6 +302,26 @@ private void manageInputSourceChannelUpdate(AvrResponse response) { new StringType(response.getParameterValue())); } + /** + * Notify an AVR now-playing, in-effect listening mode (audio output format) update to openHAB + * + * @param response + */ + private void managePlayingListeningModeUpdate(AvrResponse response) { + updateState(getChannelUID(PioneerAvrBindingConstants.PLAYING_LISTENING_MODE_CHANNEL, response.getZone()), + new StringType(response.getParameterValue())); + } + + /** + * Notify an AVR set listening mode (user-selected audio mode) update to openHAB + * + * @param response + */ + private void manageListeningModeUpdate(AvrResponse response) { + updateState(getChannelUID(PioneerAvrBindingConstants.LISTENING_MODE_CHANNEL, response.getZone()), + new StringType(response.getParameterValue())); + } + /** * Notify an AVR displayed information update to openHAB * diff --git a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/ParameterizedCommand.java b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/ParameterizedCommand.java index d9c9f3a36eb27..71055c4a98d3a 100644 --- a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/ParameterizedCommand.java +++ b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/ParameterizedCommand.java @@ -20,6 +20,7 @@ * A command which accept a parameter. * * @author Antoine Besnard - Initial contribution + * @author Leroy Foerster - Listening Mode, Playing Listening Mode */ public class ParameterizedCommand extends SimpleCommand { @@ -29,7 +30,8 @@ public class ParameterizedCommand extends SimpleCommand { public enum ParameterizedCommandType implements AvrCommand.CommandType { VOLUME_SET("[0-9]{2,3}", "VL", "ZV", "YV", "HZV"), - INPUT_CHANNEL_SET("[0-9]{2}", "FN", "ZS", "ZT", "ZEA"); + INPUT_CHANNEL_SET("[0-9]{2}", "FN", "ZS", "ZT", "ZEA"), + LISTENING_MODE_SET("[0-9]{4}", "SR"); private String[] zoneCommands; private String parameterPattern; diff --git a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/Response.java b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/Response.java index 1e0afd6253e8c..89d6269f1bd01 100644 --- a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/Response.java +++ b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/Response.java @@ -23,6 +23,7 @@ * Represent an AVR response. * * @author Antoine Besnard - Initial contribution + * @author Leroy Foerster - Listening Mode, Playing Listening Mode */ public class Response implements AvrResponse { @@ -34,6 +35,8 @@ public enum ResponseType implements AvrResponse.AvrResponseType { VOLUME_LEVEL("[0-9]{2,3}", "VOL", "ZV", "YV", "HZV"), MUTE_STATE("[0-1]", "MUT", "Z2MUT", "Z3MUT", "HZM"), INPUT_SOURCE_CHANNEL("[0-9]{2}", "FN", "Z2F", "Z3F", "ZEA"), + LISTENING_MODE("[0-9]{4}", "SR"), + PLAYING_LISTENING_MODE("[0-9a-f]{4}", "LM"), DISPLAY_INFORMATION("[0-9a-fA-F]{30}", "FL"); private String[] responsePrefixZone; diff --git a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/SimpleCommand.java b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/SimpleCommand.java index a12994c89a754..524711c509212 100644 --- a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/SimpleCommand.java +++ b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/SimpleCommand.java @@ -18,6 +18,7 @@ * A simple command without parameters. * * @author Antoine Besnard - Initial contribution + * @author Leroy Foerster - Listening Mode, Playing Listening Mode */ public class SimpleCommand implements AvrCommand { @@ -37,6 +38,8 @@ public enum SimpleCommandType implements AvrCommand.CommandType { MUTE_QUERY("?M", "?Z2M", "?Z3M", "?HZM"), INPUT_CHANGE_CYCLIC("FU"), INPUT_CHANGE_REVERSE("FD"), + LISTENING_MODE_CHANGE_CYCLIC("0010SR"), + LISTENING_MODE_QUERY("?S"), INPUT_QUERY("?F", "?ZS", "?ZT", "?ZEA"); private String zoneCommands[]; diff --git a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/StreamAvrConnection.java b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/StreamAvrConnection.java index f74e0484c82e9..5e605399d6ca8 100644 --- a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/StreamAvrConnection.java +++ b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/StreamAvrConnection.java @@ -55,6 +55,7 @@ * * @author Antoine Besnard - Initial contribution * @author Rainer Ostendorf - Initial contribution + * @author Leroy Foerster - Listening Mode, Playing Listening Mode */ public abstract class StreamAvrConnection implements AvrConnection { @@ -189,6 +190,11 @@ public boolean sendSourceInputQuery(int zone) { return sendCommand(RequestResponseFactory.getIpControlCommand(SimpleCommandType.INPUT_QUERY, zone)); } + @Override + public boolean sendListeningModeQuery(int zone) { + return sendCommand(RequestResponseFactory.getIpControlCommand(SimpleCommandType.LISTENING_MODE_QUERY, zone)); + } + @Override public boolean sendPowerCommand(Command command, int zone) throws CommandTypeNotSupportedException { AvrCommand commandToSend = null; @@ -267,6 +273,23 @@ public boolean sendInputSourceCommand(Command command, int zone) throws CommandT return sendCommand(commandToSend); } + @Override + public boolean sendListeningModeCommand(Command command, int zone) throws CommandTypeNotSupportedException { + AvrCommand commandToSend = null; + + if (command == IncreaseDecreaseType.INCREASE) { + commandToSend = RequestResponseFactory.getIpControlCommand(SimpleCommandType.LISTENING_MODE_CHANGE_CYCLIC, zone); + } else if (command instanceof StringType) { + String listeningModeValue = ((StringType) command).toString(); + commandToSend = RequestResponseFactory.getIpControlCommand(ParameterizedCommandType.LISTENING_MODE_SET, zone) + .setParameter(listeningModeValue); + } else { + throw new CommandTypeNotSupportedException("Command type not supported."); + } + + return sendCommand(commandToSend); + } + @Override public boolean sendMuteCommand(Command command, int zone) throws CommandTypeNotSupportedException { AvrCommand commandToSend = null; diff --git a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/avr/AvrConnection.java b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/avr/AvrConnection.java index eb224f18ab567..0af0b4a62a3eb 100644 --- a/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/avr/AvrConnection.java +++ b/bundles/org.openhab.binding.pioneeravr/src/main/java/org/openhab/binding/pioneeravr/internal/protocol/avr/AvrConnection.java @@ -20,6 +20,7 @@ * Represent a connection to a remote Pioneer AVR. * * @author Antoine Besnard - Initial contribution + * @author Leroy Foerster - Listening Mode, Playing Listening Mode */ public interface AvrConnection { @@ -87,6 +88,14 @@ public interface AvrConnection { */ public boolean sendSourceInputQuery(int zone); + /** + * Send a listening mode state query to the AVR + * + * @param zone + * @return + */ + public boolean sendListeningModeQuery(int zone); + /** * Send a power command ot the AVR based on the openHAB command * @@ -114,6 +123,15 @@ public interface AvrConnection { */ public boolean sendInputSourceCommand(Command command, int zone) throws CommandTypeNotSupportedException; + /** + * Send a listening mode selection command to the AVR based on the openHAB command + * + * @param command + * @param zone + * @return + */ + public boolean sendListeningModeCommand(Command command, int zone) throws CommandTypeNotSupportedException; + /** * Send a mute command to the AVR based on the openHAB command * diff --git a/bundles/org.openhab.binding.pioneeravr/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.pioneeravr/src/main/resources/ESH-INF/thing/thing-types.xml index 77d124aa4353b..54b7ebe2419b6 100644 --- a/bundles/org.openhab.binding.pioneeravr/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.pioneeravr/src/main/resources/ESH-INF/thing/thing-types.xml @@ -70,7 +70,7 @@ - Control a 2015 Pioneer AVR (SC-89, SC-LX88, SC-87, SC-LX78, SC-85, SC-LX58, SC-82, SC-2024, SC-81, VSX-80) over IP + Control a 2015 Pioneer AVR (SC-89, SC-LX88, SC-87, SC-LX78, SC-85, SC-LX58, SC-82, SC-2024, SC-81, VSX-80, VSX-830) over IP @@ -198,6 +198,8 @@ + + @@ -209,6 +211,8 @@ + + @@ -220,6 +224,8 @@ + + @@ -231,6 +237,8 @@ + + @@ -242,6 +250,8 @@ + + @@ -253,6 +263,8 @@ + + @@ -264,6 +276,8 @@ + + @@ -275,6 +289,8 @@ + + @@ -286,6 +302,8 @@ + + @@ -297,6 +315,8 @@ + + @@ -626,4 +646,76 @@ + + String + + Set the listening mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String + + Display the effective listening mode + + + diff --git a/bundles/org.openhab.binding.robonect/.classpath b/bundles/org.openhab.binding.robonect/.classpath index 448baf9f776be..615608997a6c5 100644 --- a/bundles/org.openhab.binding.robonect/.classpath +++ b/bundles/org.openhab.binding.robonect/.classpath @@ -13,9 +13,9 @@ + - @@ -28,6 +28,5 @@ - diff --git a/bundles/org.openhab.binding.samsungtv/.classpath b/bundles/org.openhab.binding.samsungtv/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.samsungtv/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.samsungtv/.project b/bundles/org.openhab.binding.samsungtv/.project new file mode 100644 index 0000000000000..fff7cc3e80298 --- /dev/null +++ b/bundles/org.openhab.binding.samsungtv/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.samsungtv + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.ihc.test/NOTICE b/bundles/org.openhab.binding.samsungtv/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.ihc.test/NOTICE rename to bundles/org.openhab.binding.samsungtv/NOTICE diff --git a/addons/binding/org.openhab.binding.samsungtv/README.md b/bundles/org.openhab.binding.samsungtv/README.md similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/README.md rename to bundles/org.openhab.binding.samsungtv/README.md diff --git a/bundles/org.openhab.binding.samsungtv/pom.xml b/bundles/org.openhab.binding.samsungtv/pom.xml new file mode 100644 index 0000000000000..e4a348145b161 --- /dev/null +++ b/bundles/org.openhab.binding.samsungtv/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.samsungtv + + openHAB Add-ons :: Bundles :: SamsungTV Binding + + diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/SamsungTvBindingConstants.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/SamsungTvBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/SamsungTvBindingConstants.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/SamsungTvBindingConstants.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/SamsungTvHandlerFactory.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/SamsungTvHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/SamsungTvHandlerFactory.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/SamsungTvHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/config/SamsungTvConfiguration.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/config/SamsungTvConfiguration.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/config/SamsungTvConfiguration.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/config/SamsungTvConfiguration.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/discovery/SamsungTvDiscoveryParticipant.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/discovery/SamsungTvDiscoveryParticipant.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/discovery/SamsungTvDiscoveryParticipant.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/discovery/SamsungTvDiscoveryParticipant.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/handler/SamsungTvHandler.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/handler/SamsungTvHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/handler/SamsungTvHandler.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/handler/SamsungTvHandler.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/KeyCode.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/KeyCode.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/KeyCode.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/KeyCode.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/RemoteController.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/RemoteController.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/RemoteController.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/RemoteController.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/RemoteControllerException.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/RemoteControllerException.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/RemoteControllerException.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/RemoteControllerException.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/DataConverters.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/DataConverters.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/DataConverters.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/DataConverters.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/MainTVServerService.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/MainTVServerService.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/MainTVServerService.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/MainTVServerService.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/MediaRendererService.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/MediaRendererService.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/MediaRendererService.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/MediaRendererService.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/RemoteControllerService.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/RemoteControllerService.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/RemoteControllerService.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/RemoteControllerService.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/SamsungTvUtils.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/SamsungTvUtils.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/SamsungTvUtils.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/SamsungTvUtils.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/ServiceFactory.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/ServiceFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/ServiceFactory.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/ServiceFactory.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/api/EventListener.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/api/EventListener.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/api/EventListener.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/api/EventListener.java diff --git a/addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/api/SamsungTvService.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/api/SamsungTvService.java similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/api/SamsungTvService.java rename to bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/service/api/SamsungTvService.java diff --git a/addons/binding/org.openhab.binding.samsungtv/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.samsungtv/ESH-INF/config/config.xml b/bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/config/config.xml similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/ESH-INF/config/config.xml rename to bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/config/config.xml diff --git a/addons/binding/org.openhab.binding.samsungtv/ESH-INF/i18n/samsung_de.properties b/bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/i18n/samsung_de.properties similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/ESH-INF/i18n/samsung_de.properties rename to bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/i18n/samsung_de.properties diff --git a/addons/binding/org.openhab.binding.samsungtv/ESH-INF/thing/channel-types.xml b/bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/thing/channel-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/ESH-INF/thing/channel-types.xml rename to bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/thing/channel-types.xml diff --git a/addons/binding/org.openhab.binding.samsungtv/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/thing/thing-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/ESH-INF/thing/thing-types.xml rename to bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/bundles/org.openhab.binding.silvercrestwifisocket/src/main/java/org/openhab/binding/silvercrestwifisocket/internal/handler/SilvercrestWifiSocketHandler.java b/bundles/org.openhab.binding.silvercrestwifisocket/src/main/java/org/openhab/binding/silvercrestwifisocket/internal/handler/SilvercrestWifiSocketHandler.java index 3628a733ded34..5c8ae8bd713a6 100644 --- a/bundles/org.openhab.binding.silvercrestwifisocket/src/main/java/org/openhab/binding/silvercrestwifisocket/internal/handler/SilvercrestWifiSocketHandler.java +++ b/bundles/org.openhab.binding.silvercrestwifisocket/src/main/java/org/openhab/binding/silvercrestwifisocket/internal/handler/SilvercrestWifiSocketHandler.java @@ -291,7 +291,7 @@ private void sendCommand(final SilvercrestWifiSocketRequestType type) { address = InetAddress.getByName(this.hostAddress); this.sendRequestPacket(new SilvercrestWifiSocketRequest(this.macAddress, type, this.vendor), address); } catch (UnknownHostException e) { - logger.debug("Host Address not found: {}. Will lookup Mac address."); + logger.debug("Host Address not found: {}. Will lookup Mac address.", this.hostAddress); this.hostAddress = null; this.lookupForSocketHostAddress(); } diff --git a/bundles/org.openhab.binding.smartmeter/.classpath b/bundles/org.openhab.binding.smartmeter/.classpath new file mode 100644 index 0000000000000..581b05c1a76d3 --- /dev/null +++ b/bundles/org.openhab.binding.smartmeter/.classpath @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.smartmeter/.project b/bundles/org.openhab.binding.smartmeter/.project new file mode 100644 index 0000000000000..9e411b6984d04 --- /dev/null +++ b/bundles/org.openhab.binding.smartmeter/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.smartmeter + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.smartmeter/NOTICE b/bundles/org.openhab.binding.smartmeter/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/NOTICE rename to bundles/org.openhab.binding.smartmeter/NOTICE diff --git a/addons/binding/org.openhab.binding.smartmeter/README.md b/bundles/org.openhab.binding.smartmeter/README.md similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/README.md rename to bundles/org.openhab.binding.smartmeter/README.md diff --git a/bundles/org.openhab.binding.smartmeter/pom.xml b/bundles/org.openhab.binding.smartmeter/pom.xml new file mode 100644 index 0000000000000..e4251fdf3f35f --- /dev/null +++ b/bundles/org.openhab.binding.smartmeter/pom.xml @@ -0,0 +1,68 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.smartmeter + + openHAB Add-ons :: Bundles :: Smartmeter Binding + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + generate-sources + + add-source + + + + src/3rdparty + + + + + + + + + + + io.reactivex.rxjava2 + rxjava + 2.2.3 + provided + + + org.reactivestreams + reactive-streams + 1.0.2 + provided + + + org.openmuc + jsml + 1.1.2 + provided + + + org.openmuc + j62056 + 2.1.0 + provided + + + + + org.openmuc.jrxtx;version="1.1.2" + + diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/DataBits.java b/bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/DataBits.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/DataBits.java rename to bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/DataBits.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/FlowControl.java b/bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/FlowControl.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/FlowControl.java rename to bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/FlowControl.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/JRxTxPort.java b/bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/JRxTxPort.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/JRxTxPort.java rename to bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/JRxTxPort.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/Parity.java b/bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/Parity.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/Parity.java rename to bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/Parity.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/PortNotFoundException.java b/bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/PortNotFoundException.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/PortNotFoundException.java rename to bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/PortNotFoundException.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/SerialPort.java b/bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/SerialPort.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/SerialPort.java rename to bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/SerialPort.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/SerialPortBuilder.java b/bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/SerialPortBuilder.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/SerialPortBuilder.java rename to bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/SerialPortBuilder.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/SerialPortException.java b/bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/SerialPortException.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/SerialPortException.java rename to bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/SerialPortException.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/SerialPortTimeoutException.java b/bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/SerialPortTimeoutException.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/SerialPortTimeoutException.java rename to bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/SerialPortTimeoutException.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/StopBits.java b/bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/StopBits.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java-extern/org/openmuc/jrxtx/StopBits.java rename to bundles/org.openhab.binding.smartmeter/src/3rdparty/java/org/openmuc/jrxtx/StopBits.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/SmartMeterBindingConstants.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/SmartMeterBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/SmartMeterBindingConstants.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/SmartMeterBindingConstants.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/SmartMeterConfiguration.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/SmartMeterConfiguration.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/SmartMeterConfiguration.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/SmartMeterConfiguration.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/connectors/ConnectorBase.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/connectors/ConnectorBase.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/connectors/ConnectorBase.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/connectors/ConnectorBase.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/connectors/IMeterReaderConnector.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/connectors/IMeterReaderConnector.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/connectors/IMeterReaderConnector.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/connectors/IMeterReaderConnector.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterDevice.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterDevice.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterDevice.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterDevice.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterDeviceFactory.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterDeviceFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterDeviceFactory.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterDeviceFactory.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterValue.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterValue.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterValue.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterValue.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterValueListener.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterValueListener.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterValueListener.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/MeterValueListener.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/ObisCode.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/ObisCode.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/ObisCode.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/ObisCode.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterChannelTypeProvider.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterChannelTypeProvider.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterChannelTypeProvider.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterChannelTypeProvider.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterConfigProvider.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterConfigProvider.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterConfigProvider.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterConfigProvider.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterHandler.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterHandler.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterHandler.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterHandlerFactory.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterHandlerFactory.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/SmartMeterHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/Conformity.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/Conformity.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/Conformity.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/Conformity.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/negate/NegateBitModel.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/negate/NegateBitModel.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/negate/NegateBitModel.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/negate/NegateBitModel.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/negate/NegateBitParser.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/negate/NegateBitParser.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/negate/NegateBitParser.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/negate/NegateBitParser.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/negate/NegateHandler.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/negate/NegateHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/negate/NegateHandler.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/conformity/negate/NegateHandler.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/Baudrate.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/Baudrate.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/Baudrate.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/Baudrate.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/ProtocolMode.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/ProtocolMode.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/ProtocolMode.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/ProtocolMode.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/SerialParameter.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/SerialParameter.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/SerialParameter.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/helper/SerialParameter.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/iec62056/Iec62056_21MeterReader.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/iec62056/Iec62056_21MeterReader.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/iec62056/Iec62056_21MeterReader.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/iec62056/Iec62056_21MeterReader.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/iec62056/Iec62056_21SerialConnector.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/iec62056/Iec62056_21SerialConnector.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/iec62056/Iec62056_21SerialConnector.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/iec62056/Iec62056_21SerialConnector.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/iec62056/Iec62056_21UnitConversion.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/iec62056/Iec62056_21UnitConversion.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/iec62056/Iec62056_21UnitConversion.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/iec62056/Iec62056_21UnitConversion.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlFileDebugOutput.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlFileDebugOutput.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlFileDebugOutput.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlFileDebugOutput.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlMeterReader.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlMeterReader.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlMeterReader.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlMeterReader.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlSerialConnector.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlSerialConnector.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlSerialConnector.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlSerialConnector.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlUnitConversion.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlUnitConversion.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlUnitConversion.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlUnitConversion.java diff --git a/addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlValueExtractor.java b/bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlValueExtractor.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlValueExtractor.java rename to bundles/org.openhab.binding.smartmeter/src/main/java/org/openhab/binding/smartmeter/internal/sml/SmlValueExtractor.java diff --git a/addons/binding/org.openhab.binding.smartmeter/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.smartmeter/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.smartmeter/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.smartmeter/ESH-INF/config/config-descriptions.xml b/bundles/org.openhab.binding.smartmeter/src/main/resources/ESH-INF/config/config-descriptions.xml similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/ESH-INF/config/config-descriptions.xml rename to bundles/org.openhab.binding.smartmeter/src/main/resources/ESH-INF/config/config-descriptions.xml diff --git a/addons/binding/org.openhab.binding.smartmeter/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.smartmeter/src/main/resources/ESH-INF/thing/thing-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter/ESH-INF/thing/thing-types.xml rename to bundles/org.openhab.binding.smartmeter/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/addons/binding/org.openhab.binding.smartmeter.test/src/test/java/org/openhab/binding/smartmeter/MockMeterReaderConnector.java b/bundles/org.openhab.binding.smartmeter/src/test/java/org/openhab/binding/smartmeter/MockMeterReaderConnector.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter.test/src/test/java/org/openhab/binding/smartmeter/MockMeterReaderConnector.java rename to bundles/org.openhab.binding.smartmeter/src/test/java/org/openhab/binding/smartmeter/MockMeterReaderConnector.java diff --git a/addons/binding/org.openhab.binding.smartmeter.test/src/test/java/org/openhab/binding/smartmeter/TestMeterReading.java b/bundles/org.openhab.binding.smartmeter/src/test/java/org/openhab/binding/smartmeter/TestMeterReading.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter.test/src/test/java/org/openhab/binding/smartmeter/TestMeterReading.java rename to bundles/org.openhab.binding.smartmeter/src/test/java/org/openhab/binding/smartmeter/TestMeterReading.java diff --git a/addons/binding/org.openhab.binding.smartmeter.test/src/test/java/org/openhab/binding/smartmeter/TestNegateBit.java b/bundles/org.openhab.binding.smartmeter/src/test/java/org/openhab/binding/smartmeter/TestNegateBit.java similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter.test/src/test/java/org/openhab/binding/smartmeter/TestNegateBit.java rename to bundles/org.openhab.binding.smartmeter/src/test/java/org/openhab/binding/smartmeter/TestNegateBit.java diff --git a/bundles/org.openhab.binding.spotify/README.md b/bundles/org.openhab.binding.spotify/README.md index 36edbecec8728..896ea2961ed70 100644 --- a/bundles/org.openhab.binding.spotify/README.md +++ b/bundles/org.openhab.binding.spotify/README.md @@ -51,13 +51,13 @@ The following configuration options are available on the Spotify Bridge player: |---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| | clientId | This is the Client ID provided by Spotify when you add a new Application for openHAB to your Spotify Account. Go to https://developer.spotify.com/ (Required) | | clientSecret | This is the Client Secret provided by Spotify when you add a new Application for openHAB to your Spotify Account. (Required) | -| refreshPeriod | This is the frequency of the polling requests to the Spotify Connect Web API in seconds. | +| refreshPeriod | This is the frequency of the polling requests to the Spotify Connect Web API in seconds. | The following configuration option is available on the Spotify device: -| Parameter | Description | -|-----------|-------------------------------------------------------| -| id | This is the device ID provided by Spotify (Required). | +| Parameter | Description | +|------------|---------------------------------------------------------| +| deviceName | This is the device name provided by Spotify (Required). | ## Supported Things @@ -65,8 +65,13 @@ The following configuration option is available on the Spotify device: All Spotify Connect capable devices should be discoverable through this binding. If you can control them from Spotify Player app on your PC/Mac/iPhone/Android/xxx you should be able to add it as a thing. Some devices can be restricted and not available for playing. The bridge will make these available in the discovery of devices, but they will never be ONLINE. -A Spotify web player in a browser is only available as long as the page is open. It will get a unique id for that session. If you close the page it will be gone. Opening a new web player will result in a new id. -Some devices will not be visible (i.e. Chrome casts) when they are not active. They go into a sleep mode and are not visible through the Spotify Web API. The binding will them show as _GONE_. +A Spotify web player in a browser is only available as long as the page is open. +It will get a unique id for that session. +If you close the page it will be gone. +Opening a new web player will result in a new id. +Some devices will not be visible (i.e. Chrome casts) when they are not active. +Some devices will not be visible (i.e. Chrome casts) when they are not active (they go into a sleep mode and are not visible through the Spotify Web API). +The binding will show them as _GONE_. ## Discovery @@ -75,7 +80,8 @@ As long as Spotify Connect devices are available in the context of the user acco If no devices are showing up, try to connect to the device(s) from your smartphone or computer to make sure the device(s) are in use by your user account. The discovery of devices in the Spotify Web API is based on what is known by Spotify. -There is difference from e.g. smartphones and computers which can discover devices on the local network - the Web API cannot do that. It only knows about a device if your account is currently associated with the device. +There is difference between e.g. smartphones and computers which can discover devices on the local network and the Web API which is not able to do so. +It only knows about a device if your account is currently associated with the device. ## Channels @@ -87,7 +93,8 @@ __Common Channels:__ | Channel Type ID | Item Type | Read/Write | Description | |-----------------|-----------|------------|--------------------------------------------------------------------------------------------------| -| deviceName | Selection | Read-only | Name of the currently active Connect Device, set the device ID to transfer play to that device. | +| deviceName | String | Read-write | Name of the currently active Connect Device, | +| devices | Selection | Read-write | List of currently active Connect Devices, Set the device ID to transfer play to that device. | | deviceVolume | Dimmer | Read-write | Get or set the active Connect Device volume. | | deviceShuffle | Switch | Read-write | Turn on/off shuffle play on the active device. | | trackPlay | String | Read-write | Set which music to play on the active device. This channel accepts Spotify URIs and URLs. | @@ -98,7 +105,8 @@ __Common Channels:__ | trackDurationMs | Number | Read-only | The duration of the currently playing track in milliseconds. | | trackProgress | String | Read-only | The progress (m:ss) of the currently playing track. This is updated every second. | | trackProgressMs | Number | Read-only | The progress of the currently playing track in milliseconds. | -| playlist | Selection | Read-only | This channel will be populated with the users playlists. Set the playlist ID to start. | +| playlists | Selection | Read-write | This channel will be populated with the users playlists. Set the playlist ID to start. | +| playlistName | String | Read-write | The currently playing playlist. Or empty if no playing list is playing. | | albumName | String | Read-only | Album Name of the currently playing track. | | albumImage | RawType | Read-only | Album Image of the currently playing track. | | artistName | String | Read-only | Artist Name of the currently playing track. | @@ -108,24 +116,25 @@ They are dynamically populated by the binding with the user specific devices and __Advanced Channels:__ -| Channel Type ID | Item Type | Read/Write | Description | -|-----------------|-----------|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| accessToken | String | Read-only | The current accessToken used in communication with Web API. This can be used in client-side scripting towards the Web API if you would like to maintain your playlists etc. | -| trackId | String | Read-only | Track Id of the currently playing track. | -| trackHref | String | Read-only | Track URL of the currently playing track. | -| trackUri | String | Read-only | Track URI of the currently playing track. | -| trackType | String | Read-only | Type of the currently playing track. | -| trackNumber | String | Read-only | Number of the track on the album/record. | -| trackDiscNumber | String | Read-only | Disc Number of the track on the album/record. | -| trackPopularity | Number | Read-only | Currently playing track popularity. | -| albumId | String | Read-only | Album Id of the currently playing track. | -| albumUri | String | Read-only | Album URI of the currently playing track. | -| albumHref | String | Read-only | Album URL of the currently playing track. | -| albumType | String | Read-only | Album Type of the currently playing track. | -| artistId | String | Read-only | Artist Id of the currently playing track. | -| artistUri | String | Read-only | Artist URI of the currently playing track. | -| artistHref | String | Read-only | Artist URL of the currently playing track. | -| artistType | String | Read-only | Artist Type of the currently playing track. | +| Channel Type ID | Item Type | Read/Write | Description | +|-----------------|-----------|------------|-------------------------------------------------------------| +| accessToken | String | Read-only | The current accessToken used in communication with Web API. | +| deviceId | String | Read-write | The Spotify Connect device Id. | +| trackId | String | Read-only | Track Id of the currently playing track. | +| trackHref | String | Read-only | Track URL of the currently playing track. | +| trackUri | String | Read-only | Track URI of the currently playing track. | +| trackType | String | Read-only | Type of the currently playing track. | +| trackNumber | String | Read-only | Number of the track on the album/record. | +| trackDiscNumber | String | Read-only | Disc Number of the track on the album/record. | +| trackPopularity | Number | Read-only | Currently playing track popularity. | +| albumId | String | Read-only | Album Id of the currently playing track. | +| albumUri | String | Read-only | Album URI of the currently playing track. | +| albumHref | String | Read-only | Album URL of the currently playing track. | +| albumType | String | Read-only | Album Type of the currently playing track. | +| artistId | String | Read-only | Artist Id of the currently playing track. | +| artistUri | String | Read-only | Artist URI of the currently playing track. | +| artistHref | String | Read-only | Artist URL of the currently playing track. | +| artistType | String | Read-only | Artist Type of the currently playing track. | ### Devices @@ -136,19 +145,19 @@ Assigning a playlist to the _trackPlay_ channel of the bridge will start playing __Common Channels:__ -| Channel Type ID | Item Type | Read/Write | Description | -|-----------------|-----------|------------|-----------------------------------------------------------------------------------------------------------------------------------------| -| trackPlay | String | Read-write | Track to play on the device. Assigning a track, playlist, artist etc will activate the device and make it the currently playing device. | -| deviceName | String | Read-only | Name of the device. | -| deviceVolume | Dimmer | Read-write | Volume setting for the device. | -| devicePlayer | Player | Read-write | Player Control of the device. | -| deviceShuffle | Switch | Read-write | Turn on/off shuffle play. | +| Channel Type ID | Item Type | Read/Write | Description | +|-----------------|-----------|------------|-----------------------------------------------------------------| +| trackPlay | String | Write-only | Update to play a track, playlist, artist. Activates the device. | +| deviceName | String | Read-only | Name of the device. | +| deviceVolume | Dimmer | Read-write | Volume setting for the device. | +| devicePlayer | Player | Read-write | Player Control of the device. | +| deviceShuffle | Switch | Read-write | Turn on/off shuffle play. | __Advanced Channels:__ | Channel Type ID | Item Type | Read/Write | Description | |------------------|-----------|------------|------------------------------------------------------------------------------------------------------------| -| deviceId | String | Read-only | The Spotify Connect device Id. | +| deviceId | String | Read-write | The Spotify Connect device Id. | | deviceType | String | Read-only | The type of device e.g. Speaker, Smartphone. | | deviceActive | Switch | Read-only | Indicates if the device is active or not. Should be the same as Thing status ONLINE/OFFLINE. | | deviceRestricted | Switch | Read-only | Indicates if this device allows to be controlled by the API or not. If restricted it cannot be controlled. | @@ -162,16 +171,36 @@ spotify.things: ``` Bridge spotify:player:user1 "Me" [clientId="", clientSecret=""] { Things: - device device1 "Device 1" [id=""] - device device2 "Device 2" [id=""] + device device1 "Device 1" [deviceName=""] + device device2 "Device 2" [deviceName=""] } ``` spotify.items: ``` -Player device1Player {channel="spotify:device:user1:device1:devicePlayer"} -Player device2Player {channel="spotify:device:user1:device2:devicePlayer"} +Player spotifyTrackPlayer label="Player" {channel="spotify:player:user1:trackPlayer"} +String spotifyDevices label="Active device [%s]" {channel="spotify:player:user1:devices"} +Switch spotifyDeviceShuffle label="Shuffle mode" {channel="spotify:player:user1:deviceShuffle"} +String spotifyTrackRepeat label="Repeat mode: [%s]" {channel="spotify:player:user1:trackRepeat"} +String spotifyTrackProgress label="Track progress: [%s]" {channel="spotify:player:user1:trackProgress"} +String spotifyTrackDuration label="Track duration: [%s]" {channel="spotify:player:user1:tackDuration"} +String spotifyTrackName label="Track Name: [%s]" {channel="spotify:player:user1:trackName"} +String spotifyAlbumName label="Album Name: [%s]" {channel="spotify:player:user1:albumName"} +String spotifyArtistName label="Artist Name: [%s]" {channel="spotify:player:user1:artistName"} +Image spotifyAlbumImage label="Album Art" {channel="spotify:player:user1:albumImage"} +String spotifyPlaylists label="Playlists [%s]" {channel="spotify:player:user1:playlists"} +String spotifyPlayName label="Playlist [%s]" {channel="spotify:player:user1:playlistName"} + +String device1DeviceName {channel="spotify:device:user1:device1:deviceName"} +Player device1Player {channel="spotify:device:user1:device1:devicePlayer"} +Dimmer device1DeviceVolume {channel="spotify:device:user1:device1:deviceVolume"} +Switch device1DeviceShuffle {channel="spotify:device:user1:device1:deviceShuffle"} + +String device2DeviceName {channel="spotify:device:user1:device2:deviceName"} +Player device2Player {channel="spotify:device:user1:device2:devicePlayer"} +Dimmer device2DeviceVolume {channel="spotify:device:user1:device2:deviceVolume"} +Switch device2DeviceShuffle {channel="spotify:device:user1:device2:deviceShuffle"} ``` spotify.sitemap: @@ -180,30 +209,31 @@ spotify.sitemap: sitemap spotify label="Spotify Sitemap" { Frame label="Spotify Player Info" { - Selection item=spotify_player_user1_deviceName label="Active device [%]" - Player item="spotify_player_user1_trackPlayer - Text item=spotify_player_user1_deviceShuffle label="Currently Player shuffle mode: [%s]" - Text item=spotify_player_user1_trackRepeat label="Currently Player repeat mode: [%s]" - Text item=spotify_player_user1_trackProgress label="Currently Played track progress: [%s]" - Text item=spotify_player_user1_trackDuration label="Currently Played track duration: [%s]" - Text item=spotify_player_user1_trackName label="Currently Played Track Name: [%s]" - Text item=spotify_player_user1_albumName label="Currently Played Album Name: [%s]" - Text item=spotify_player_user1_artistName label="Currently Played Artist Name: [%s]" - Selection item=spotify_player_user1_trackPlay label="Playlist" icon="music" + Selection item=spotifyDevices label="Active device [%]" + Default item=spotifyTrackPlayer label="Player" + Switch item=spotifyDeviceShuffle label="Shuffle mode:" + Text item=spotifyTrackRepeat label="Repeat mode: [%s]" + Text item=spotifyTrackProgress label="Track progress: [%s]" + Text item=spotifyTrackDuration label="Track duration: [%s]" + Text item=spotifyTrackName label="Track Name: [%s]" + Image item=spotifyAlbumImage label="Album Art" + Text item=spotifyAlbumName label="Currently Played Album Name: [%s]" + Text item=spotifyTrtistName label="Currently Played Artist Name: [%s]" + Selection item=spotifyTrackPlay label="Playlist" icon="music" } Frame label="My Spotify Device 1" { - Text item=spotify_device_user1_device1_deviceName label="Device Name [%s]" - Player item=device1Player - Slider item=spotify_device_user1_device1_deviceVolume - Switch item=spotify_device_user1_device1_deviceShuffle + Text item=device1DeviceName label="Device Name [%s]" + Default item=device1Player + Slider item=device1DeviceVolume + Switch item=device1DeviceShuffle } Frame label="My Spotify Device 2" { - Text item=spotify_device_user1_device2_deviceName label="Device Name [%s]" - Player item=device2Player - Slider item=spotify_device_user1_device2_deviceVolume - Switch item=spotify_device_user1_device2_deviceShuffle + Text item=device2DeviceName label="Device Name [%s]" + Default item=device2Player + Slider item=device2DeviceVolume + Switch item=device2DeviceShuffle } } ``` diff --git a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/SpotifyBindingConstants.java b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/SpotifyBindingConstants.java index 1db8c58468e40..f8fb038a890fb 100644 --- a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/SpotifyBindingConstants.java +++ b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/SpotifyBindingConstants.java @@ -53,7 +53,8 @@ public class SpotifyBindingConstants { public static final String CHANNEL_TRACKPLAYER = "trackPlayer"; public static final String CHANNEL_TRACKREPEAT = "trackRepeat"; - public static final String CHANNEL_PLAYLIST = "playlist"; + public static final String CHANNEL_PLAYLISTS = "playlists"; + public static final String CHANNEL_PLAYLISTNAME = "playlistName"; public static final String CHANNEL_PLAYED_TRACKID = "trackId"; public static final String CHANNEL_PLAYED_TRACKURI = "trackUri"; @@ -82,7 +83,7 @@ public class SpotifyBindingConstants { public static final String CHANNEL_PLAYED_ARTISTTYPE = "artistType"; public static final String CHANNEL_DEVICEID = "deviceId"; - public static final String CHANNEL_TYPE_ACTIVE_DEVICENAME = "activeDeviceName"; + public static final String CHANNEL_DEVICES = "devices"; public static final String CHANNEL_DEVICENAME = "deviceName"; public static final String CHANNEL_DEVICETYPE = "deviceType"; public static final String CHANNEL_DEVICEACTIVE = "deviceActive"; @@ -96,5 +97,5 @@ public class SpotifyBindingConstants { // List of Bridge/Thing properties public static final String PROPERTY_SPOTIFY_USER = "user"; - public static final String PROPERTY_SPOTIFY_DEVICE_ID = "id"; + public static final String PROPERTY_SPOTIFY_DEVICE_NAME = "deviceName"; } diff --git a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/api/SpotifyConnector.java b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/api/SpotifyConnector.java index 606c0d2627938..aebd2901ac04a 100644 --- a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/api/SpotifyConnector.java +++ b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/api/SpotifyConnector.java @@ -48,9 +48,8 @@ class SpotifyConnector { private static final String RETRY_AFTER_HEADER = "Retry-After"; private static final String AUTHORIZATION_HEADER = "Authorization"; - private static final int HTTP_CLIENT_TIMEOUT_SECONDS = 30; + private static final int HTTP_CLIENT_TIMEOUT_SECONDS = 10; private static final int HTTP_CLIENT_RETRY_COUNT = 5; - private static final int DEFAULT_RETRY_DELAY_SECONDS = 5; private final Logger logger = LoggerFactory.getLogger(SpotifyConnector.class); @@ -185,8 +184,9 @@ private boolean processResponse(ContentResponse response) { break; case ACCEPTED_202: logger.debug( - "Spotify Web API returned code 202 - The request has been accepted for processing, but the processing has not been completed. Retrying..."); - delaySeconds = DEFAULT_RETRY_DELAY_SECONDS; + "Spotify Web API returned code 202 - The request has been accepted for processing, but the processing has not been completed."); + future.complete(response); + success = true; break; case BAD_REQUEST_400: throw new SpotifyException(processErrorState(response)); diff --git a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/discovery/SpotifyDeviceDiscoveryService.java b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/discovery/SpotifyDeviceDiscoveryService.java index 3b2584ba41a8e..10d63d74917f5 100644 --- a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/discovery/SpotifyDeviceDiscoveryService.java +++ b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/discovery/SpotifyDeviceDiscoveryService.java @@ -107,7 +107,7 @@ protected void startScan() { try { bridgeHandler.listDevices().forEach(this::thingDiscovered); } catch (RuntimeException e) { - logger.debug("Finding devices failed with message: ", e.getMessage()); + logger.debug("Finding devices failed with message: {}", e.getMessage(), e); } } } @@ -115,12 +115,12 @@ protected void startScan() { private void thingDiscovered(Device device) { Map properties = new HashMap(); - properties.put(PROPERTY_SPOTIFY_DEVICE_ID, device.getId()); + properties.put(PROPERTY_SPOTIFY_DEVICE_NAME, device.getName()); ThingUID thing = new ThingUID(SpotifyBindingConstants.THING_TYPE_DEVICE, bridgeUID, device.getId().substring(0, PLAYER_ID_LENGTH)); DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thing).withBridge(bridgeUID) - .withProperties(properties).withRepresentationProperty(PROPERTY_SPOTIFY_DEVICE_ID) + .withProperties(properties).withRepresentationProperty(PROPERTY_SPOTIFY_DEVICE_NAME) .withLabel(device.getName()).build(); thingDiscovered(discoveryResult); diff --git a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyBridgeHandler.java b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyBridgeHandler.java index 0f22600fcaa7a..e1d6ca1d5eb41 100644 --- a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyBridgeHandler.java +++ b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyBridgeHandler.java @@ -20,6 +20,7 @@ import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -58,6 +59,7 @@ import org.openhab.binding.spotify.internal.api.exception.SpotifyException; import org.openhab.binding.spotify.internal.api.model.Album; import org.openhab.binding.spotify.internal.api.model.Artist; +import org.openhab.binding.spotify.internal.api.model.Context; import org.openhab.binding.spotify.internal.api.model.CurrentlyPlayingContext; import org.openhab.binding.spotify.internal.api.model.Device; import org.openhab.binding.spotify.internal.api.model.Image; @@ -174,9 +176,12 @@ public String getLabel() { @Override public boolean isAuthorized() { try { - return oAuthService != null && oAuthService.getAccessTokenResponse() != null - && oAuthService.getAccessTokenResponse().getAccessToken() != null - && oAuthService.getAccessTokenResponse().getRefreshToken() != null; + if (oAuthService == null) { + return false; + } + final AccessTokenResponse accessTokenResponse = oAuthService.getAccessTokenResponse(); + return accessTokenResponse != null && accessTokenResponse.getAccessToken() != null + && accessTokenResponse.getRefreshToken() != null; } catch (OAuthException | IOException | OAuthResponseException | RuntimeException e) { logger.debug("Exception checking authorization: ", e); return false; @@ -274,16 +279,18 @@ public List listDevices() { /** * Scheduled method to restart polling in case polling is not running. */ - private synchronized void scheduledPollingRestart() { - try { - final boolean pollingNotRunning = pollingFuture == null || pollingFuture.isCancelled(); + private void scheduledPollingRestart() { + synchronized (pollSynchronization) { + try { + final boolean pollingNotRunning = pollingFuture == null || pollingFuture.isCancelled(); - expireCache(); - if (pollStatus() && pollingNotRunning) { - startPolling(); + expireCache(); + if (pollStatus() && pollingNotRunning) { + startPolling(); + } + } catch (RuntimeException e) { + logger.debug("Restarting polling failed: ", e); } - } catch (RuntimeException e) { - logger.debug("Restarting polling failed: ", e); } } @@ -291,12 +298,14 @@ private synchronized void scheduledPollingRestart() { * This method initiates a new thread for polling the available Spotify Connect devices and update the player * information. */ - private synchronized void startPolling() { - cancelSchedulers(); - if (active) { - expireCache(); - pollingFuture = scheduler.scheduleWithFixedDelay(this::pollStatus, 0, configuration.refreshPeriod, - TimeUnit.SECONDS); + private void startPolling() { + synchronized (pollSynchronization) { + cancelSchedulers(); + if (active) { + expireCache(); + pollingFuture = scheduler.scheduleWithFixedDelay(this::pollStatus, 0, configuration.refreshPeriod, + TimeUnit.SECONDS); + } } } @@ -316,19 +325,21 @@ private boolean pollStatus() { try { // Collect devices and populate selection with available devices. final List ld = devicesCache.getValue(); - final List listDevices = ld == null ? Collections.emptyList() : ld; - spotifyDynamicStateDescriptionProvider.setDevices(listDevices); + final List devices = ld == null ? Collections.emptyList() : ld; + spotifyDynamicStateDescriptionProvider.setDevices(devices); // Collect currently playing context. final CurrentlyPlayingContext pc = playingContextCache.getValue(); final CurrentlyPlayingContext playingContext = pc == null ? EMPTY_CURRENTLYPLAYINGCONTEXT : pc; + final List lp = playlistCache.getValue(); + final List playlists = lp == null ? Collections.emptyList() : lp; updateStatus(ThingStatus.ONLINE); - updatePlayerInfo(playingContext); - final List playlists = playlistCache.getValue(); + handleCommand.setLists(devices, playlists); + updatePlayerInfo(playingContext, playlists); spotifyDynamicStateDescriptionProvider .setPlayList(playlists == null ? Collections.emptyList() : playlists); - updateDevicesStatus(listDevices, playingContext.isPlaying()); + updateDevicesStatus(devices, playingContext.isPlaying()); return true; } catch (SpotifyAuthorizationException e) { logger.debug("Authorization error during polling: ", e); @@ -383,8 +394,9 @@ private void updateDevicesStatus(List spotifyDevices, boolean playing) { * Update the player data. * * @param playerInfo The object with the current playing context + * @param playlists List of available playlists */ - private void updatePlayerInfo(CurrentlyPlayingContext playerInfo) { + private void updatePlayerInfo(CurrentlyPlayingContext playerInfo, List playlists) { updateChannelState(CHANNEL_TRACKPLAYER, playerInfo.isPlaying() ? PlayPauseType.PLAY : PlayPauseType.PAUSE); updateChannelState(CHANNEL_DEVICESHUFFLE, playerInfo.isShuffleState() ? OnOffType.ON : OnOffType.OFF); updateChannelState(CHANNEL_TRACKREPEAT, playerInfo.getRepeatState()); @@ -398,15 +410,14 @@ private void updatePlayerInfo(CurrentlyPlayingContext playerInfo) { if (!lastTrackId.equals(trackId)) { lastTrackId = trackId; updateChannelState(CHANNEL_PLAYED_TRACKDURATION_MS, new DecimalType(item.getDurationMs())); + final String formattedProgress; synchronized (MUSIC_TIME_FORMAT) { // synchronize because SimpleDateFormat is not thread safe - updateChannelState(CHANNEL_PLAYED_TRACKDURATION_FMT, - MUSIC_TIME_FORMAT.format(new Date(item.getDurationMs()))); + formattedProgress = MUSIC_TIME_FORMAT.format(new Date(item.getDurationMs())); } - updateChannelState(CHANNEL_PLAYLIST, - valueOrEmpty(playerInfo.getContext() != null && "playlist".equals(playerInfo.getContext().getType()) - ? playerInfo.getContext().getUri() - : "")); + updateChannelState(CHANNEL_PLAYED_TRACKDURATION_FMT, formattedProgress); + + updateChannelsPlayList(playerInfo, playlists); updateChannelState(CHANNEL_PLAYED_TRACKID, lastTrackId); updateChannelState(CHANNEL_PLAYED_TRACKHREF, valueOrEmpty(item.getHref())); updateChannelState(CHANNEL_PLAYED_TRACKURI, valueOrEmpty(item.getUri())); @@ -440,7 +451,8 @@ private void updatePlayerInfo(CurrentlyPlayingContext playerInfo) { if (device.getId() != null) { lastKnownDeviceId = device.getId(); updateChannelState(CHANNEL_DEVICEID, valueOrEmpty(lastKnownDeviceId)); - updateChannelState(CHANNEL_DEVICENAME, valueOrEmpty(lastKnownDeviceId)); + updateChannelState(CHANNEL_DEVICES, valueOrEmpty(lastKnownDeviceId)); + updateChannelState(CHANNEL_DEVICENAME, valueOrEmpty(device.getName())); } lastKnownDeviceActive = device.isActive(); updateChannelState(CHANNEL_DEVICEACTIVE, lastKnownDeviceActive ? OnOffType.ON : OnOffType.OFF); @@ -451,6 +463,27 @@ private void updatePlayerInfo(CurrentlyPlayingContext playerInfo) { device.getVolumePercent() == null ? UnDefType.UNDEF : new PercentType(device.getVolumePercent())); } + private void updateChannelsPlayList(CurrentlyPlayingContext playerInfo, @Nullable List playlists) { + final Context context = playerInfo.getContext(); + final String playlistId; + String playlistName = ""; + + if (context != null && "playlist".equals(context.getType())) { + playlistId = "spotify:playlist" + context.getUri().substring(context.getUri().lastIndexOf(':')); + + if (playlists != null) { + final Optional optionalPlaylist = playlists.stream() + .filter(pl -> playlistId.equals(pl.getUri())).findFirst(); + + playlistName = optionalPlaylist.isPresent() ? optionalPlaylist.get().getName() : ""; + } + } else { + playlistId = ""; + } + updateChannelState(CHANNEL_PLAYLISTS, valueOrEmpty(playlistId)); + updateChannelState(CHANNEL_PLAYLISTNAME, valueOrEmpty(playlistName)); + } + /** * @param value Integer value to return as {@link DecimalType} * @return value as {@link DecimalType} or ZERO if the value is null @@ -537,10 +570,13 @@ private synchronized void incrementProgress() { */ private void setProgress(long progress) { this.progress = progress; + final String formattedProgress; + synchronized (MUSIC_TIME_FORMAT) { - updateChannelState(CHANNEL_PLAYED_TRACKPROGRESS_MS, new DecimalType(progress)); - updateChannelState(CHANNEL_PLAYED_TRACKPROGRESS_FMT, MUSIC_TIME_FORMAT.format(new Date(progress))); + formattedProgress = MUSIC_TIME_FORMAT.format(new Date(progress)); } + updateChannelState(CHANNEL_PLAYED_TRACKPROGRESS_MS, new DecimalType(progress)); + updateChannelState(CHANNEL_PLAYED_TRACKPROGRESS_FMT, formattedProgress); } /** diff --git a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyDeviceHandler.java b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyDeviceHandler.java index bee088e19703c..c02007e78e7f5 100644 --- a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyDeviceHandler.java +++ b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyDeviceHandler.java @@ -15,7 +15,6 @@ import static org.openhab.binding.spotify.internal.SpotifyBindingConstants.*; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.PercentType; import org.eclipse.smarthome.core.library.types.PlayPauseType; @@ -48,7 +47,8 @@ public class SpotifyDeviceHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(SpotifyDeviceHandler.class); private @NonNullByDefault({}) SpotifyHandleCommands commandHandler; private @NonNullByDefault({}) SpotifyApi spotifyApi; - private @NonNullByDefault({}) String deviceId; + private @NonNullByDefault({}) String deviceName; + private String deviceId = ""; private boolean active; @@ -64,7 +64,7 @@ public SpotifyDeviceHandler(Thing thing) { @Override public void handleCommand(ChannelUID channelUID, Command command) { try { - if (commandHandler != null) { + if (commandHandler != null && !deviceId.isEmpty()) { commandHandler.handleCommand(channelUID, command, active, deviceId); } } catch (SpotifyException e) { @@ -77,10 +77,14 @@ public void initialize() { final SpotifyBridgeHandler bridgeHandler = (SpotifyBridgeHandler) getBridge().getHandler(); spotifyApi = bridgeHandler.getSpotifyApi(); - final Configuration config = thing.getConfiguration(); - deviceId = (String) config.get(PROPERTY_SPOTIFY_DEVICE_ID); - commandHandler = new SpotifyHandleCommands(spotifyApi); - updateStatus(ThingStatus.UNKNOWN); + deviceName = (String) getConfig().get(PROPERTY_SPOTIFY_DEVICE_NAME); + if (deviceName == null || deviceName.isEmpty()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "The deviceName property is not set or empty. If you have an older thing please recreate this thing."); + } else { + commandHandler = new SpotifyHandleCommands(spotifyApi); + updateStatus(ThingStatus.UNKNOWN); + } } @Override @@ -100,10 +104,12 @@ public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { * @return returns true if given device matches with this handler */ public boolean updateDeviceStatus(Device device, boolean playing) { - if (deviceId.equals(device.getId())) { - logger.debug("Updating status of Thing: {} Device [ {} {}, {} ]", thing.getUID(), device.getId(), + if (deviceName.equals(device.getName())) { + deviceId = device.getId() == null ? "" : device.getId(); + logger.debug("Updating status of Thing: {} Device [ {} {}, {} ]", thing.getUID(), deviceId, device.getName(), device.getType()); boolean online = setOnlineStatus(device.isRestricted()); + updateChannelState(CHANNEL_DEVICEID, new StringType(deviceId)); updateChannelState(CHANNEL_DEVICENAME, new StringType(device.getName())); updateChannelState(CHANNEL_DEVICETYPE, new StringType(device.getType())); updateChannelState(CHANNEL_DEVICEVOLUME, @@ -122,12 +128,15 @@ public boolean updateDeviceStatus(Device device, boolean playing) { * Updates the device as showing status is gone and reset all device status to default. */ public void setStatusGone() { - logger.debug("Device is gone: {}", thing.getUID()); - getThing().setStatusInfo( - new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.GONE, "Device not available on Spotify")); - updateChannelState(CHANNEL_DEVICERESTRICTED, OnOffType.ON); - updateChannelState(CHANNEL_DEVICEACTIVE, OnOffType.OFF); - updateChannelState(CHANNEL_DEVICEPLAYER, PlayPauseType.PAUSE); + if (getThing().getStatus() != ThingStatus.OFFLINE + && getThing().getStatusInfo().getStatusDetail() != ThingStatusDetail.GONE) { + logger.debug("Device is gone: {}", thing.getUID()); + getThing().setStatusInfo(new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.GONE, + "Device not available on Spotify")); + updateChannelState(CHANNEL_DEVICERESTRICTED, OnOffType.ON); + updateChannelState(CHANNEL_DEVICEACTIVE, OnOffType.OFF); + updateChannelState(CHANNEL_DEVICEPLAYER, PlayPauseType.PAUSE); + } } /** diff --git a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyDynamicStateDescriptionProvider.java b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyDynamicStateDescriptionProvider.java index 712d2aa13aee5..ec1c3a173ab87 100644 --- a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyDynamicStateDescriptionProvider.java +++ b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyDynamicStateDescriptionProvider.java @@ -72,11 +72,9 @@ public void setPlayList(List spotifyPlaylists) { @Nullable StateDescription originalStateDescription, @Nullable Locale locale) { final String channelId = channel.getUID().getId(); - if (channel.getChannelTypeUID() != null - && CHANNEL_TYPE_ACTIVE_DEVICENAME.equals(channel.getChannelTypeUID().getId()) - && CHANNEL_DEVICENAME.equals(channelId)) { + if (CHANNEL_DEVICES.equals(channelId)) { return devicesStateDescription; - } else if (CHANNEL_PLAYLIST.equals(channelId)) { + } else if (CHANNEL_PLAYLISTS.equals(channelId)) { return playlistStateDescription; } else { return originalStateDescription; diff --git a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyHandleCommands.java b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyHandleCommands.java index 535c9192e47e4..5ec47e659b18f 100644 --- a/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyHandleCommands.java +++ b/bundles/org.openhab.binding.spotify/src/main/java/org/openhab/binding/spotify/internal/handler/SpotifyHandleCommands.java @@ -14,6 +14,9 @@ import static org.openhab.binding.spotify.internal.SpotifyBindingConstants.*; +import java.util.Collections; +import java.util.List; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.NextPreviousType; @@ -24,6 +27,8 @@ import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.Command; import org.openhab.binding.spotify.internal.api.SpotifyApi; +import org.openhab.binding.spotify.internal.api.model.Device; +import org.openhab.binding.spotify.internal.api.model.Playlist; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,7 +43,10 @@ class SpotifyHandleCommands { private final Logger logger = LoggerFactory.getLogger(SpotifyDeviceHandler.class); - private SpotifyApi spotifyApi; + private final SpotifyApi spotifyApi; + + private List devices = Collections.emptyList(); + private List playlists = Collections.emptyList(); /** * Constructor. For the bridge the deviceId is empty. @@ -49,6 +57,11 @@ public SpotifyHandleCommands(SpotifyApi spotifyApi) { this.spotifyApi = spotifyApi; } + public void setLists(List devices, List playlists) { + this.devices = devices; + this.playlists = playlists; + } + /** * Handles commands from the given Channel and calls Spotify Web Api with the given command. * @@ -66,13 +79,17 @@ public boolean handleCommand(ChannelUID channelUID, Command command, boolean act switch (channel) { case CHANNEL_DEVICENAME: if (command instanceof StringType) { - final String newDeviceId = command.toString(); + final String newName = command.toString(); - if (deviceId.equals(newDeviceId) && active) { - spotifyApi.play(newDeviceId); - } else { - spotifyApi.transferPlay(newDeviceId, true); - } + devices.stream().filter(d -> d.getName().equals(newName)).findFirst() + .ifPresent(d -> playDeviceId(d.getId(), active, deviceId)); + commandRun = true; + } + break; + case CHANNEL_DEVICEID: + case CHANNEL_DEVICES: + if (command instanceof StringType) { + playDeviceId(command.toString(), active, deviceId); commandRun = true; } break; @@ -105,16 +122,33 @@ public boolean handleCommand(ChannelUID channelUID, Command command, boolean act } break; case CHANNEL_TRACKPLAY: - case CHANNEL_PLAYLIST: + case CHANNEL_PLAYLISTS: if (command instanceof StringType) { spotifyApi.playTrack(deviceId, command.toString()); commandRun = true; } break; + case CHANNEL_PLAYLISTNAME: + if (command instanceof StringType) { + final String newName = command.toString(); + + playlists.stream().filter(pl -> pl.getName().equals(newName)).findFirst() + .ifPresent(pl -> spotifyApi.playTrack(deviceId, pl.getUri())); + commandRun = true; + } + break; } return commandRun; } + private void playDeviceId(String newDeviceId, boolean active, String currentDeviceId) { + if (currentDeviceId.equals(newDeviceId) && active) { + spotifyApi.play(newDeviceId); + } else { + spotifyApi.transferPlay(newDeviceId, true); + } + } + /** * Helper method to handle device play status. * diff --git a/bundles/org.openhab.binding.spotify/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.spotify/src/main/resources/ESH-INF/thing/thing-types.xml index 735acef22132c..b2a632e985fc5 100644 --- a/bundles/org.openhab.binding.spotify/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.spotify/src/main/resources/ESH-INF/thing/thing-types.xml @@ -18,11 +18,13 @@ register a bridge for each account. Go to http://your openHAB address::8080/conn + - + + @@ -96,6 +98,7 @@ bridge you have configured. + @@ -105,13 +108,13 @@ bridge you have configured. - id + deviceName - + true - - This is the device ID provided by Spotify. + + This is the device name provided by Spotify. @@ -126,13 +129,16 @@ bridge you have configured. String The Spotify ID of active device - String The name of the currently active device - + + + String + + List of active devices. String @@ -146,13 +152,19 @@ bridge you have configured. If shuffle is on or off on the active device - + String - + List of the users playlists + + String + + Name of the active playlist + + String @@ -299,6 +311,11 @@ bridge you have configured. + + String + + The Spotify ID of the device + String diff --git a/bundles/org.openhab.binding.squeezebox/src/main/java/org/openhab/binding/squeezebox/internal/handler/SqueezeBoxServerHandler.java b/bundles/org.openhab.binding.squeezebox/src/main/java/org/openhab/binding/squeezebox/internal/handler/SqueezeBoxServerHandler.java index 44355f8551814..c9651cbfef5bc 100644 --- a/bundles/org.openhab.binding.squeezebox/src/main/java/org/openhab/binding/squeezebox/internal/handler/SqueezeBoxServerHandler.java +++ b/bundles/org.openhab.binding.squeezebox/src/main/java/org/openhab/binding/squeezebox/internal/handler/SqueezeBoxServerHandler.java @@ -20,6 +20,7 @@ import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; +import java.io.Writer; import java.net.Socket; import java.net.URLDecoder; import java.net.URLEncoder; @@ -325,8 +326,8 @@ private synchronized void sendCommand(String command) { } logger.debug("Sending command: {}", sanitizeCommand(command)); - try { - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())); + try (Writer osw = new OutputStreamWriter(clientSocket.getOutputStream()); + BufferedWriter writer = new BufferedWriter(osw)) { writer.write(command + NEW_LINE); writer.flush(); } catch (IOException e) { diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.synopanalyzer/src/main/resources/ESH-INF/binding/binding.xml index 2bdf0c4b64db7..cce3bbb004916 100644 --- a/bundles/org.openhab.binding.synopanalyzer/src/main/resources/ESH-INF/binding/binding.xml +++ b/bundles/org.openhab.binding.synopanalyzer/src/main/resources/ESH-INF/binding/binding.xml @@ -1,8 +1,8 @@ + 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"> Synop Analyzer Binding This is the binding to download and interpret Synop messages diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.synopanalyzer/src/main/resources/ESH-INF/thing/thing-types.xml index 4b895012ac206..d98afc1ee634e 100644 --- a/bundles/org.openhab.binding.synopanalyzer/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.synopanalyzer/src/main/resources/ESH-INF/thing/thing-types.xml @@ -1,8 +1,8 @@ + 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"> diff --git a/bundles/org.openhab.binding.systeminfo/.classpath b/bundles/org.openhab.binding.systeminfo/.classpath new file mode 100644 index 0000000000000..cd04a794bfd47 --- /dev/null +++ b/bundles/org.openhab.binding.systeminfo/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.systeminfo/.project b/bundles/org.openhab.binding.systeminfo/.project new file mode 100644 index 0000000000000..ef162e9afc293 --- /dev/null +++ b/bundles/org.openhab.binding.systeminfo/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.systeminfo + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.systeminfo/NOTICE b/bundles/org.openhab.binding.systeminfo/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.systeminfo/NOTICE rename to bundles/org.openhab.binding.systeminfo/NOTICE diff --git a/addons/binding/org.openhab.binding.systeminfo/README.md b/bundles/org.openhab.binding.systeminfo/README.md similarity index 100% rename from addons/binding/org.openhab.binding.systeminfo/README.md rename to bundles/org.openhab.binding.systeminfo/README.md diff --git a/bundles/org.openhab.binding.systeminfo/pom.xml b/bundles/org.openhab.binding.systeminfo/pom.xml new file mode 100644 index 0000000000000..2fa0c3fa8845f --- /dev/null +++ b/bundles/org.openhab.binding.systeminfo/pom.xml @@ -0,0 +1,44 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.systeminfo + + openHAB Add-ons :: Bundles :: Systeminfo Binding + + + + net.java.dev.jna + jna-platform + 5.3.0 + provided + + + net.java.dev.jna + jna + 5.3.0 + provided + + + com.github.oshi + oshi-core + 3.13.0 + provided + + + org.slf4j + slf4j-api + + + + + + diff --git a/addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/SysteminfoBindingConstants.java b/bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/SysteminfoBindingConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/SysteminfoBindingConstants.java rename to bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/SysteminfoBindingConstants.java diff --git a/addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/SysteminfoHandlerFactory.java b/bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/SysteminfoHandlerFactory.java similarity index 100% rename from addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/SysteminfoHandlerFactory.java rename to bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/SysteminfoHandlerFactory.java diff --git a/addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/discovery/SysteminfoDiscoveryService.java b/bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/discovery/SysteminfoDiscoveryService.java similarity index 100% rename from addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/discovery/SysteminfoDiscoveryService.java rename to bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/discovery/SysteminfoDiscoveryService.java diff --git a/addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/handler/SysteminfoHandler.java b/bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/handler/SysteminfoHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/handler/SysteminfoHandler.java rename to bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/handler/SysteminfoHandler.java diff --git a/addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/model/DeviceNotFoundException.java b/bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/model/DeviceNotFoundException.java similarity index 100% rename from addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/model/DeviceNotFoundException.java rename to bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/model/DeviceNotFoundException.java diff --git a/addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/model/OSHISysteminfo.java b/bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/model/OSHISysteminfo.java similarity index 100% rename from addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/model/OSHISysteminfo.java rename to bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/model/OSHISysteminfo.java diff --git a/addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/model/SysteminfoInterface.java b/bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/model/SysteminfoInterface.java similarity index 100% rename from addons/binding/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/model/SysteminfoInterface.java rename to bundles/org.openhab.binding.systeminfo/src/main/java/org/openhab/binding/systeminfo/internal/model/SysteminfoInterface.java diff --git a/bundles/org.openhab.binding.systeminfo/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.systeminfo/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..c90deb5d77b78 --- /dev/null +++ b/bundles/org.openhab.binding.systeminfo/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,9 @@ + + + + Systeminfo Binding + This binding provides information about the operating system and the hardware. + Svilen Valkanov + + diff --git a/addons/binding/org.openhab.binding.systeminfo/ESH-INF/config/channelConfig.xml b/bundles/org.openhab.binding.systeminfo/src/main/resources/ESH-INF/config/channelConfig.xml similarity index 100% rename from addons/binding/org.openhab.binding.systeminfo/ESH-INF/config/channelConfig.xml rename to bundles/org.openhab.binding.systeminfo/src/main/resources/ESH-INF/config/channelConfig.xml diff --git a/addons/binding/org.openhab.binding.systeminfo/ESH-INF/config/computerConfig.xml b/bundles/org.openhab.binding.systeminfo/src/main/resources/ESH-INF/config/computerConfig.xml similarity index 100% rename from addons/binding/org.openhab.binding.systeminfo/ESH-INF/config/computerConfig.xml rename to bundles/org.openhab.binding.systeminfo/src/main/resources/ESH-INF/config/computerConfig.xml diff --git a/addons/binding/org.openhab.binding.systeminfo/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.systeminfo/src/main/resources/ESH-INF/thing/channels.xml similarity index 100% rename from addons/binding/org.openhab.binding.systeminfo/ESH-INF/thing/channels.xml rename to bundles/org.openhab.binding.systeminfo/src/main/resources/ESH-INF/thing/channels.xml diff --git a/addons/binding/org.openhab.binding.systeminfo/ESH-INF/thing/computer.xml b/bundles/org.openhab.binding.systeminfo/src/main/resources/ESH-INF/thing/computer.xml similarity index 100% rename from addons/binding/org.openhab.binding.systeminfo/ESH-INF/thing/computer.xml rename to bundles/org.openhab.binding.systeminfo/src/main/resources/ESH-INF/thing/computer.xml diff --git a/bundles/org.openhab.binding.tado/.classpath b/bundles/org.openhab.binding.tado/.classpath index a5d95095ccaaf..2669451039905 100644 --- a/bundles/org.openhab.binding.tado/.classpath +++ b/bundles/org.openhab.binding.tado/.classpath @@ -13,9 +13,9 @@ + - @@ -28,5 +28,6 @@ + diff --git a/bundles/org.openhab.binding.tankerkoenig/README.md b/bundles/org.openhab.binding.tankerkoenig/README.md index 325534f593694..f31d1d18ce129 100644 --- a/bundles/org.openhab.binding.tankerkoenig/README.md +++ b/bundles/org.openhab.binding.tankerkoenig/README.md @@ -1,7 +1,7 @@ # Tankerkönig Binding -The binding uses the Tankerkönig API for collecting gas price data of german gas stations. -Special thanks to the creators of Tankerkönig for providing an easy way to get data from the [MTS-K] (Markttransparenzstelle für Kraftstoffe). +The binding uses the Tankerkönig API for collecting gas price data of German gas stations. +Special thanks to the creators of Tankerkönig for providing an easy way to get data from the [MTS-K] (Markttransparenzstelle für Kraftstoffe). Tankerkönig is providing this service for free, however they request to prevent overloading of their server by reducing the number of web-requests. This binding handles those requests (minimum Refresh Interval is 10 minutes, a webserver does handle a maximum of 10 stations). @@ -16,18 +16,19 @@ While using the mode Opening-Times the channel "station_open" will NOT show "clo In order to use this binding one needs to prepare: --minimal Java Varsion is 1.8.0_101-b13 (otherwise the https request will not produce a usable return) +- minimal Java Version is 1.8.0_101-b13 (otherwise the https request will not produce a usable return) --a personal API-Key +- a personal API-Key -Request a free Tankerkönig API key from: Select the tab "API-Key". +Request a free Tankerkönig API key from: (Select the tab "API-Key"). --LocationIDs of the selected gas stations +- LocationIDs of the selected gas stations -Search for the gas station IDs here: +Search for the gas station IDs via the [finder tool](https://creativecommons.tankerkoenig.de/TankstellenFinder/index.html) (Select tab "Tools" -> "Tankstellenfinder"). Drag the red marker on the map to the rough location of desired gas stations. Select the gas stations and click "Tankstellen übernehmen" on the right. -This will download a file holding the location IDs. For example: a7cdd9cf-b467-4aac-8eab-d662f082511e +This will download a file holding the location IDs. +For example: `a7cdd9cf-b467-4aac-8eab-d662f082511e` ## Supported Things @@ -55,7 +56,7 @@ Each Station needs to be configured with a LocationID and the Webservice to whic ## Channels -The binding introduces the channel holiday for the Webservice and the channels e10, e5 ,diesel and station_open for the Stations: +The binding introduces the channel `holiday` for the Webservice and the channels `e10`, `e5`, `diesel` and `station_open` for the Stations: | Channel ID | Channel Description | Supported item type | Advanced | |--------------|---------------------------------------|---------------------|----------| @@ -160,10 +161,9 @@ The required password is "changeit". Restart your server - -The Station(s) and Webservice go to OFFLINE after being ONLINE -Either the web-request to Tankerköng returned a failure or no valid response was received (this could be caused by a banned API-key). +Either the web-request to Tankerkönig returned a failure or no valid response was received (this could be caused by a banned API-key). In both cases the Webservice and the Station(s) go OFFLINE. If the Tankerkönig return indicates an error a descriptive message (in German) is added next to the OFFLINE which will be displayed on the Webservice and Station(s) pages on PaperUI. On the next receipt of a valid message Webservice and Station(s) will go ONLINE again. diff --git a/bundles/org.openhab.binding.tankerkoenig/src/main/resources/ESH-INF/i18n/tankerkoenig_de.properties b/bundles/org.openhab.binding.tankerkoenig/src/main/resources/ESH-INF/i18n/tankerkoenig_de.properties index 0170b4cd02886..702b7a0123928 100644 --- a/bundles/org.openhab.binding.tankerkoenig/src/main/resources/ESH-INF/i18n/tankerkoenig_de.properties +++ b/bundles/org.openhab.binding.tankerkoenig/src/main/resources/ESH-INF/i18n/tankerkoenig_de.properties @@ -6,7 +6,7 @@ binding.tankerkoenig.description = Das Tankerk thing-type.tankerkoenig.webservice.label = Tankerkönig Webservice thing-type.tankerkoenig.webservice.description = Der Tankerkönig Werbservice ermöglicht es die Spritpreise von 1 bis 10 Tankstellen abzufragen. thing-type.config.tankerkoenig.webservice.apikey.label=API-Key -thing-type.config.tankerkoenig.webservice.apikey.description=Tankerkönig API-Key. Schlüssel erhältlich unter https://creativecommons.tankerkoenig.de +thing-type.config.tankerkoenig.webservice.apikey.description=Tankerkönig API-Key. Der Schlüssel ist auf der Tankerkönig Webseite erhältlich. thing-type.config.tankerkoenig.webservice.refresh.label=Aktualisierungsintervall thing-type.config.tankerkoenig.webservice.refresh.description=Spezifiziert das Aktualisierungsintervall in Minuten. Minimum 10 Minuten. thing-type.config.tankerkoenig.webservice.modeOpeningTime.label=Öffnungszeiten @@ -14,7 +14,7 @@ thing-type.config.tankerkoenig.webservice.modeOpeningTime.description=Im Mode thing-type.tankerkoenig.station.label=Tankstelle thing-type.tankerkoenig.station.description=Stellt die Informationen zu den E5-, E10-, und Diesel-Preisen einer Tankstelle bereit. thing-type.config.tankerkoenig.station.locationid.label=Tankstellen-ID -thing-type.config.tankerkoenig.station.locationid.description=Tankstellen-ID. Die ID ist erhältlich unter: https://creativecommons.tankerkoenig.de/configurator/index.html +thing-type.config.tankerkoenig.station.locationid.description=Tankstellen-ID. Die ID ist auf der Tankerkönig Webseite erhältlich. # channel types channel-type.tankerkoenig.diesel.label = Diesel channel-type.tankerkoenig.e5.label = E5 @@ -26,4 +26,4 @@ channel-type.tankerkoenig.diesel.description = Diesel-Preis channel-type.tankerkoenig.e5.description= E5-Preis channel-type.tankerkoenig.e10.description = E10-Preis channel-type.tankerkoenig.station_open.description = Gemeldeter Öffnungs-Status -channel-type.tankerkoenig.holiday.description = ON wenn Heute ein Feiertag ist +channel-type.tankerkoenig.holiday.description = ON, wenn Heute ein Feiertag ist diff --git a/bundles/org.openhab.binding.tankerkoenig/src/main/resources/ESH-INF/thing/station.xml b/bundles/org.openhab.binding.tankerkoenig/src/main/resources/ESH-INF/thing/station.xml index b1699141d3d73..ab2763cfef740 100644 --- a/bundles/org.openhab.binding.tankerkoenig/src/main/resources/ESH-INF/thing/station.xml +++ b/bundles/org.openhab.binding.tankerkoenig/src/main/resources/ESH-INF/thing/station.xml @@ -1,49 +1,49 @@ - - - - - - Provides the prices of gas types E5-, E10- and Diesel of that station and if that station reports as opened. - - - - - - - - - - Fuel-Station-ID. You can get the required ID for your Gas-Station via this Webpage: https://creativecommons.tankerkoenig.de/configurator/index.html - - - - - Number - - price for diesel - - - - Number - - price for E10 - - - - Number - - price for E5 - - - - Contact - - The reported opening-state of that Station - - + 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"> + + + + + + Provides the prices of gas types E5-, E10- and Diesel of that station and if that station reports as opened. + + + + + + + + + + Fuel-Station-ID. You can get the required ID for your Gas-Station via the Tankerkönig website. + + + + + Number + + price for diesel + + + + Number + + price for E10 + + + + Number + + price for E5 + + + + Contact + + The reported opening-state of that Station. + + diff --git a/bundles/org.openhab.binding.tankerkoenig/src/main/resources/ESH-INF/thing/webservice.xml b/bundles/org.openhab.binding.tankerkoenig/src/main/resources/ESH-INF/thing/webservice.xml index 861d154a58b46..12d93dd3e967a 100644 --- a/bundles/org.openhab.binding.tankerkoenig/src/main/resources/ESH-INF/thing/webservice.xml +++ b/bundles/org.openhab.binding.tankerkoenig/src/main/resources/ESH-INF/thing/webservice.xml @@ -1,6 +1,8 @@ - + The Tankerkönig Webservice can handle 1 to 10 gas stations. @@ -10,7 +12,7 @@ - API-Key. Necessary registration under https://creativecommons.tankerkoenig.de + API-Key. Necessary registration on the Tankerkönig website. @@ -28,6 +30,6 @@ Switch - ON if today is a holiday + ON if today is a holiday. diff --git a/bundles/org.openhab.binding.valloxmv/build.properties b/bundles/org.openhab.binding.valloxmv/build.properties deleted file mode 100644 index a6cfff567c9b1..0000000000000 --- a/bundles/org.openhab.binding.valloxmv/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -source..=src/main/java/ -output..=target/classes -bin.includes=META-INF/,\ - .,\ - OSGI-INF/,\ - ESH-INF/,\ - NOTICE diff --git a/bundles/org.openhab.binding.vitotronic/src/main/java/org/openhab/binding/vitotronic/internal/handler/VitotronicThingHandler.java b/bundles/org.openhab.binding.vitotronic/src/main/java/org/openhab/binding/vitotronic/internal/handler/VitotronicThingHandler.java index d9e43806d8110..b7af6bcc96671 100644 --- a/bundles/org.openhab.binding.vitotronic/src/main/java/org/openhab/binding/vitotronic/internal/handler/VitotronicThingHandler.java +++ b/bundles/org.openhab.binding.vitotronic/src/main/java/org/openhab/binding/vitotronic/internal/handler/VitotronicThingHandler.java @@ -71,7 +71,7 @@ public void updateStatus(ThingStatus status) { private synchronized VitotronicBridgeHandler getBridgeHandler() { Bridge bridge = getBridge(); if (bridge == null) { - logger.debug("Required bridge not defined for device {}."); + logger.debug("Required bridge not defined for device {}.", getThing().getUID()); return null; } else { return getBridgeHandler(bridge); diff --git a/bundles/org.openhab.binding.wifiled/src/main/java/org/openhab/binding/wifiled/internal/handler/AbstractWiFiLEDDriver.java b/bundles/org.openhab.binding.wifiled/src/main/java/org/openhab/binding/wifiled/internal/handler/AbstractWiFiLEDDriver.java index fb919c1577bb6..9813bd0f3abbb 100644 --- a/bundles/org.openhab.binding.wifiled/src/main/java/org/openhab/binding/wifiled/internal/handler/AbstractWiFiLEDDriver.java +++ b/bundles/org.openhab.binding.wifiled/src/main/java/org/openhab/binding/wifiled/internal/handler/AbstractWiFiLEDDriver.java @@ -111,14 +111,13 @@ public void init() throws IOException { public abstract LEDStateDTO getLEDStateDTO() throws IOException; protected synchronized LEDState getLEDState() throws IOException { - try (Socket socket = new Socket(host, port)) { + try (Socket socket = new Socket(host, port); + DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream()); + DataInputStream inputStream = new DataInputStream(socket.getInputStream())) { logger.debug("Connected to '{}'", socket); socket.setSoTimeout(DEFAULT_SOCKET_TIMEOUT); - DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream()); - DataInputStream inputStream = new DataInputStream(socket.getInputStream()); - byte[] data = { (byte) 0x81, (byte) 0x8A, (byte) 0x8B, (byte) 0x96 }; outputStream.write(data); logger.debug("Data sent: '{}'", bytesToHex(data)); @@ -135,8 +134,8 @@ protected synchronized LEDState getLEDState() throws IOException { int state = statusBytes[2] & 0xFF; // On/Off int program = statusBytes[3] & 0xFF; int programSpeed = statusBytes[5] & 0xFF; - - // On factory default the controller can be configured + + // On factory default the controller can be configured // with a value of 255 but max should be 31. if (programSpeed > 31) { programSpeed = 31; @@ -151,8 +150,6 @@ protected synchronized LEDState getLEDState() throws IOException { logger.debug("RGBW: {},{},{},{}, {}", red, green, blue, white, white2); return new LEDState(state, program, programSpeed, red, green, blue, white, white2); - } catch (Exception e) { - throw new IOException(e); } } @@ -161,20 +158,19 @@ protected void sendRaw(byte[] data) throws IOException { } protected synchronized void sendRaw(byte[] data, int delay) throws IOException { - try (Socket socket = new Socket(host, port)) { + try (Socket socket = new Socket(host, port); + DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream())) { logger.debug("Connected to '{}'", socket); socket.setSoTimeout(DEFAULT_SOCKET_TIMEOUT); - DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream()); - sendRaw(data, outputStream); if (delay > 0) { Thread.sleep(delay); } - } catch (Exception e) { - throw new IOException(e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } } diff --git a/bundles/org.openhab.binding.xmltv/.classpath b/bundles/org.openhab.binding.xmltv/.classpath new file mode 100644 index 0000000000000..cd04a794bfd47 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.xmltv/.project b/bundles/org.openhab.binding.xmltv/.project new file mode 100644 index 0000000000000..9fb984ac47b60 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.xmltv + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.loxone/NOTICE b/bundles/org.openhab.binding.xmltv/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.loxone/NOTICE rename to bundles/org.openhab.binding.xmltv/NOTICE diff --git a/bundles/org.openhab.binding.xmltv/README.md b/bundles/org.openhab.binding.xmltv/README.md new file mode 100644 index 0000000000000..ddbfd8b5dc64d --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/README.md @@ -0,0 +1,87 @@ +# XmlTV Binding + +XMLTV is an XML based file format for describing TV listings. +This format is often used by Home Theater software to produce their Electronic Program Guide (http://wiki.xmltv.org/index.php/Main_Page) + +The norm allows to separate program display from its building. +The building of the XMLTV file itself is taken in charge by so called "grabbers" (http://wiki.xmltv.org/index.php/HowtoUseGrabbers). + +Some websites provides updated XMLTV files than can be directly downloaded. + +Here is a sample for France : https://www.xmltv.fr/ + +This binding takes an XMLTV file as input and creates a thing for each channel contained in it. +XmlTV channels are called Media Channels in this binding in order to avoid messing with ESH Channels. + +For each thing, you'll be able to get information regarding the current program and the next to come. + +## Supported Things + +## Discovery + +Once the XmlTV bridge to a file is created, you can add all known channels by searching new things. + +## Binding Configuration + +| Configuration Parameter | Required | Description | Default | +|-------------------------|----------|-----------------------------------------------------|---------| +| filePath | X | Full path (including filename) to an Xml TV file | | +| refresh | X | XMLTV file reload interval in hours | 24h | + +## Thing Configuration + +| Configuration Parameter | Required | Description | Default | +|-------------------------|----------|----------------------------------------------------------------|---------| +| channelId | X | Id of the channel as presented in the XmlTV file | | +| offset | X | Offset applied to program times (forward or backward (minutes) | 0 | +| refresh | X | Refresh interval in seconds | 60 | + +## Channels + +| Channel Type ID | Item Type | Description | +|-----------------|----------------------|-------------------------------------| +| iconUrl | String | Channel Icon URL | +| icon | Image | Icon of the channel | + +### Current program (currentprog) Channels Group + +| Channel Type ID | Item Type | Description | +|-----------------|----------------------|---------------------------------------------| +| progStart | DateTime | Program Start Time | +| progEnd | DateTime | Program End Time | +| progTitle | String | Program Title | +| progCategory | String | Program Category | +| progIconUrl | String | URL to an image of the program | +| icon | Image | Icon of the program | +| elapsedTime | Number:Time | Current time of currently playing program | +| remainingTime | Number:Time | Time remaining until end of the program | +| progress | Number:Dimensionless | Relative progression of the current program | + +### Next program (nextprog) Channels Group + +| Channel Type ID | Item Type | Description | +|-----------------|----------------------|---------------------------------------------| +| progStart | DateTime | Program Start Time | +| timeLeft | Number:Time | Time left before program start | +| progEnd | DateTime | Program End Time | +| progTitle | String | Program Title | +| progCategory | String | Program Category | +| progIconUrl | String | URL to an image of the program | +| icon | Image | Icon of the program | + +## Full Example + +### xmltv.things + +``` +Bridge xmltv:xmltvfile:france "XmlTV" @ "TV" [filePath="/etc/openhab2/scripts/tvguide.xml"] +{ + Thing channel france2 "France 2" @ "TV" [channelId="C4.api.telerama.fr", offset=0, refresh=60] +} +``` + +### xmltv.items + +``` +String france2_title "Titre" {channel="xmltv:channel:france:france2:currentprog#progTitle"} +``` diff --git a/bundles/org.openhab.binding.xmltv/pom.xml b/bundles/org.openhab.binding.xmltv/pom.xml new file mode 100644 index 0000000000000..e7f7a4e4ca794 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.xmltv + + openHAB Add-ons :: Bundles :: XMLTV Binding + + + + javax.xml.bind + jaxb-api + 2.2.12 + provided + + + com.sun.xml.bind + jaxb-impl + 2.2.11 + provided + + + com.sun.xml.bind + jaxb-core + 2.2.11 + provided + + + javax.activation + activation + 1.1.1 + provided + + + + diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVBindingConstants.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVBindingConstants.java new file mode 100644 index 0000000000000..e080c0eef29eb --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVBindingConstants.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmltv.internal; + +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link XmlTVBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Gaël L'hopital - Initial contribution + */ +@NonNullByDefault +public class XmlTVBindingConstants { + + private static final String BINDING_ID = "xmltv"; + + // List of all Bridge Type UIDs + public static final ThingTypeUID XMLTV_FILE_BRIDGE_TYPE = new ThingTypeUID(BINDING_ID, "xmltvfile"); + public static final ThingTypeUID XMLTV_CHANNEL_THING_TYPE = new ThingTypeUID(BINDING_ID, "channel"); + + // Channel groups + public static final String GROUP_CURRENT_PROGRAMME = "currentprog"; + public static final String GROUP_NEXT_PROGRAMME = "nextprog"; + public static final String GROUP_CHANNEL_PROPERTIES = "channelprops"; + + // List of all Channel ids + public static final String CHANNEL_CHANNEL_URL = "iconUrl"; + public static final String CHANNEL_ICON = "icon"; + + public static final String CHANNEL_PROGRAMME_START = "progStart"; + public static final String CHANNEL_PROGRAMME_END = "progEnd"; + public static final String CHANNEL_PROGRAMME_TITLE = "progTitle"; + public static final String CHANNEL_PROGRAMME_CATEGORY = "progCategory"; + public static final String CHANNEL_PROGRAMME_ICON = "progIconUrl"; + + public static final String CHANNEL_PROGRAMME_ELAPSED = "elapsedTime"; + public static final String CHANNEL_PROGRAMME_REMAINING = "remainingTime"; + public static final String CHANNEL_PROGRAMME_PROGRESS = "progress"; + public static final String CHANNEL_PROGRAMME_TIMELEFT = "timeLeft"; + + // Supported Thing types + public static final Set SUPPORTED_THING_TYPES_UIDS = Collections + .unmodifiableSet(Stream.of(XMLTV_FILE_BRIDGE_TYPE, XMLTV_CHANNEL_THING_TYPE).collect(Collectors.toSet())); +} diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVHandlerFactory.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVHandlerFactory.java new file mode 100644 index 0000000000000..43b51d70655e7 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVHandlerFactory.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmltv.internal; + +import static org.openhab.binding.xmltv.internal.XmlTVBindingConstants.*; + +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import javax.xml.bind.JAXBException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.openhab.binding.xmltv.internal.discovery.XmlTVDiscoveryService; +import org.openhab.binding.xmltv.internal.handler.ChannelHandler; +import org.openhab.binding.xmltv.internal.handler.XmlTVHandler; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link XmlTVHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Gaël L'hopital - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.xmltv", service = ThingHandlerFactory.class) +public class XmlTVHandlerFactory extends BaseThingHandlerFactory { + private final Logger logger = LoggerFactory.getLogger(XmlTVHandlerFactory.class); + private final Map> discoveryServiceRegs = new HashMap<>(); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + public @Nullable Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, + @Nullable ThingUID thingUID, @Nullable ThingUID bridgeUID) { + if (XMLTV_FILE_BRIDGE_TYPE.equals(thingTypeUID)) { + return super.createThing(thingTypeUID, configuration, thingUID, null); + } else if (XMLTV_CHANNEL_THING_TYPE.equals(thingTypeUID)) { + ThingUID newThingUID; + if (bridgeUID != null && thingUID != null) { + newThingUID = new ThingUID(thingTypeUID, bridgeUID, thingUID.getId()); + } else { + newThingUID = thingUID; + } + return super.createThing(thingTypeUID, configuration, newThingUID, bridgeUID); + } + throw new IllegalArgumentException( + "The thing type " + thingTypeUID + " is not supported by the XmlTV binding."); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (XMLTV_FILE_BRIDGE_TYPE.equals(thingTypeUID)) { + try { + XmlTVHandler bridgeHandler = new XmlTVHandler((Bridge) thing); + registerDeviceDiscoveryService(bridgeHandler); + return bridgeHandler; + } catch (JAXBException e) { + logger.error("Unable to create XmlTVHandler : {}", e.getMessage()); + } + } else if (XMLTV_CHANNEL_THING_TYPE.equals(thingTypeUID)) { + return new ChannelHandler(thing); + } + + return null; + } + + @Override + protected void removeHandler(ThingHandler thingHandler) { + if (thingHandler instanceof XmlTVHandler) { + Thing thing = thingHandler.getThing(); + unregisterDeviceDiscoveryService(thing); + } + super.removeHandler(thingHandler); + } + + private synchronized void registerDeviceDiscoveryService(XmlTVHandler bridgeHandler) { + XmlTVDiscoveryService discoveryService = new XmlTVDiscoveryService(bridgeHandler); + discoveryServiceRegs.put(bridgeHandler.getThing().getUID(), + bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>())); + } + + private synchronized void unregisterDeviceDiscoveryService(Thing thing) { + ServiceRegistration serviceReg = discoveryServiceRegs.remove(thing.getUID()); + serviceReg.unregister(); + } +} diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlChannelConfiguration.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlChannelConfiguration.java new file mode 100644 index 0000000000000..5b703eec565ee --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlChannelConfiguration.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmltv.internal.configuration; + +/** + * The {@link XmlChannelConfiguration} class contains fields mapping + * Channel thing configuration parameters. + * + * @author Gaël L'hopital - Initial contribution + */ +public class XmlChannelConfiguration { + public static final String CHANNEL_ID = "channelId"; + + public String channelId; + public Integer offset; + public Integer refresh; +} diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlTVConfiguration.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlTVConfiguration.java new file mode 100644 index 0000000000000..84252c968b9d2 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlTVConfiguration.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmltv.internal.configuration; + +/** + * The {@link XmlTVConfiguration} class contains fields mapping TV bridge + * configuration parameters. + * + * @author Gaël L'hopital - Initial contribution + */ +public class XmlTVConfiguration { + public String filePath; + public Integer refresh; +} diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/discovery/XmlTVDiscoveryService.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/discovery/XmlTVDiscoveryService.java new file mode 100644 index 0000000000000..a156f036b8af5 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/discovery/XmlTVDiscoveryService.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmltv.internal.discovery; + +import static org.openhab.binding.xmltv.internal.XmlTVBindingConstants.XMLTV_CHANNEL_THING_TYPE; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.openhab.binding.xmltv.internal.XmlTVBindingConstants; +import org.openhab.binding.xmltv.internal.configuration.XmlChannelConfiguration; +import org.openhab.binding.xmltv.internal.handler.XmlTVHandler; +import org.openhab.binding.xmltv.internal.jaxb.Tv; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link XmlTVDiscoveryService} is responsible for discovering all channels + * declared in the XmlTV file + * + * @author Gaël L'hopital - Initial contribution + */ +@NonNullByDefault +public class XmlTVDiscoveryService extends AbstractDiscoveryService { + private final Logger logger = LoggerFactory.getLogger(XmlTVDiscoveryService.class); + + private static final int SEARCH_TIME = 10; + + private XmlTVHandler bridgeHandler; + + /** + * Creates a XmlTVDiscoveryService with background discovery disabled. + */ + public XmlTVDiscoveryService(XmlTVHandler bridgeHandler) { + super(XmlTVBindingConstants.SUPPORTED_THING_TYPES_UIDS, SEARCH_TIME); + this.bridgeHandler = bridgeHandler; + } + + @Override + protected void startScan() { + logger.debug("Starting XmlTV discovery scan"); + if (bridgeHandler.getThing().getStatus() == ThingStatus.ONLINE) { + Tv tv = bridgeHandler.getXmlFile(); + if (tv != null) { + tv.getMediaChannels().stream().forEach(channel -> { + String channelId = channel.getId(); + String uid = channelId.replaceAll("[^A-Za-z0-9_]", "_"); + ThingUID thingUID = new ThingUID(XMLTV_CHANNEL_THING_TYPE, bridgeHandler.getThing().getUID(), uid); + + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) + .withBridge(bridgeHandler.getThing().getUID()) + .withLabel(channel.getDisplayNames().get(0).getValue()).withRepresentationProperty(uid) + .withProperty(XmlChannelConfiguration.CHANNEL_ID, channelId).build(); + + thingDiscovered(discoveryResult); + }); + } + } + } + +} diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/ChannelHandler.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/ChannelHandler.java new file mode 100644 index 0000000000000..2c556597ba681 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/ChannelHandler.java @@ -0,0 +1,250 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmltv.internal.handler; + +import static org.openhab.binding.xmltv.internal.XmlTVBindingConstants.*; + +import java.time.Duration; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.DateTimeType; +import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.library.types.RawType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.RefreshType; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.eclipse.smarthome.io.net.http.HttpUtil; +import org.openhab.binding.xmltv.internal.configuration.XmlChannelConfiguration; +import org.openhab.binding.xmltv.internal.jaxb.Icon; +import org.openhab.binding.xmltv.internal.jaxb.MediaChannel; +import org.openhab.binding.xmltv.internal.jaxb.Programme; +import org.openhab.binding.xmltv.internal.jaxb.Tv; +import org.openhab.binding.xmltv.internal.jaxb.WithLangType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link ChannelHandler} is responsible for handling information + * made available in regard of the channel and current program + * + * @author Gaël L'hopital - Initial contribution + */ +@NonNullByDefault +public class ChannelHandler extends BaseThingHandler { + private final Logger logger = LoggerFactory.getLogger(XmlTVHandler.class); + + private @NonNullByDefault({}) ScheduledFuture globalJob; + private @Nullable MediaChannel mediaChannel; + private @Nullable RawType mediaIcon = new RawType(new byte[0], RawType.DEFAULT_MIME_TYPE); + + public final List programmes = new ArrayList<>(); + + public ChannelHandler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + XmlChannelConfiguration config = getConfigAs(XmlChannelConfiguration.class); + + logger.debug("Initializing Broadcast Channel handler for uid '{}'", getThing().getUID()); + + if (globalJob == null || globalJob.isCancelled()) { + globalJob = scheduler.scheduleWithFixedDelay(() -> { + if (programmes.size() < 2) { + refreshProgramList(); + } + if (programmes.size() == 0) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, + "No programmes to come in the current XML file for this channel"); + } else if (Instant.now().isAfter(programmes.get(0).getProgrammeStop())) { + programmes.remove(0); + } + + getThing().getChannels().forEach(channel -> updateChannel(channel.getUID())); + + }, 3, config.refresh, TimeUnit.SECONDS); + } + } + + private void refreshProgramList() { + Bridge bridge = getBridge(); + if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) { + XmlTVHandler handler = (XmlTVHandler) bridge.getHandler(); + if (handler != null) { + Tv tv = handler.getXmlFile(); + if (tv != null) { + String channelId = (String) getConfig().get(XmlChannelConfiguration.CHANNEL_ID); + + if (mediaChannel == null) { + Optional channel = tv.getMediaChannels().stream() + .filter(mediaChannel -> mediaChannel.getId().equals(channelId)).findFirst(); + if (channel.isPresent()) { + mediaChannel = channel.get(); + mediaIcon = downloadIcon(mediaChannel.getIcons()); + } + } + + programmes.clear(); + tv.getProgrammes().stream().filter( + p -> p.getChannel().equals(channelId) && p.getProgrammeStop().isAfter(Instant.now())) + .forEach(p -> programmes.add(p)); + + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "No file available"); + } + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + } + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + } + } + + @Override + public void dispose() { + if (globalJob != null && !globalJob.isCancelled()) { + globalJob.cancel(true); + globalJob = null; + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("handleCommand {} for {}", command, channelUID); + if (command == RefreshType.REFRESH) { + refreshProgramList(); + } + } + + /** + * Update the channel from the last OpenUV data retrieved + * + * @param channelUID the id identifying the channel to be updated + * + */ + private void updateChannel(ChannelUID channelUID) { + String[] uidElements = channelUID.getId().split("#"); + if (uidElements.length == 2) { + int target = GROUP_NEXT_PROGRAMME.equals(uidElements[0]) ? 1 : 0; + if (programmes.size() > target) { + Programme programme = programmes.get(target); + + switch (uidElements[1]) { + case CHANNEL_ICON: + State icon = null; + if (GROUP_CHANNEL_PROPERTIES.equals(uidElements[0])) { + icon = mediaIcon; + } else { + icon = downloadIcon(programme.getIcons()); + } + updateState(channelUID, icon != null ? icon : UnDefType.UNDEF); + break; + case CHANNEL_CHANNEL_URL: + updateState(channelUID, + mediaChannel != null ? mediaChannel.getIcons().size() > 0 + ? new StringType(mediaChannel.getIcons().get(0).getSrc()) + : UnDefType.UNDEF : UnDefType.UNDEF); + break; + case CHANNEL_PROGRAMME_START: + Instant is = programme.getProgrammeStart(); + ZonedDateTime zds = ZonedDateTime.ofInstant(is, ZoneId.systemDefault()); + updateState(channelUID, new DateTimeType(zds)); + break; + case CHANNEL_PROGRAMME_END: + ZonedDateTime zde = ZonedDateTime.ofInstant(programme.getProgrammeStop(), + ZoneId.systemDefault()); + updateState(channelUID, new DateTimeType(zde)); + break; + case CHANNEL_PROGRAMME_TITLE: + List titles = programme.getTitles(); + updateState(channelUID, + titles.size() > 0 ? new StringType(programme.getTitles().get(0).getValue()) + : UnDefType.UNDEF); + break; + case CHANNEL_PROGRAMME_CATEGORY: + List categories = programme.getCategories(); + updateState(channelUID, + categories.size() > 0 ? new StringType(programme.getCategories().get(0).getValue()) + : UnDefType.UNDEF); + break; + case CHANNEL_PROGRAMME_ICON: + List icons = programme.getIcons(); + updateState(channelUID, + icons.size() > 0 ? new StringType(icons.get(0).getSrc()) : UnDefType.UNDEF); + break; + case CHANNEL_PROGRAMME_ELAPSED: + updateState(channelUID, getDurationInSeconds(programme.getProgrammeStart(), Instant.now())); + break; + case CHANNEL_PROGRAMME_REMAINING: + updateState(channelUID, getDurationInSeconds(Instant.now(), programme.getProgrammeStop())); + break; + case CHANNEL_PROGRAMME_TIMELEFT: + updateState(channelUID, getDurationInSeconds(Instant.now(), programme.getProgrammeStart())); + break; + case CHANNEL_PROGRAMME_PROGRESS: + Duration totalLength = Duration.between(programme.getProgrammeStart(), + programme.getProgrammeStop()); + Duration elapsed1 = Duration.between(programme.getProgrammeStart(), Instant.now()); + + long secondsElapsed1 = elapsed1.toMillis() / 1000; + long secondsLength = totalLength.toMillis() / 1000; + + double progress = 100.0 * secondsElapsed1 / secondsLength; + if (progress > 100 || progress < 0) { + logger.debug("Outstanding process"); + } + updateState(channelUID, new QuantityType<>(progress, SmartHomeUnits.PERCENT)); + + break; + } + } else { + logger.warn("Not enough programs in XML file, think to refresh it"); + } + } + } + + private QuantityType getDurationInSeconds(Instant from, Instant to) { + Duration elapsed = Duration.between(from, to); + long secondsElapsed = TimeUnit.MILLISECONDS.toSeconds(elapsed.toMillis()); + return new QuantityType<>(secondsElapsed, SmartHomeUnits.SECOND); + } + + private @Nullable RawType downloadIcon(List icons) { + if (icons.size() > 0) { + String url = icons.get(0).getSrc(); + return HttpUtil.downloadImage(url); + } + return null; + } + +} diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/XmlTVHandler.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/XmlTVHandler.java new file mode 100644 index 0000000000000..6c9397e9814a2 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/XmlTVHandler.java @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmltv.internal.handler; + +import java.time.Instant; +import java.util.Collections; +import java.util.Comparator; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.stream.StreamSource; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.xmltv.internal.configuration.XmlTVConfiguration; +import org.openhab.binding.xmltv.internal.jaxb.Programme; +import org.openhab.binding.xmltv.internal.jaxb.Tv; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link XmlTVHandler} is responsible for handling XMLTV file and dispatch + * information made available to according Media Channels + * + * @author Gaël L'hopital - Initial contribution + */ +@NonNullByDefault +public class XmlTVHandler extends BaseBridgeHandler { + private final Logger logger = LoggerFactory.getLogger(XmlTVHandler.class); + private final XMLInputFactory xif = XMLInputFactory.newFactory(); + private final JAXBContext jc; + + private @Nullable Tv currentXmlFile; + private @NonNullByDefault({}) ScheduledFuture reloadJob; + + public XmlTVHandler(Bridge thing) throws JAXBException { + super(thing); + xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); + jc = JAXBContext.newInstance(Tv.class); + } + + @Override + public void initialize() { + XmlTVConfiguration config = getConfigAs(XmlTVConfiguration.class); + logger.debug("Initializing {} for input file '{}'", getClass(), config.filePath); + + reloadJob = scheduler.scheduleWithFixedDelay(() -> { + final StreamSource source = new StreamSource(config.filePath); + currentXmlFile = null; + XMLStreamReader xsr = null; + try { + // This can take some seconds depending upon weight of the XmlTV source file + xsr = xif.createXMLStreamReader(source); + + try { + Unmarshaller unmarshaller = jc.createUnmarshaller(); + Tv xmlFile = (Tv) unmarshaller.unmarshal(xsr); + // Remove all finished programmes + xmlFile.getProgrammes().removeIf(programme -> Instant.now().isAfter(programme.getProgrammeStop())); + + if (xmlFile.getProgrammes().size() > 0) { + // Sort programmes by starting instant + Collections.sort(xmlFile.getProgrammes(), Comparator.comparing(Programme::getProgrammeStart)); + // Ready to deliver data to ChannelHandlers + currentXmlFile = xmlFile; + updateStatus(ThingStatus.ONLINE); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.DISABLED, "XMLTV file seems outdated"); + } + xsr.close(); + } catch (JAXBException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR, e.getMessage()); + } + } catch (XMLStreamException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); + } finally { + try { + if (xsr != null) { + xsr.close(); + } + } catch (XMLStreamException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); + } + } + }, 0, config.refresh, TimeUnit.HOURS); + } + + @Override + public void dispose() { + logger.debug("Running dispose"); + if (reloadJob != null && !reloadJob.isCancelled()) { + reloadJob.cancel(true); + reloadJob = null; + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + // nothing to do + } + + @Nullable + public Tv getXmlFile() { + return currentXmlFile; + } + +} diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/Icon.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/Icon.java new file mode 100644 index 0000000000000..2a0449abd92d7 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/Icon.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmltv.internal.jaxb; + +import java.math.BigInteger; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSchemaType; +import javax.xml.bind.annotation.XmlType; + +/** + * Java class for an icon XML element + * + * @author Gaël L'hopital - Initial contribution + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType +@XmlRootElement(name = "icon") +public class Icon { + + @XmlAttribute(required = true) + protected String src; + + @XmlAttribute + @XmlSchemaType(name = "positiveInteger") + protected BigInteger width; + + @XmlAttribute + @XmlSchemaType(name = "positiveInteger") + protected BigInteger height; + + public String getSrc() { + return src; + } + + public BigInteger getWidth() { + return width; + } + + public BigInteger getHeight() { + return height; + } +} diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/MediaChannel.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/MediaChannel.java new file mode 100644 index 0000000000000..9d4eb64bedfb8 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/MediaChannel.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmltv.internal.jaxb; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlSchemaType; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Java class for an channel XML element + * Renamed to MediaChannel in order to avoid confusion with ESH Channels + * + * @author Gaël L'hopital - Initial contribution + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType +@NonNullByDefault +public class MediaChannel { + + @XmlElement(name = "display-name", required = true) + protected List displayNames = new ArrayList<>(); + + @XmlElement(name = "icon") + protected List icons = new ArrayList<>(); + + @XmlAttribute(required = true) + @XmlJavaTypeAdapter(CollapsedStringAdapter.class) + @XmlSchemaType(name = "token") + protected String id = ""; + + public List getDisplayNames() { + return this.displayNames; + } + + public List getIcons() { + return this.icons; + } + + public String getId() { + return id; + } +} diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/ObjectFactory.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/ObjectFactory.java new file mode 100644 index 0000000000000..1b1bf7f317dab --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/ObjectFactory.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmltv.internal.jaxb; + +import javax.xml.bind.annotation.XmlRegistry; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * This object contains factory methods for each Java content + * interface and Java element interface generated in the + * org.openhab.binding.xmltv.internal.jaxb package. + * + * @author Gaël L'hopital - Initial contribution + */ +@XmlRegistry +@NonNullByDefault +public class ObjectFactory { + + /** + * Create an instance of {@link Tv } + * + */ + public Tv createTv() { + return new Tv(); + } + + /** + * Create an instance of {@link Programme } + * + */ + public Programme createProgramme() { + return new Programme(); + } + + /** + * Create an instance of {@link MediaChannel } + * + */ + public MediaChannel createChannel() { + return new MediaChannel(); + } + + /** + * Create an instance of {@link Icon } + * + */ + public Icon createIcon() { + return new Icon(); + } + + /** + * Create an instance of {@link WithLangType } + * + */ + public WithLangType createWithLangType() { + return new WithLangType(); + } +} diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/Programme.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/Programme.java new file mode 100644 index 0000000000000..b156ccd92fd92 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/Programme.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmltv.internal.jaxb; + +import java.time.Instant; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Java class for a programme XML element + * + * @author Gaël L'hopital - Initial contribution + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType +@NonNullByDefault +public class Programme { + private static final DateTimeFormatter XMLTV_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss Z"); + + @XmlElement(name = "title", required = true) + protected List titles = new ArrayList<>(); + + @XmlElement(name = "category") + protected List categories = new ArrayList<>(); + + @XmlElement(name = "icon") + protected List icons = new ArrayList<>(); + + @XmlAttribute(required = true) + private String start = ""; + + @XmlAttribute + private String stop = ""; + + @XmlAttribute(required = true) + @XmlJavaTypeAdapter(CollapsedStringAdapter.class) + protected String channel = ""; + + public List getTitles() { + return titles; + } + + public List getCategories() { + return categories; + } + + public Instant getProgrammeStart() { + long epoch = iso860DateToEpoch(start); + return Instant.ofEpochMilli(epoch); + } + + public Instant getProgrammeStop() { + long epoch = iso860DateToEpoch(stop); + return Instant.ofEpochMilli(epoch); + } + + private long iso860DateToEpoch(String date) { + long epoch = ZonedDateTime.parse(date, XMLTV_DATE_FORMAT).toInstant().toEpochMilli(); + return epoch; + } + + public List getIcons() { + return icons; + } + + public String getChannel() { + return channel; + } + +} diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/Tv.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/Tv.java new file mode 100644 index 0000000000000..b93e2c70d45f6 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/Tv.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmltv.internal.jaxb; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Java class for a TV XML root element + * + * @author Gaël L'hopital - Initial contribution + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType +@XmlRootElement(name = "tv") +@NonNullByDefault +public class Tv { + @XmlElement(name = "channel") + protected List channels = new ArrayList<>(); + + @XmlElement(name = "programme") + protected List programmes = new ArrayList<>(); + + public List getMediaChannels() { + return this.channels; + } + + public List getProgrammes() { + return this.programmes; + } +} diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/WithLangType.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/WithLangType.java new file mode 100644 index 0000000000000..6169683ee0a84 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/WithLangType.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmltv.internal.jaxb; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlValue; +import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +/** + * Java class for an XML element value including a language attribute + * information made available to according Media Channels + * + * @author Gaël L'hopital - Initial contribution + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "withLangType") +public class WithLangType { + + @XmlValue + @XmlJavaTypeAdapter(CollapsedStringAdapter.class) + protected String value; + + @XmlAttribute + @XmlJavaTypeAdapter(CollapsedStringAdapter.class) + protected String lang; + + public String getValue() { + return value; + } + + public String getLang() { + return lang; + } + +} diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/package-info.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/package-info.java new file mode 100644 index 0000000000000..4196cbe2595c4 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/package-info.java @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +@javax.xml.bind.annotation.XmlSchema(namespace = "", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +package org.openhab.binding.xmltv.internal.jaxb; diff --git a/bundles/org.openhab.binding.xmltv/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.xmltv/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..0c40146ad7e8e --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,10 @@ + + + + XmlTV Binding + This is the binding for reading and parsing XmlTV files + Gaël L'hopital + + diff --git a/bundles/org.openhab.binding.xmltv/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.xmltv/src/main/resources/ESH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..05671afd6953d --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/resources/ESH-INF/thing/thing-types.xml @@ -0,0 +1,175 @@ + + + + + + This is the interface to a XmlTV file + + + + + Path to an XmlTV file. + + + + Specifies the XMLTV file reload interval in hours + 24 + + + + + + + + + + This represent a channel on a given TV file + + + + + + + + + + + Id of the channel as presented in the XmlTV file. + + + + + Moves an event or datetime value forward or backward (in minutes) + 0 + + + + + Specifies the refresh interval in seconds + 60 + + + + + + + + + Properties of the current channel + + + + + + + + + Program currently on air on this channel + + + + + + + + + + + + + + + + Program which will follow current program on this channel + + + + + + + + + + + + + String + + Icon URL of the TV channel. + + + + + String + + URL to an image of the program. + + + + + String + + Program Title. + + + + + String + + Program Category. + + + + + DateTime + + Program Start Time + + + + + DateTime + + Program End Time + + + + + Number:Time + + Current time of currently playing program. + + + + + Number:Time + + Time remaining until end of the program. + + + + + Number:Time + + Time left before program start + + + + + Number:Dimensionless + + Relative progression of the current program. + + + + + Image + + Icon of the channel / program. + + + + diff --git a/bundles/org.openhab.binding.xmppclient/.classpath b/bundles/org.openhab.binding.xmppclient/.classpath new file mode 100644 index 0000000000000..39abf1c5e9102 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/.classpath @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.xmppclient/.project b/bundles/org.openhab.binding.xmppclient/.project new file mode 100644 index 0000000000000..d9284ae70385f --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.xmppclient + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.modbus.test/NOTICE b/bundles/org.openhab.binding.xmppclient/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.modbus.test/NOTICE rename to bundles/org.openhab.binding.xmppclient/NOTICE diff --git a/bundles/org.openhab.binding.xmppclient/README.md b/bundles/org.openhab.binding.xmppclient/README.md new file mode 100644 index 0000000000000..412f8613ef54d --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/README.md @@ -0,0 +1,71 @@ +# XMPPClient Binding + +XMPPClient binding provides support for sending and receiving XMPP (Jabber) messages. + +## Supported Things + +xmppBridge - Basic XMPP (Jabber) client thing, that can send and receive messages. + +## Thing Configuration + +**xmppBridge** parameters: + +| Name | Label | Description | Required | +|----------|-------------|------|------| +| host | Server Hostname/IP | The IP/Hostname of the XMPP server | true | +| port | XMPP server Port | The typical port is 5222 | true | +| username | Username | The XMPP username | true | +| password | Password | The XMPP password | true | + + +## Channels + +**publishTrigger** parameters: + +| Name | Label | Description | Required | +|----------|-------------|------|------| +| payload | Payload condition | An optional condition on the value | false | +| separator | Separator character | The trigger channel payload usually only contains the received text. If you define a separator character, for example '#', the sender UID and received text will be in the trigger channel payload. For example: pavel@example.com#My Message Text | false | + +## Example Rules + +Send message: + +``` +rule "Leak detected" +when + Item Xi_Leak changed +then + if(Xi_Leak.state == ON) { + val actions = getActions("xmpp","xmppclient:xmppBridge:xmpp") + actions.publishXMPP("pavel@example.com","Warning! Leak detected!") + } +end +``` + +Receive and process message: + +``` +rule "Turn off all lights without separator" +when + Channel "xmppclient:xmppBridge:xmpp:xmpp_command" triggered +then + var actionName = receivedEvent.getEvent() + if(actionName.toLowerCase() == "turn off lights") { + Group_Light_Home_All.sendCommand(OFF) + } +end + +rule "Turn off all lights with separator and reply" +when + Channel "xmppclient:xmppBridge:xmpp:xmpp_command" triggered +then + var actionName = receivedEvent.getEvent().split("#") + if(actionName.get(1).toLowerCase() == "turn off lights") { + Group_Light_Home_All.sendCommand(OFF) + + val actions = getActions("xmpp","xmppclient:xmppBridge:xmpp") + actions.publishXMPP(actionName.get(0),"All lights was turned off") + } +end +``` diff --git a/bundles/org.openhab.binding.xmppclient/pom.xml b/bundles/org.openhab.binding.xmppclient/pom.xml new file mode 100644 index 0000000000000..9cbb8041e5cf9 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + + 4.3.3 + + + org.openhab.binding.xmppclient + + openHAB Add-ons :: Bundles :: XMPPClient Binding + + + + org.igniterealtime.smack + smack-java7 + ${smack.version} + provided + + + org.igniterealtime.smack + smack-extensions + ${smack.version} + provided + + + org.igniterealtime.smack + smack-im + ${smack.version} + provided + + + org.igniterealtime.smack + smack-tcp + ${smack.version} + provided + + + + diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/XMPPClientBindingConstants.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/XMPPClientBindingConstants.java new file mode 100644 index 0000000000000..ad34f918ae9d0 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/XMPPClientBindingConstants.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmppclient; + +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +/** + * The {@link XMPPClientBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Pavel Gololobov - Initial contribution + */ +public class XMPPClientBindingConstants { + private static final String BINDING_ID = "xmppclient"; + + // List of all Thing Type UIDs + public static final ThingTypeUID BRIDGE_TYPE_XMPP = new ThingTypeUID(BINDING_ID, "xmppBridge"); + public static final String PUBLISH_TRIGGER_CHANNEL = "publishTrigger"; +} diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/action/XMPPActions.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/action/XMPPActions.java new file mode 100644 index 0000000000000..4a33a0de190b0 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/action/XMPPActions.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmppclient.action; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.binding.ThingActions; +import org.eclipse.smarthome.core.thing.binding.ThingActionsScope; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.openhab.binding.xmppclient.internal.XMPPClient; +import org.openhab.binding.xmppclient.handler.XMPPClientHandler; +import org.openhab.core.automation.annotation.ActionInput; +import org.openhab.core.automation.annotation.RuleAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is the automation engine action handler service for the + * publishXMPP action. + * + * @author Pavel Gololobov - Initial contribution + */ +@ThingActionsScope(name = "xmpp") +@NonNullByDefault +public class XMPPActions implements ThingActions { + private static final Logger logger = LoggerFactory.getLogger(XMPPActions.class); + private @Nullable XMPPClientHandler handler; + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + this.handler = (XMPPClientHandler) handler; + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return this.handler; + } + + @RuleAction(label = "publishXMPP", description = "Publish to XMPP") + public void publishXMPP( + @ActionInput(name = "to", label = "To", description = "Send to") @Nullable String to, + @ActionInput(name = "text", label = "Text", description = "Message text") @Nullable String text) { + XMPPClientHandler clientHandler = handler; + if (clientHandler == null) { + logger.warn("XMPP ThingHandler is null"); + return; + } + + XMPPClient connection = clientHandler.getXMPPClient(); + if (connection == null) { + logger.warn("XMPP ThingHandler connection is null"); + return; + } + if ((to == null) || (text == null)) { + logger.info("Skipping XMPP messaging to {} value {}", to, text); + return; + } + connection.sendMessage(to, text); + } + + public static void publishXMPP(@Nullable ThingActions actions, @Nullable String to, @Nullable String text) { + if(actions == null) { + logger.warn("Sending error, actions is NULL"); + throw new IllegalArgumentException("actions is NULL"); + } + if (actions instanceof XMPPActions) { + ((XMPPActions) actions).publishXMPP(to, text); + } else { + logger.warn("Sending error, instance of actions is {}", actions.getClass().getName()); + throw new IllegalArgumentException("actions is not an XMPPActions class"); + } + } +} diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannel.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannel.java new file mode 100644 index 0000000000000..6ac547448ad55 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannel.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmppclient.handler; + +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.openhab.binding.xmppclient.internal.XMPPClient; + +/** + * Subscribes to a chat and calls {@link AbstractBrokerHandler#triggerChannel(ChannelUID, String)} if a value has been + * received. + * + * @author Pavel Gololobov - Initial contribution + */ +public class PublishTriggerChannel implements XMPPClientMessageSubscriber { + private final XMPPClient connection; + private final PublishTriggerChannelConfig config; + private final ChannelUID uid; + private final XMPPClientHandler handler; + + PublishTriggerChannel(PublishTriggerChannelConfig config, ChannelUID uid, XMPPClient connection, XMPPClientHandler handler) { + this.config = config; + this.uid = uid; + this.connection = connection; + this.handler = handler; + } + + public void start() { + connection.subscribe(this); + } + + public void stop() { + connection.unsubscribe(this); + } + + @Override + public void processMessage(String from, String payload) { + // Check condition if exists + String expectedPayload = config.payload; + if ((expectedPayload != null) && (!expectedPayload.isEmpty()) && !payload.equals(expectedPayload)) { + return; + } + String eventValue = ""; + if(!config.separator.isEmpty()) { + eventValue = from + config.separator + payload; + } else { + eventValue = payload; + } + handler.triggerChannel(uid, eventValue); + } + + @Override + public String getName() { + return uid.toString(); + } +} diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannelConfig.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannelConfig.java new file mode 100644 index 0000000000000..7e17e541f7c84 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/PublishTriggerChannelConfig.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmppclient.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Holds the configuration of a {@link PublishTriggerChannel}. + * + * @author Pavel Gololobov - Initial contribution + */ +@NonNullByDefault +public class PublishTriggerChannelConfig { + public @Nullable String payload; + public String separator = ""; +} diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientConfiguration.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientConfiguration.java new file mode 100644 index 0000000000000..643a55808ce07 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientConfiguration.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmppclient.handler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link XMPPClientConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Pavel Gololobov - Initial contribution + */ +@NonNullByDefault +public class XMPPClientConfiguration { + public @Nullable String host; + public Integer port = 5222; + public String username = ""; + public String password = ""; + public String domain = ""; +} diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientHandler.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientHandler.java new file mode 100644 index 0000000000000..f9ce3d6e34057 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientHandler.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmppclient.handler; + +import org.jivesoftware.smack.*; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.openhab.binding.xmppclient.action.XMPPActions; +import org.openhab.binding.xmppclient.internal.XMPPClient; +import org.openhab.binding.xmppclient.handler.XMPPClientConfiguration; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; +import org.eclipse.smarthome.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +/** + * The {@link XMPPClientHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Pavel Gololobov - Initial contribution + */ + +public class XMPPClientHandler extends BaseBridgeHandler { + private final Logger logger = LoggerFactory.getLogger(XMPPClientHandler.class); + private XMPPClient xmppClient; + private XMPPClientConfiguration config; + private final Map channelStateByChannelUID = new HashMap<>(); + + public XMPPClientHandler(Bridge thing) { + super(thing); + } + + public XMPPClient getXMPPClient() { + return xmppClient; + } + + @Override + public Collection> getServices() { + return Collections.singleton(XMPPActions.class); + } + + @Override + protected void triggerChannel(ChannelUID channelUID, String event) { + super.triggerChannel(channelUID, event); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + // not supported + } + + @Override + public void initialize() { + updateStatus(ThingStatus.UNKNOWN); + scheduler.schedule(this::doConnect, 0, TimeUnit.SECONDS); + } + + @Override + public void dispose() { + channelStateByChannelUID.values().forEach(c -> c.stop()); + channelStateByChannelUID.clear(); + xmppClient.disconnect(); + super.dispose(); + } + + private void doConnect() { + config = getConfigAs(XMPPClientConfiguration.class); + xmppClient = new XMPPClient(); + try { + xmppClient.connect(config.host, config.port, config.username, config.domain, config.password); + } catch (SmackException | IOException | XMPPException e) { + logger.info("XMPP connection error", e); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + return; + } + + for(Channel channel : thing.getChannels()) { + final PublishTriggerChannelConfig channelConfig = channel.getConfiguration().as(PublishTriggerChannelConfig.class); + PublishTriggerChannel c = new PublishTriggerChannel(channelConfig, channel.getUID(), xmppClient, this); + channelStateByChannelUID.put(channel.getUID(), c); + logger.info("XMPP added channel {} payload {}", channel.getUID().toString(), channelConfig.payload); + } + channelStateByChannelUID.values().forEach(c -> c.start()); + + updateStatus(ThingStatus.ONLINE); + } +} diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientMessageSubscriber.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientMessageSubscriber.java new file mode 100644 index 0000000000000..cc7b8bc8d4838 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/handler/XMPPClientMessageSubscriber.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmppclient.handler; + +/** + * Subscriber interface + * + * @author Pavel Gololobov - Initial contribution + */ +public interface XMPPClientMessageSubscriber { + public void processMessage(String from, String payload); + public String getName(); +} diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClient.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClient.java new file mode 100644 index 0000000000000..b2cd8ae3e2ef3 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClient.java @@ -0,0 +1,152 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmppclient.internal; + +import org.jivesoftware.smack.*; +import org.jivesoftware.smack.chat2.Chat; +import org.jivesoftware.smack.chat2.ChatManager; +import org.jivesoftware.smack.chat2.IncomingChatMessageListener; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.tcp.XMPPTCPConnection; +import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; +import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; +import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity; + +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; +import org.jxmpp.stringprep.XmppStringprepException; +import org.openhab.binding.xmppclient.handler.XMPPClientMessageSubscriber; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link XMPPClient} is lib for handling XMPP connection and messaging + * + * @author Pavel Gololobov - Initial contribution + */ +public class XMPPClient implements IncomingChatMessageListener, ConnectionListener { + private final Logger logger = LoggerFactory.getLogger(XMPPClient.class); + private AbstractXMPPConnection connection; + private ChatManager chatManager; + private Set subscribers = new HashSet<>(); + + public void subscribe(XMPPClientMessageSubscriber channel) { + logger.debug("Channel {} subscribed", channel.getName()); + subscribers.add(channel); + } + + public void unsubscribe(XMPPClientMessageSubscriber channel) { + logger.debug("Channel {} unsubscribed", channel.getName()); + subscribers.remove(channel); + } + + public void connect(String host, Integer port, String login, String domain, String password) + throws XMPPException, SmackException, IOException { + disconnect(); + String serverHost = domain; + if((host != null) && !host.isEmpty()) { + serverHost = host; + } + + XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder() // + .setHost(serverHost) // + .setPort(port) // + .setUsernameAndPassword(login, password) // + .setXmppDomain(domain) // + .build(); + + connection = new XMPPTCPConnection(config); + connection.addConnectionListener(this); + + ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(connection); + reconnectionManager.enableAutomaticReconnection(); + + Identity identity = new Identity("client", "openHAB", "bot"); + ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); + sdm.setIdentity(identity); + + try { + connection.connect().login(); + } catch (InterruptedException ex) { + } + + chatManager = ChatManager.getInstanceFor(connection); + chatManager.addIncomingListener(this); + } + + public void disconnect() { + if(connection != null) { + connection.disconnect(); + } + } + + public void sendMessage(String to, String message) { + if(connection == null) { + logger.warn("XMPP connection is null"); + return; + } + if(chatManager == null) { + logger.warn("XMPP chatManager is null"); + return; + } + try { + EntityBareJid jid = JidCreate.entityBareFrom(to); + Chat chat = chatManager.chatWith(jid); + chat.send(message); + } catch (XmppStringprepException | SmackException.NotConnectedException | InterruptedException e) { + logger.info("XMPP message sending error", e); + } + } + + @Override + public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) { + logger.debug("XMPP {} says {}", from.asBareJid().toString(), message.getBody()); + for(XMPPClientMessageSubscriber subscriber : subscribers) { + logger.debug("Push to subscriber {}", subscriber.getName()); + subscriber.processMessage(from.asBareJid().toString(), message.getBody()); + } + } + + @Override + public void connected(XMPPConnection connection) { + logger.debug("Connected to XMPP server."); + } + + @Override + public void authenticated(XMPPConnection connection, boolean resumed) { + logger.debug("Authenticated to XMPP server."); + } + + @Override + public void connectionClosed() { + logger.debug("XMPP connection was closed."); + } + + @Override + public void connectionClosedOnError(Exception e) { + logger.debug("Connection to XMPP server was lost."); + if(connection != null) { + connection.disconnect(); + try { + connection.connect().login(); + } catch (SmackException | IOException | XMPPException | InterruptedException ex) { + logger.info("XMPP connection error", ex); + } + } + } +} diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientHandlerFactory.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientHandlerFactory.java new file mode 100644 index 0000000000000..eef605958e798 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientHandlerFactory.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.xmppclient.internal; + +import java.util.Collections; +import java.util.Set; + +import org.openhab.binding.xmppclient.XMPPClientBindingConstants; +import org.openhab.binding.xmppclient.handler.XMPPClientHandler; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link XMPPClientHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Pavel Gololobov - Initial contribution + */ +@Component(configurationPid = "binding.xmppclient", service = ThingHandlerFactory.class) +public class XMPPClientHandlerFactory extends BaseThingHandlerFactory { + private static final Set SUPPORTED_THING_TYPES_UIDS = + Collections.singleton(XMPPClientBindingConstants.BRIDGE_TYPE_XMPP); + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (thingTypeUID.equals(XMPPClientBindingConstants.BRIDGE_TYPE_XMPP)) { + return new XMPPClientHandler((Bridge) thing); + } + return null; + } +} diff --git a/bundles/org.openhab.binding.xmppclient/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.xmppclient/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..c82138608aaf8 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,10 @@ + + + + XMPPClient Binding + This is the binding for XMPP (Jabber) notifications. + Pavel Gololobov + + diff --git a/bundles/org.openhab.binding.xmppclient/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.xmppclient/src/main/resources/ESH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..f2fe001c79c11 --- /dev/null +++ b/bundles/org.openhab.binding.xmppclient/src/main/resources/ESH-INF/thing/thing-types.xml @@ -0,0 +1,53 @@ + + + + + + A connection to a XMPP server + + + + The XMPP Username (the left side of JID, e.g. user for JID user@example.com) + + + + The XMPP Domain (the right side of JID, e.g. example.com for JID user@example.com) + network-address + + + + The XMPP Password + password + + + + The IP/Hostname of the XMPP server (if not specified, the Domain will be used) + network-address + + + + The default port is 5222. + + + + + + trigger + + This channel is triggered when a message is received on the configured XMPP account. The event payload will be the received text. + + + + + An optional condition on the value + + + + The trigger channel payload usually only contains the received text. If you define a separator character, for example '#', the sender UID and received text will be in the trigger channel payload. For example: pavel@example.com#My Message Text. + + + + diff --git a/bundles/org.openhab.binding.yamahareceiver/.classpath b/bundles/org.openhab.binding.yamahareceiver/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.yamahareceiver/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.yamahareceiver/.project b/bundles/org.openhab.binding.yamahareceiver/.project new file mode 100644 index 0000000000000..fc33a5a9c6eb3 --- /dev/null +++ b/bundles/org.openhab.binding.yamahareceiver/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.yamahareceiver + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.modbus/NOTICE b/bundles/org.openhab.binding.yamahareceiver/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.modbus/NOTICE rename to bundles/org.openhab.binding.yamahareceiver/NOTICE diff --git a/addons/binding/org.openhab.binding.yamahareceiver/README.md b/bundles/org.openhab.binding.yamahareceiver/README.md similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/README.md rename to bundles/org.openhab.binding.yamahareceiver/README.md diff --git a/addons/binding/org.openhab.binding.yamahareceiver/docs/README.md b/bundles/org.openhab.binding.yamahareceiver/docs/README.md similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/docs/README.md rename to bundles/org.openhab.binding.yamahareceiver/docs/README.md diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/HTR-4069/desc_HTR-4069.xml b/bundles/org.openhab.binding.yamahareceiver/docs/desc_HTR-4069.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/HTR-4069/desc_HTR-4069.xml rename to bundles/org.openhab.binding.yamahareceiver/docs/desc_HTR-4069.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-A2000.xml b/bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-A2000.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-A2000.xml rename to bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-A2000.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-A3070.xml b/bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-A3070.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-A3070.xml rename to bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-A3070.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-S601D.xml b/bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-S601D.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-S601D.xml rename to bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-S601D.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/desc_RX-V3900.xml b/bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-V3900.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/desc_RX-V3900.xml rename to bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-V3900.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-V479.xml b/bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-V479.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-V479.xml rename to bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-V479.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-V583.xml b/bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-V583.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-V583.xml rename to bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-V583.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-V675.xml b/bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-V675.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-V675.xml rename to bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-V675.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-V775.xml b/bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-V775.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-V775.xml rename to bundles/org.openhab.binding.yamahareceiver/docs/desc_RX-V775.xml diff --git a/bundles/org.openhab.binding.yamahareceiver/pom.xml b/bundles/org.openhab.binding.yamahareceiver/pom.xml new file mode 100644 index 0000000000000..41b879a5a39c6 --- /dev/null +++ b/bundles/org.openhab.binding.yamahareceiver/pom.xml @@ -0,0 +1,16 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.yamahareceiver + + openHAB Add-ons :: Bundles :: Yamaha Receiver Binding + + diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/ChannelsTypeProviderAvailableInputs.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/ChannelsTypeProviderAvailableInputs.java similarity index 77% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/ChannelsTypeProviderAvailableInputs.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/ChannelsTypeProviderAvailableInputs.java index 140c7eb083248..01221558f0e43 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/ChannelsTypeProviderAvailableInputs.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/ChannelsTypeProviderAvailableInputs.java @@ -22,7 +22,10 @@ import java.util.Map; import java.util.Map.Entry; -import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; import org.eclipse.smarthome.core.thing.type.ChannelGroupType; import org.eclipse.smarthome.core.thing.type.ChannelGroupTypeUID; import org.eclipse.smarthome.core.thing.type.ChannelType; @@ -31,25 +34,27 @@ import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; import org.eclipse.smarthome.core.types.StateDescription; import org.eclipse.smarthome.core.types.StateOption; +import org.openhab.binding.yamahareceiver.internal.handler.YamahaZoneThingHandler; /** * Provide a custom channel type for available inputs * - * @author David Graeff + * @author David Graeff - Initial contribution * @author Tomasz Maruszak - Refactoring the input source names. */ -public class ChannelsTypeProviderAvailableInputs implements ChannelTypeProvider { - - private ChannelType channelType; - private final ChannelTypeUID channelTypeUID; +@NonNullByDefault +public class ChannelsTypeProviderAvailableInputs implements ChannelTypeProvider, ThingHandlerService { + private @NonNullByDefault({}) ChannelType channelType; + private @NonNullByDefault({}) ChannelTypeUID channelTypeUID; + private @NonNullByDefault({}) YamahaZoneThingHandler handler; @Override - public Collection getChannelTypes(Locale locale) { + public @Nullable Collection getChannelTypes(@Nullable Locale locale) { return Collections.singleton(channelType); } @Override - public ChannelType getChannelType(ChannelTypeUID channelTypeUID, Locale locale) { + public @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID, @Nullable Locale locale) { if (this.channelTypeUID.equals(channelTypeUID)) { return channelType; } else { @@ -58,12 +63,13 @@ public ChannelType getChannelType(ChannelTypeUID channelTypeUID, Locale locale) } @Override - public ChannelGroupType getChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID, Locale locale) { + public @Nullable ChannelGroupType getChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID, + @Nullable Locale locale) { return null; } @Override - public Collection getChannelGroupTypes(Locale locale) { + public @Nullable Collection getChannelGroupTypes(@Nullable Locale locale) { return null; } @@ -71,25 +77,20 @@ public ChannelTypeUID getChannelTypeUID() { return channelTypeUID; } - public ChannelsTypeProviderAvailableInputs(ThingUID thing) { - channelTypeUID = new ChannelTypeUID(YamahaReceiverBindingConstants.BINDING_ID, - YamahaReceiverBindingConstants.CHANNEL_INPUT_TYPE_AVAILABLE + thing.getId()); - createChannelType(getDefaultStateDescription()); - } - private void createChannelType(StateDescription state) { channelType = ChannelTypeBuilder.state(channelTypeUID, "Input source", "String") .withDescription("Select the input source of the AVR").withStateDescription(state).build(); } private StateDescription getDefaultStateDescription() { - List options = new ArrayList(); + List options = new ArrayList<>(); options.add(new StateOption(INPUT_NET_RADIO, "Net Radio")); options.add(new StateOption(INPUT_PC, "PC")); options.add(new StateOption(INPUT_USB, "USB")); options.add(new StateOption(INPUT_TUNER, "Tuner")); options.add(new StateOption("MULTI_CH", "Multi Channel")); - // Note: this might need review in the future, it should be 'HDMI 1', the 'HDMI_1' are XML node names, not source names. + // Note: this might need review in the future, it should be 'HDMI 1', the 'HDMI_1' are XML node names, not + // source names. options.add(new StateOption("HDMI_1", "HDMI 1")); options.add(new StateOption("HDMI_2", "HDMI 2")); options.add(new StateOption("HDMI_3", "HDMI 3")); @@ -134,4 +135,18 @@ public void changeAvailableInputs(Map availableInputs) { createChannelType(new StateDescription(null, null, null, "%s", false, options)); } + @NonNullByDefault({}) + @Override + public void setThingHandler(ThingHandler handler) { + this.handler = (YamahaZoneThingHandler) handler; + this.handler.channelsTypeProviderAvailableInputs = this; + channelTypeUID = new ChannelTypeUID(YamahaReceiverBindingConstants.BINDING_ID, + YamahaReceiverBindingConstants.CHANNEL_INPUT_TYPE_AVAILABLE + handler.getThing().getUID().getId()); + createChannelType(getDefaultStateDescription()); + } + + @Override + public ThingHandler getThingHandler() { + return handler; + } } diff --git a/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/ChannelsTypeProviderPreset.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/ChannelsTypeProviderPreset.java new file mode 100644 index 0000000000000..a190f828eb88b --- /dev/null +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/ChannelsTypeProviderPreset.java @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.yamahareceiver.internal; + +import static java.util.stream.Collectors.toList; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.*; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.stream.IntStream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; +import org.eclipse.smarthome.core.thing.type.ChannelGroupType; +import org.eclipse.smarthome.core.thing.type.ChannelGroupTypeUID; +import org.eclipse.smarthome.core.thing.type.ChannelType; +import org.eclipse.smarthome.core.thing.type.ChannelTypeBuilder; +import org.eclipse.smarthome.core.thing.type.ChannelTypeProvider; +import org.eclipse.smarthome.core.thing.type.ChannelTypeUID; +import org.eclipse.smarthome.core.types.StateDescription; +import org.eclipse.smarthome.core.types.StateOption; +import org.openhab.binding.yamahareceiver.internal.handler.YamahaZoneThingHandler; +import org.openhab.binding.yamahareceiver.internal.state.PresetInfoState; + +/** + * Provide a custom channel type for the preset channel + * + * @author David Graeff - Initial contribution + * @author Tomasz Maruszak - RX-V3900 compatibility improvements + */ +@NonNullByDefault +public class ChannelsTypeProviderPreset implements ChannelTypeProvider, ThingHandlerService { + private @NonNullByDefault({}) ChannelType channelType; + private @NonNullByDefault({}) ChannelTypeUID channelTypeUID; + private @NonNullByDefault({}) YamahaZoneThingHandler handler; + + @Override + public @Nullable Collection getChannelTypes(@Nullable Locale locale) { + return Collections.singleton(channelType); + } + + @Override + public @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID, @Nullable Locale locale) { + if (this.channelTypeUID.equals(channelTypeUID)) { + return channelType; + } else { + return null; + } + } + + @Override + public @Nullable ChannelGroupType getChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID, + @Nullable Locale locale) { + return null; + } + + @Override + public @Nullable Collection getChannelGroupTypes(@Nullable Locale locale) { + return null; + } + + public ChannelTypeUID getChannelTypeUID() { + return channelTypeUID; + } + + private StateDescription getDefaultStateDescription() { + List options = IntStream.rangeClosed(1, 40) + .mapToObj(i -> new StateOption(Integer.toString(i), "Item_" + i)).collect(toList()); + + StateDescription state = new StateDescription(null, null, null, "%s", false, options); + return state; + } + + public void changePresetNames(List presets) { + List options = presets.stream() + .map(preset -> new StateOption(String.valueOf(preset.getValue()), preset.getName())).collect(toList()); + + StateDescription state = new StateDescription(null, null, null, "%s", false, options); + createChannelType(state); + } + + private void createChannelType(StateDescription state) { + channelType = ChannelTypeBuilder.state(channelTypeUID, "Preset", "Number") + .withDescription("Select a saved channel by its preset number").withStateDescription(state).build(); + } + + @NonNullByDefault({}) + @Override + public void setThingHandler(ThingHandler handler) { + this.handler = (YamahaZoneThingHandler) handler; + this.handler.channelsTypeProviderPreset = this; + /** + * We generate a thing specific channelTypeUID, because presets absolutely depends on the thing. + */ + channelTypeUID = new ChannelTypeUID(BINDING_ID, + CHANNEL_PLAYBACK_PRESET_TYPE_NAMED + handler.getThing().getUID().getId()); + + StateDescription state = getDefaultStateDescription(); + createChannelType(state); + } + + @Override + public ThingHandler getThingHandler() { + return this.handler; + } + +} diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverBindingConstants.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverBindingConstants.java similarity index 99% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverBindingConstants.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverBindingConstants.java index 66e1a07aeb84a..fc8238b29b418 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverBindingConstants.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverBindingConstants.java @@ -24,7 +24,7 @@ * The {@link YamahaReceiverBindingConstants} class defines common constants, which are * used across the whole binding. * - * @author David Graeff + * @author David Graeff - Initial contribution * @author Tomasz Maruszak - DAB support, Spotify support, refactoring */ @NonNullByDefault diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverHandlerFactory.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverHandlerFactory.java similarity index 91% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverHandlerFactory.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverHandlerFactory.java index 1eb9b92fc29b6..bfa89a851dad4 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverHandlerFactory.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverHandlerFactory.java @@ -19,6 +19,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingTypeUID; @@ -35,9 +37,10 @@ * The {@link YamahaReceiverHandlerFactory} is responsible for creating things and thing * handlers. * - * @author David Graeff -- Intial contribution + * @author David Graeff - Initial contribution */ @Component(service = ThingHandlerFactory.class, configurationPid = "binding.yamahareceiver") +@NonNullByDefault public class YamahaReceiverHandlerFactory extends BaseThingHandlerFactory { private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(Stream .concat(BRIDGE_THING_TYPES_UIDS.stream(), ZONE_THING_TYPES_UIDS.stream()).collect(Collectors.toSet())); @@ -49,7 +52,7 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { } @Override - protected ThingHandler createHandler(Thing thing) { + protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); if (thingTypeUID.equals(BRIDGE_THING_TYPE)) { diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaBridgeConfig.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaBridgeConfig.java similarity index 84% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaBridgeConfig.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaBridgeConfig.java index 2e499407948c1..bb2e8d14b240f 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaBridgeConfig.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaBridgeConfig.java @@ -12,21 +12,24 @@ */ package org.openhab.binding.yamahareceiver.internal.config; -import org.apache.commons.lang.StringUtils; - import java.util.Optional; +import org.apache.commons.lang.StringUtils; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + /** * Main settings. * * @author Tomasz Maruszak - Initial contribution. */ +@NonNullByDefault public class YamahaBridgeConfig { /** * The host name of the Yamaha AVR. */ - private String host; + private @Nullable String host; /** * Port under which the control interface is exposed on the Yamaha AVR. */ @@ -44,7 +47,7 @@ public class YamahaBridgeConfig { */ private String inputMapping = ""; - public String getHost() { + public @Nullable String getHost() { return host; } @@ -61,10 +64,11 @@ public String getAlbumUrl() { } public Optional getHostWithPort() { - if (StringUtils.isEmpty(host)) { + final String str = host; + if (StringUtils.isEmpty(str)) { return Optional.empty(); } - return Optional.of(host + ":" + port); + return Optional.of(str + ":" + port); } public String getInputMapping() { diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaUtils.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaUtils.java similarity index 81% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaUtils.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaUtils.java index 643e668f82c8d..1e870a758ccc6 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaUtils.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaUtils.java @@ -12,21 +12,26 @@ */ package org.openhab.binding.yamahareceiver.internal.config; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + /** * Utilities used by the Yamaha binding * * @author Tomasz Maruszak - Initial contribution */ +@NonNullByDefault public class YamahaUtils { /** * Tries to parse a string into Enum, if unsuccessful returns null. + * * @param c * @param string * @param * @return Enum value or null if unsuccessful */ - public static > T tryParseEnum(Class c, String string) { + public static @Nullable > T tryParseEnum(Class c, @Nullable String string) { if (string != null) { try { return Enum.valueOf(c, string); diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaZoneConfig.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaZoneConfig.java similarity index 84% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaZoneConfig.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaZoneConfig.java index 50976e1b9aa65..57bfc628b9f77 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaZoneConfig.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/config/YamahaZoneConfig.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.yamahareceiver.internal.config; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants; /** @@ -19,14 +21,16 @@ * * @author Tomasz Maruszak - Initial contribution. */ +@NonNullByDefault public class YamahaZoneConfig { - /** - * Zone name, will be one of {@link org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone}. + * Zone name, will be one of + * {@link org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone}. */ private String zone = ""; /** - * Volume relative change factor when sending {@link org.eclipse.smarthome.core.library.types.IncreaseDecreaseType} commands. + * Volume relative change factor when sending {@link org.eclipse.smarthome.core.library.types.IncreaseDecreaseType} + * commands. */ private float volumeRelativeChangeFactor = 0.5f; // Default: 0.5 percent /** @@ -38,7 +42,7 @@ public class YamahaZoneConfig { */ private float volumeDbMax = 12f; // 12.0 dB - public YamahaReceiverBindingConstants.Zone getZone() { + public YamahaReceiverBindingConstants.@Nullable Zone getZone() { return YamahaUtils.tryParseEnum(YamahaReceiverBindingConstants.Zone.class, zone); } @@ -64,6 +68,7 @@ private float getVolumeDbRange() { /** * Converts from volume percentage to volume dB. + * * @param volume volume percentage * @return volume dB */ @@ -73,6 +78,7 @@ public float getVolumeDb(float volume) { /** * Converts from volume dB to volume percentage. + * * @param volumeDb volume dB * @return volume percentage */ diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/discovery/YamahaDiscoveryParticipant.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/discovery/YamahaDiscoveryParticipant.java similarity index 91% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/discovery/YamahaDiscoveryParticipant.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/discovery/YamahaDiscoveryParticipant.java index 2d374d1725bb2..e92697630b0f9 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/discovery/YamahaDiscoveryParticipant.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/discovery/YamahaDiscoveryParticipant.java @@ -21,6 +21,8 @@ import java.util.Map; import java.util.Set; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.discovery.DiscoveryResult; import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; import org.eclipse.smarthome.config.discovery.upnp.UpnpDiscoveryParticipant; @@ -39,6 +41,7 @@ * @author Tomasz Maruszak - Introduced config object, migrated to newer UPnP api */ @Component(immediate = true) +@NonNullByDefault public class YamahaDiscoveryParticipant implements UpnpDiscoveryParticipant { private final Logger logger = LoggerFactory.getLogger(YamahaDiscoveryParticipant.class); @@ -51,7 +54,7 @@ public Set getSupportedThingTypeUIDs() { } @Override - public DiscoveryResult createResult(RemoteDevice device) { + public @Nullable DiscoveryResult createResult(RemoteDevice device) { ThingUID uid = getThingUID(device); if (uid == null) { return null; @@ -84,7 +87,8 @@ public DiscoveryResult createResult(RemoteDevice device) { return result; } - public static ThingUID getThingUID(String manufacturer, String deviceType, String udn) { + public static @Nullable ThingUID getThingUID(@Nullable String manufacturer, @Nullable String deviceType, + String udn) { if (manufacturer == null || deviceType == null) { return null; } @@ -97,7 +101,7 @@ public static ThingUID getThingUID(String manufacturer, String deviceType, Strin } @Override - public ThingUID getThingUID(RemoteDevice device) { + public @Nullable ThingUID getThingUID(RemoteDevice device) { String manufacturer = device.getDetails().getManufacturerDetails().getManufacturer(); String deviceType = device.getType().getType(); diff --git a/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/discovery/ZoneDiscoveryService.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/discovery/ZoneDiscoveryService.java new file mode 100644 index 0000000000000..0dcc8fb341337 --- /dev/null +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/discovery/ZoneDiscoveryService.java @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.yamahareceiver.internal.discovery; + +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.*; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Configs.CONFIG_ZONE; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.discovery.AbstractDiscoveryService; +import org.eclipse.smarthome.config.discovery.DiscoveryResult; +import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; +import org.eclipse.smarthome.config.discovery.DiscoveryService; +import org.eclipse.smarthome.core.thing.ThingUID; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; +import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone; +import org.openhab.binding.yamahareceiver.internal.handler.YamahaBridgeHandler; +import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState; + +/** + * After the AVR bridge thing has been added and a connection could be established, + * the user is presented with the available zones. + * + * @author David Gräff - Initial contribution + * @author Tomasz Maruszak - Introduced config object + */ +@NonNullByDefault +public class ZoneDiscoveryService extends AbstractDiscoveryService implements DiscoveryService, ThingHandlerService { + + private @Nullable YamahaBridgeHandler handler; + + /** + * Constructs a zone discovery service. + * Registers this zone discovery service programmatically. + * Call {@link ZoneDiscoveryService#destroy()} to unregister the service after use. + */ + public ZoneDiscoveryService() { + super(ZONE_THING_TYPES_UIDS, 0, false); + } + + @Override + public void activate() { + super.activate(null); + } + + @Override + public void deactivate() { + super.deactivate(); + } + + @Override + protected void startScan() { + } + + public static ThingUID zoneThing(ThingUID bridgeUid, String zoneName) { + return new ThingUID(ZONE_THING_TYPE, bridgeUid, zoneName); + } + + /** + * The available zones are within the {@link DeviceInformationState}. Will will publish those + * as things via this discovery service instance. + * + * @param state The device information state + * @param bridgeUid The bridge UID + */ + public void publishZones(DeviceInformationState state, ThingUID bridgeUid) { + // Create a copy of the list to avoid concurrent modification exceptions, because + // the state update takes place in another thread + List zoneCopy = new ArrayList<>(state.zones); + + for (Zone zone : zoneCopy) { + String zoneName = zone.name(); + ThingUID uid = zoneThing(bridgeUid, zoneName); + + Map properties = new HashMap<>(); + properties.put(CONFIG_ZONE, zoneName); + + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(uid).withProperties(properties) + .withLabel(state.name + " " + zoneName).withBridge(bridgeUid) + .withRepresentationProperty(CONFIG_ZONE).build(); + + thingDiscovered(discoveryResult); + } + } + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + if (handler instanceof YamahaBridgeHandler) { + this.handler = (YamahaBridgeHandler) handler; + this.handler.setZoneDiscoveryService(this); + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return handler; + } +} diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/handler/YamahaBridgeHandler.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/handler/YamahaBridgeHandler.java similarity index 84% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/handler/YamahaBridgeHandler.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/handler/YamahaBridgeHandler.java index 5cf6c487a998e..55412329630c7 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/handler/YamahaBridgeHandler.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/handler/YamahaBridgeHandler.java @@ -12,17 +12,40 @@ */ package org.openhab.binding.yamahareceiver.internal.handler; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.*; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; import org.eclipse.smarthome.core.library.types.OnOffType; -import org.eclipse.smarthome.core.thing.*; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; import org.eclipse.smarthome.core.thing.binding.builder.ThingStatusInfoBuilder; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; import org.openhab.binding.yamahareceiver.internal.config.YamahaBridgeConfig; import org.openhab.binding.yamahareceiver.internal.discovery.ZoneDiscoveryService; -import org.openhab.binding.yamahareceiver.internal.protocol.*; +import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; +import org.openhab.binding.yamahareceiver.internal.protocol.ConnectionStateListener; +import org.openhab.binding.yamahareceiver.internal.protocol.DeviceInformation; +import org.openhab.binding.yamahareceiver.internal.protocol.InputConverter; +import org.openhab.binding.yamahareceiver.internal.protocol.ProtocolFactory; +import org.openhab.binding.yamahareceiver.internal.protocol.ReceivedMessageParseException; +import org.openhab.binding.yamahareceiver.internal.protocol.SystemControl; import org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolFactory; import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState; import org.openhab.binding.yamahareceiver.internal.state.SystemControlState; @@ -30,16 +53,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.*; - -import java.io.IOException; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - /** * The {@link YamahaBridgeHandler} is responsible for fetching basic information about the * found AVR and start the zone detection. @@ -84,7 +97,7 @@ public InputConverter getInputConverter() { /** * @return Return the protocol communication object. This may be null - * if the bridge is offline. + * if the bridge is offline. */ public AbstractConnection getConnection() { return connection; @@ -92,6 +105,7 @@ public AbstractConnection getConnection() { /** * Gets the current protocol factory. + * * @return */ public ProtocolFactory getProtocolFactory() { @@ -100,6 +114,7 @@ public ProtocolFactory getProtocolFactory() { /** * Sets the current protocol factory. + * * @param protocolFactory */ public void setProtocolFactory(ProtocolFactory protocolFactory) { @@ -154,13 +169,17 @@ public void handleCommand(ChannelUID channelUID, Command command) { break; case CHANNEL_PARTY_MODE_VOLUME: if (command instanceof IncreaseDecreaseType) { - systemControl.setPartyModeVolume(((IncreaseDecreaseType) command) == IncreaseDecreaseType.INCREASE); + systemControl + .setPartyModeVolume(((IncreaseDecreaseType) command) == IncreaseDecreaseType.INCREASE); } else { - logger.warn("Only {} and {} commands are supported for {}", IncreaseDecreaseType.DECREASE, IncreaseDecreaseType.DECREASE, id); + logger.warn("Only {} and {} commands are supported for {}", IncreaseDecreaseType.DECREASE, + IncreaseDecreaseType.DECREASE, id); } break; default: - logger.warn("Channel {} not supported on the yamaha device directly! Try with the zone things instead.", id); + logger.warn( + "Channel {} not supported on the yamaha device directly! Try with the zone things instead.", + id); } } catch (IOException | ReceivedMessageParseException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); @@ -189,12 +208,14 @@ private void refreshFromState(ChannelUID channelUID) { * Sets up a refresh timer (using the scheduler) with the given interval. * * @param initialWaitTime The delay before the first refresh. Maybe 0 to immediately - * initiate a refresh. + * initiate a refresh. */ private void setupRefreshTimer(int initialWaitTime) { cancelRefreshTimer(); - logger.trace("Setting up refresh timer with fixed delay {} seconds, starting in {} seconds", bridgeConfig.getRefreshInterval(), initialWaitTime); - refreshTimer = scheduler.scheduleWithFixedDelay(() -> updateAllZoneInformation(), initialWaitTime, bridgeConfig.getRefreshInterval(), TimeUnit.SECONDS); + logger.trace("Setting up refresh timer with fixed delay {} seconds, starting in {} seconds", + bridgeConfig.getRefreshInterval(), initialWaitTime); + refreshTimer = scheduler.scheduleWithFixedDelay(() -> updateAllZoneInformation(), initialWaitTime, + bridgeConfig.getRefreshInterval(), TimeUnit.SECONDS); } /** @@ -284,7 +305,8 @@ public void handleConfigurationUpdate(Map configurationParameter updateConfiguration(configurationObject); bridgeConfig = configurationObject.as(YamahaBridgeConfig.class); - logger.trace("Update configuration of {} with host '{}' and port {}", getThing().getLabel(), bridgeConfig.getHost(), bridgeConfig.getPort()); + logger.trace("Update configuration of {} with host '{}' and port {}", getThing().getLabel(), + bridgeConfig.getHost(), bridgeConfig.getPort()); Optional host = bridgeConfig.getHostWithPort(); if (host.isPresent()) { @@ -304,15 +326,26 @@ public void thingUpdated(Thing thing) { this.thing = thing; } + @Override + public Collection> getServices() { + return Collections.singleton(ZoneDiscoveryService.class); + } + + /** + * Called by the zone discovery service to let this handler have a reference. + */ + public void setZoneDiscoveryService(ZoneDiscoveryService s) { + this.zoneDiscoveryService = s; + } + /** * Calls createCommunicationObject if the host name is configured correctly. */ @Override public void initialize() { - zoneDiscoveryService = new ZoneDiscoveryService(bundleContext); - bridgeConfig = getConfigAs(YamahaBridgeConfig.class); - logger.trace("Initialize of {} with host '{}' and port {}", getThing().getLabel(), bridgeConfig.getHost(), bridgeConfig.getPort()); + logger.trace("Initialize of {} with host '{}' and port {}", getThing().getLabel(), bridgeConfig.getHost(), + bridgeConfig.getPort()); Optional host = bridgeConfig.getHostWithPort(); if (!host.isPresent()) { @@ -322,6 +355,11 @@ public void initialize() { return; } + if (this.zoneDiscoveryService == null) { + logger.warn("Zone discovery service not ready!"); + return; + } + protocolFactory.createConnection(host.get(), this); inputConverter = protocolFactory.InputConverter(connection, bridgeConfig.getInputMapping()); } diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/handler/YamahaZoneThingHandler.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/handler/YamahaZoneThingHandler.java similarity index 84% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/handler/YamahaZoneThingHandler.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/handler/YamahaZoneThingHandler.java index 66a3d0b6c7166..aa2721ec0b362 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/handler/YamahaZoneThingHandler.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/handler/YamahaZoneThingHandler.java @@ -12,36 +12,74 @@ */ package org.openhab.binding.yamahareceiver.internal.handler; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.*; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.*; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.config.core.Configuration; -import org.eclipse.smarthome.core.library.types.*; -import org.eclipse.smarthome.core.thing.*; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; +import org.eclipse.smarthome.core.library.types.NextPreviousType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.library.types.PlayPauseType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.library.types.UpDownType; +import org.eclipse.smarthome.core.thing.Bridge; +import org.eclipse.smarthome.core.thing.Channel; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.thing.ThingStatus; +import org.eclipse.smarthome.core.thing.ThingStatusDetail; +import org.eclipse.smarthome.core.thing.ThingStatusInfo; import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; import org.eclipse.smarthome.core.thing.binding.builder.ChannelBuilder; -import org.eclipse.smarthome.core.thing.type.ChannelTypeProvider; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; import org.openhab.binding.yamahareceiver.internal.ChannelsTypeProviderAvailableInputs; import org.openhab.binding.yamahareceiver.internal.ChannelsTypeProviderPreset; +import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Feature; +import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone; import org.openhab.binding.yamahareceiver.internal.config.YamahaZoneConfig; -import org.openhab.binding.yamahareceiver.internal.protocol.*; +import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; +import org.openhab.binding.yamahareceiver.internal.protocol.IStateUpdatable; +import org.openhab.binding.yamahareceiver.internal.protocol.InputWithNavigationControl; +import org.openhab.binding.yamahareceiver.internal.protocol.InputWithPlayControl; +import org.openhab.binding.yamahareceiver.internal.protocol.InputWithPresetControl; +import org.openhab.binding.yamahareceiver.internal.protocol.InputWithTunerBandControl; +import org.openhab.binding.yamahareceiver.internal.protocol.ProtocolFactory; +import org.openhab.binding.yamahareceiver.internal.protocol.ReceivedMessageParseException; +import org.openhab.binding.yamahareceiver.internal.protocol.ZoneAvailableInputs; +import org.openhab.binding.yamahareceiver.internal.protocol.ZoneControl; import org.openhab.binding.yamahareceiver.internal.protocol.xml.InputWithNavigationControlXML; import org.openhab.binding.yamahareceiver.internal.protocol.xml.InputWithPlayControlXML; import org.openhab.binding.yamahareceiver.internal.protocol.xml.ZoneControlXML; -import org.openhab.binding.yamahareceiver.internal.state.*; -import org.osgi.framework.ServiceRegistration; +import org.openhab.binding.yamahareceiver.internal.state.AvailableInputState; +import org.openhab.binding.yamahareceiver.internal.state.AvailableInputStateListener; +import org.openhab.binding.yamahareceiver.internal.state.DabBandState; +import org.openhab.binding.yamahareceiver.internal.state.DabBandStateListener; +import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState; +import org.openhab.binding.yamahareceiver.internal.state.NavigationControlState; +import org.openhab.binding.yamahareceiver.internal.state.NavigationControlStateListener; +import org.openhab.binding.yamahareceiver.internal.state.PlayInfoState; +import org.openhab.binding.yamahareceiver.internal.state.PlayInfoStateListener; +import org.openhab.binding.yamahareceiver.internal.state.PresetInfoState; +import org.openhab.binding.yamahareceiver.internal.state.PresetInfoStateListener; +import org.openhab.binding.yamahareceiver.internal.state.ZoneControlState; +import org.openhab.binding.yamahareceiver.internal.state.ZoneControlStateListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.*; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.INPUT_SPOTIFY; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.INPUT_TUNER; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Hashtable; -import java.util.Map; -import java.util.Map.Entry; - /** * The {@link YamahaZoneThingHandler} is managing one zone of an Yamaha AVR. * It has a state consisting of the zone, the current input ID, {@link ZoneControlState} @@ -50,25 +88,20 @@ * for communication. * * @author David Graeff - * @author Tomasz Maruszak - [yamaha] Tuner band selection and preset feature for dual band models (RX-S601D), added config object + * @author Tomasz Maruszak - [yamaha] Tuner band selection and preset feature for dual band models (RX-S601D), added + * config object */ -public class YamahaZoneThingHandler extends BaseThingHandler implements - ZoneControlStateListener, - NavigationControlStateListener, - PlayInfoStateListener, - AvailableInputStateListener, - PresetInfoStateListener, - DabBandStateListener { +public class YamahaZoneThingHandler extends BaseThingHandler + implements ZoneControlStateListener, NavigationControlStateListener, PlayInfoStateListener, + AvailableInputStateListener, PresetInfoStateListener, DabBandStateListener { private final Logger logger = LoggerFactory.getLogger(YamahaZoneThingHandler.class); private YamahaZoneConfig zoneConfig; /// ChannelType providers - protected ChannelsTypeProviderPreset channelsTypeProviderPreset; - protected ChannelsTypeProviderAvailableInputs channelsTypeProviderAvailableInputs; - private ServiceRegistration servicePreset; - private ServiceRegistration serviceAvailableInputs; + public @NonNullByDefault({}) ChannelsTypeProviderPreset channelsTypeProviderPreset; + public @NonNullByDefault({}) ChannelsTypeProviderAvailableInputs channelsTypeProviderAvailableInputs; /// State protected ZoneControlState zoneState = new ZoneControlState(); @@ -89,6 +122,13 @@ public YamahaZoneThingHandler(Thing thing) { super(thing); } + @Override + public Collection> getServices() { + return Collections + .unmodifiableList(Stream.of(ChannelsTypeProviderAvailableInputs.class, ChannelsTypeProviderPreset.class) + .collect(Collectors.toList())); + } + /** * Sets the {@link DeviceInformationState} for the handler. */ @@ -130,20 +170,13 @@ public void initialize() { logger.trace("Initialize {} with zone '{}'", getThing().getLabel(), zoneConfig.getZoneValue()); if (zoneConfig.getZone() == null) { - String msg = String.format("Zone not set or invalid zone name used: '%s'. It needs to be on of: '%s'", zoneConfig.getZoneValue(), Arrays.toString(Zone.values())); + String msg = String.format("Zone not set or invalid zone name used: '%s'. It needs to be on of: '%s'", + zoneConfig.getZoneValue(), Arrays.toString(Zone.values())); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, msg); logger.warn(msg); return; } - channelsTypeProviderPreset = new ChannelsTypeProviderPreset(thing.getUID()); - channelsTypeProviderAvailableInputs = new ChannelsTypeProviderAvailableInputs(thing.getUID()); - // Allow bundleContext to be null for tests - if (bundleContext != null) { - servicePreset = bundleContext.registerService(ChannelTypeProvider.class.getName(), channelsTypeProviderPreset, new Hashtable<>()); - serviceAvailableInputs = bundleContext.registerService(ChannelTypeProvider.class.getName(), channelsTypeProviderAvailableInputs, new Hashtable<>()); - } - YamahaBridgeHandler bridgeHandler = getBridgeHandler(); if (bridgeHandler != null) { bridgeStatusChanged(bridgeHandler.getThing().getStatusInfo()); @@ -152,16 +185,7 @@ public void initialize() { @Override public void dispose() { - if (serviceAvailableInputs != null) { - serviceAvailableInputs.unregister(); - channelsTypeProviderAvailableInputs = null; - serviceAvailableInputs = null; - } - if (servicePreset != null) { - servicePreset.unregister(); - channelsTypeProviderPreset = null; - servicePreset = null; - } + } protected YamahaBridgeHandler getBridgeHandler() { @@ -186,8 +210,10 @@ public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { if (zoneControl == null) { YamahaBridgeHandler brHandler = getBridgeHandler(); - zoneControl = getProtocolFactory().ZoneControl(getConnection(), zoneConfig, this, brHandler::getInputConverter, getDeviceInformationState()); - zoneAvailableInputs = getProtocolFactory().ZoneAvailableInputs(getConnection(), zoneConfig, this, brHandler::getInputConverter, getDeviceInformationState()); + zoneControl = getProtocolFactory().ZoneControl(getConnection(), zoneConfig, this, + brHandler::getInputConverter, getDeviceInformationState()); + zoneAvailableInputs = getProtocolFactory().ZoneAvailableInputs(getConnection(), zoneConfig, this, + brHandler::getInputConverter, getDeviceInformationState()); updateZoneInformation(); } @@ -263,7 +289,8 @@ public void handleCommand(ChannelUID channelUID, Command command) { zoneControl.setVolume(((DecimalType) command).floatValue()); } else if (command instanceof IncreaseDecreaseType) { zoneControl.setVolumeRelative(zoneState, - (((IncreaseDecreaseType) command) == IncreaseDecreaseType.INCREASE ? 1 : -1) * zoneConfig.getVolumeRelativeChangeFactor()); + (((IncreaseDecreaseType) command) == IncreaseDecreaseType.INCREASE ? 1 : -1) + * zoneConfig.getVolumeRelativeChangeFactor()); } break; case CHANNEL_MUTE: @@ -444,9 +471,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { */ private void refreshFromState(ChannelUID channelUID) { String id = channelUID.getId(); - if (id == null) { - return; - } + if (id.equals(grpZone(CHANNEL_POWER))) { updateState(channelUID, zoneState.power ? OnOffType.ON : OnOffType.OFF); @@ -547,7 +572,8 @@ private boolean isInputSupported(String inputID) { return getDeviceInformationState().features.contains(Feature.SPOTIFY); case INPUT_TUNER: - return getDeviceInformationState().features.contains(Feature.TUNER) || getDeviceInformationState().features.contains(Feature.DAB); + return getDeviceInformationState().features.contains(Feature.TUNER) + || getDeviceInformationState().features.contains(Feature.DAB); // Note: add more inputs here in the future } @@ -580,7 +606,8 @@ private void inputChangedCheckForNavigationControl() { return; } - inputWithNavigationControl = getProtocolFactory().InputWithNavigationControl(getConnection(), navigationInfoState, zoneState.inputID, this, getDeviceInformationState()); + inputWithNavigationControl = getProtocolFactory().InputWithNavigationControl(getConnection(), + navigationInfoState, zoneState.inputID, this, getDeviceInformationState()); updateAsyncMakeOfflineIfFail(inputWithNavigationControl); } @@ -612,13 +639,15 @@ private void inputChangedCheckForPlaybackControl() { } /** - * The {@link inputChangedCheckForDabBand} needs to be called first before this method, in case the AVR Supports DAB + * The {@link inputChangedCheckForDabBand} needs to be called first before this method, in case the AVR Supports + * DAB */ if (inputWithDabBandControl != null) { // When input is Tuner DAB there is no playback control inputWithPlayControl = null; } else { - inputWithPlayControl = getProtocolFactory().InputWithPlayControl(getConnection(), zoneState.inputID, this, getBridgeHandler().getConfiguration(), getDeviceInformationState()); + inputWithPlayControl = getProtocolFactory().InputWithPlayControl(getConnection(), zoneState.inputID, this, + getBridgeHandler().getConfiguration(), getDeviceInformationState()); updateAsyncMakeOfflineIfFail(inputWithPlayControl); } @@ -644,14 +673,17 @@ private void inputChangedCheckForPresetControl() { } /** - * The {@link inputChangedCheckForDabBand} needs to be called first before this method, in case the AVR Supports DAB + * The {@link inputChangedCheckForDabBand} needs to be called first before this method, in case the AVR Supports + * DAB */ if (inputWithDabBandControl != null) { // When the input is Tuner DAB the control also provides preset functionality inputWithPresetControl = (InputWithPresetControl) inputWithDabBandControl; - // Note: No need to update state - it will be already called for DabBand control (see inputChangedCheckForDabBand) + // Note: No need to update state - it will be already called for DabBand control (see + // inputChangedCheckForDabBand) } else { - inputWithPresetControl = getProtocolFactory().InputWithPresetControl(getConnection(), zoneState.inputID, this, getDeviceInformationState()); + inputWithPresetControl = getProtocolFactory().InputWithPresetControl(getConnection(), zoneState.inputID, + this, getDeviceInformationState()); updateAsyncMakeOfflineIfFail(inputWithPresetControl); } @@ -679,8 +711,8 @@ private void inputChangedCheckForDabBand() { } logger.debug("InputWithTunerBandControl created for {}", zoneState.inputID); - inputWithDabBandControl = getProtocolFactory().InputWithDabBandControl(zoneState.inputID, getConnection(), - this, this, this, getDeviceInformationState()); + inputWithDabBandControl = getProtocolFactory().InputWithDabBandControl(zoneState.inputID, getConnection(), this, + this, this, getDeviceInformationState()); updateAsyncMakeOfflineIfFail(inputWithDabBandControl); } @@ -711,10 +743,8 @@ public void availableInputsChanged(AvailableInputState msg) { // Remove the old channel and add the new channel. The channel will be requested from the // yamahaChannelTypeProvider. ChannelUID inputChannelUID = new ChannelUID(thing.getUID(), CHANNEL_GROUP_ZONE, CHANNEL_INPUT); - Channel channel = ChannelBuilder - .create(inputChannelUID, "String") - .withType(channelsTypeProviderAvailableInputs.getChannelTypeUID()) - .build(); + Channel channel = ChannelBuilder.create(inputChannelUID, "String") + .withType(channelsTypeProviderAvailableInputs.getChannelTypeUID()).build(); updateThing(editThing().withoutChannel(inputChannelUID).withChannel(channel).build()); } @@ -753,7 +783,8 @@ public void presetInfoUpdated(PresetInfoState msg) { // Remove the old channel and add the new channel. The channel will be requested from the // channelsTypeProviderPreset. - ChannelUID inputChannelUID = new ChannelUID(thing.getUID(), CHANNEL_GROUP_PLAYBACK, CHANNEL_PLAYBACK_PRESET); + ChannelUID inputChannelUID = new ChannelUID(thing.getUID(), CHANNEL_GROUP_PLAYBACK, + CHANNEL_PLAYBACK_PRESET); Channel channel = ChannelBuilder.create(inputChannelUID, "Number") .withType(channelsTypeProviderPreset.getChannelTypeUID()).build(); updateThing(editThing().withoutChannel(inputChannelUID).withChannel(channel).build()); diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/AbstractConnection.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/AbstractConnection.java similarity index 99% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/AbstractConnection.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/AbstractConnection.java index 26285a54ac9a0..ad6825d721e2e 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/AbstractConnection.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/AbstractConnection.java @@ -26,7 +26,6 @@ * * @author David Graeff - Initial contribution * @author Tomasz Maruszak - Refactoring - * */ public abstract class AbstractConnection { private Logger logger = LoggerFactory.getLogger(AbstractConnection.class); diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ConnectionStateListener.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ConnectionStateListener.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ConnectionStateListener.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ConnectionStateListener.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/DeviceInformation.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/DeviceInformation.java similarity index 99% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/DeviceInformation.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/DeviceInformation.java index 911cbc75b3077..8c473fc2d6f62 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/DeviceInformation.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/DeviceInformation.java @@ -17,7 +17,6 @@ * * @author David Graeff - Initial contribution */ - public interface DeviceInformation extends IStateUpdatable { } diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/IStateUpdatable.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/IStateUpdatable.java similarity index 99% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/IStateUpdatable.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/IStateUpdatable.java index 27d82bd06a5ea..88abf5c5a6d8d 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/IStateUpdatable.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/IStateUpdatable.java @@ -18,7 +18,6 @@ * To offer a method to retrieve a specific state, a protocol part would extend this interface. * * @author David Graeff - Initial contribution - * */ public interface IStateUpdatable { /** diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputConverter.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputConverter.java similarity index 98% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputConverter.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputConverter.java index 4b7b8219411d4..1c96dd60bf7b1 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputConverter.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputConverter.java @@ -13,7 +13,6 @@ package org.openhab.binding.yamahareceiver.internal.protocol; /** - * * Performs conversion logic between canonical input names and underlying Yamaha protocol. * * For example, AVRs when setting input 'AUDIO_X' (or HDMI_X) need the input to be sent in this form. @@ -25,6 +24,7 @@ public interface InputConverter { /** * Converts the canonical input name to name used by the protocol + * * @param name canonical name * @return command name */ @@ -32,6 +32,7 @@ public interface InputConverter { /** * Converts the state name used by the protocol to canonical input name + * * @param name state name * @return canonical name */ diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithNavigationControl.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithNavigationControl.java similarity index 86% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithNavigationControl.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithNavigationControl.java index 48501accb5bfc..23500b29c4f05 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithNavigationControl.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithNavigationControl.java @@ -12,39 +12,27 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol; +import static java.util.stream.Collectors.toSet; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.*; + import java.io.IOException; import java.util.Set; import java.util.stream.Stream; -import static java.util.stream.Collectors.toSet; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.*; - /** * The navigation control protocol interface * * @author David Graeff - Initial contribution * @author Tomasz Maruszak - refactoring */ - public interface InputWithNavigationControl extends IStateUpdatable { /** * List all inputs that are compatible with this kind of control */ Set SUPPORTED_INPUTS = Stream - .of( - INPUT_NET_RADIO, - INPUT_NET_RADIO_LEGACY, - INPUT_USB, - INPUT_IPOD_USB, - INPUT_DOCK, - INPUT_PC, - INPUT_NAPSTER, - INPUT_PANDORA, - INPUT_SIRIUS, - INPUT_RHAPSODY, - INPUT_IPOD, - INPUT_HD_RADIO - ).collect(toSet()); + .of(INPUT_NET_RADIO, INPUT_NET_RADIO_LEGACY, INPUT_USB, INPUT_IPOD_USB, INPUT_DOCK, INPUT_PC, INPUT_NAPSTER, + INPUT_PANDORA, INPUT_SIRIUS, INPUT_RHAPSODY, INPUT_IPOD, INPUT_HD_RADIO) + .collect(toSet()); /** * Navigate back diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithPlayControl.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithPlayControl.java similarity index 83% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithPlayControl.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithPlayControl.java index 3bdaf99702916..b51939462ae86 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithPlayControl.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithPlayControl.java @@ -12,36 +12,26 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol; +import static java.util.stream.Collectors.toSet; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.*; + import java.io.IOException; import java.util.Set; import java.util.stream.Stream; -import static java.util.stream.Collectors.toSet; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.*; - /** * The play controls protocol interface * * @author David Graeff - Initial contribution * @author Tomasz Maruszak - Spotify support, adding Server to supported preset inputs */ - public interface InputWithPlayControl extends IStateUpdatable { /** * List all inputs that are compatible with this kind of control */ - Set SUPPORTED_INPUTS = Stream - .of( - INPUT_NET_RADIO, - INPUT_NET_RADIO_LEGACY, - INPUT_USB, INPUT_IPOD_USB, INPUT_IPOD, - INPUT_DOCK, INPUT_PC, - INPUT_NAPSTER, INPUT_PANDORA, INPUT_SIRIUS, INPUT_RHAPSODY, - INPUT_BLUETOOTH, - INPUT_SPOTIFY, - INPUT_SERVER, - INPUT_HD_RADIO - ).collect(toSet()); + Set SUPPORTED_INPUTS = Stream.of(INPUT_NET_RADIO, INPUT_NET_RADIO_LEGACY, INPUT_USB, INPUT_IPOD_USB, + INPUT_IPOD, INPUT_DOCK, INPUT_PC, INPUT_NAPSTER, INPUT_PANDORA, INPUT_SIRIUS, INPUT_RHAPSODY, + INPUT_BLUETOOTH, INPUT_SPOTIFY, INPUT_SERVER, INPUT_HD_RADIO).collect(toSet()); /** * Start the playback of the content which is usually selected by the means of the Navigation control class or diff --git a/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithPresetControl.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithPresetControl.java new file mode 100644 index 0000000000000..17d5d4615c95c --- /dev/null +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithPresetControl.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.yamahareceiver.internal.protocol; + +import static java.util.stream.Collectors.toSet; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.*; + +import java.io.IOException; +import java.util.Set; +import java.util.stream.Stream; + +/** + * The preset control protocol interface + * + * @author David Graeff - Initial contribution + * @author Tomasz Maruszak - Adding Spotify, Server to supported preset inputs + */ +public interface InputWithPresetControl extends IStateUpdatable { + + int PRESET_CHANNELS = 40; + + /** + * List all inputs that are compatible with this kind of control + */ + Set SUPPORTED_INPUTS = Stream.of(INPUT_TUNER, INPUT_NET_RADIO, INPUT_NET_RADIO_LEGACY, INPUT_USB, + INPUT_IPOD, INPUT_IPOD_USB, INPUT_DOCK, INPUT_PC, INPUT_NAPSTER, INPUT_PANDORA, INPUT_SIRIUS, + INPUT_RHAPSODY, INPUT_BLUETOOTH, INPUT_SPOTIFY, INPUT_SERVER, INPUT_HD_RADIO).collect(toSet()); + + /** + * Select a preset channel. + * + * @param presetChannel The preset position [1,40] + * @throws Exception + */ + void selectItemByPresetNumber(int presetChannel) throws IOException, ReceivedMessageParseException; +} diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithTunerBandControl.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithTunerBandControl.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithTunerBandControl.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithTunerBandControl.java index b8423eb37d608..17959db54882a 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithTunerBandControl.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/InputWithTunerBandControl.java @@ -12,13 +12,13 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol; +import static java.util.stream.Collectors.toSet; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.INPUT_TUNER; + import java.io.IOException; import java.util.Set; import java.util.stream.Stream; -import static java.util.stream.Collectors.toSet; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.INPUT_TUNER; - /** * The DAB Band control protocol interface. * diff --git a/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ProtocolFactory.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ProtocolFactory.java new file mode 100644 index 0000000000000..2715a3b7b1e2d --- /dev/null +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ProtocolFactory.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.yamahareceiver.internal.protocol; + +import java.util.function.Supplier; + +import org.openhab.binding.yamahareceiver.internal.config.YamahaBridgeConfig; +import org.openhab.binding.yamahareceiver.internal.config.YamahaZoneConfig; +import org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConnection; +import org.openhab.binding.yamahareceiver.internal.state.AvailableInputStateListener; +import org.openhab.binding.yamahareceiver.internal.state.DabBandStateListener; +import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState; +import org.openhab.binding.yamahareceiver.internal.state.NavigationControlState; +import org.openhab.binding.yamahareceiver.internal.state.NavigationControlStateListener; +import org.openhab.binding.yamahareceiver.internal.state.PlayInfoStateListener; +import org.openhab.binding.yamahareceiver.internal.state.PresetInfoStateListener; +import org.openhab.binding.yamahareceiver.internal.state.SystemControlStateListener; +import org.openhab.binding.yamahareceiver.internal.state.ZoneControlStateListener; + +/** + * Factory to create a {@link AbstractConnection} connection object based on a feature test. + * Also returns implementation objects for all the protocol interfaces. + *

+ * At the moment only the XML protocol is supported. + * + * @author David Graeff - Initial contribution + * @author Tomasz Maruszak - Input mapping fix, refactoring + */ +public interface ProtocolFactory { + /** + * Asynchronous method to create and return a connection object. Depending + * on the feature test it might be either a {@link XMLConnection} or a JsonConnection. + * + * @param host The host name + * @param connectionStateListener + */ + void createConnection(String host, ConnectionStateListener connectionStateListener); + + SystemControl SystemControl(AbstractConnection connection, SystemControlStateListener listener, + DeviceInformationState deviceInformationState); + + InputWithPlayControl InputWithPlayControl(AbstractConnection connection, String currentInputID, + PlayInfoStateListener listener, YamahaBridgeConfig settings, DeviceInformationState deviceInformationState); + + InputWithPresetControl InputWithPresetControl(AbstractConnection connection, String currentInputID, + PresetInfoStateListener listener, DeviceInformationState deviceInformationState); + + InputWithTunerBandControl InputWithDabBandControl(String currentInputID, AbstractConnection connection, + DabBandStateListener observerForBand, PresetInfoStateListener observerForPreset, + PlayInfoStateListener observerForPlayInfo, DeviceInformationState deviceInformationState); + + InputWithNavigationControl InputWithNavigationControl(AbstractConnection connection, NavigationControlState state, + String inputID, NavigationControlStateListener observer, DeviceInformationState deviceInformationState); + + ZoneControl ZoneControl(AbstractConnection connection, YamahaZoneConfig zoneSettings, + ZoneControlStateListener listener, Supplier inputConverterSupplier, + DeviceInformationState deviceInformationState); + + ZoneAvailableInputs ZoneAvailableInputs(AbstractConnection connection, YamahaZoneConfig zoneSettings, + AvailableInputStateListener listener, Supplier inputConverterSupplier, + DeviceInformationState deviceInformationState); + + DeviceInformation DeviceInformation(AbstractConnection connection, DeviceInformationState state); + + InputConverter InputConverter(AbstractConnection connection, String setting); +} diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ReceivedMessageParseException.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ReceivedMessageParseException.java similarity index 99% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ReceivedMessageParseException.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ReceivedMessageParseException.java index dccb1ed4f8527..6e0b47313ff58 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ReceivedMessageParseException.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ReceivedMessageParseException.java @@ -18,7 +18,6 @@ * * @author David Graeff - Initial contribution */ - public class ReceivedMessageParseException extends Exception { private static final long serialVersionUID = 2703218443322787635L; diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/SystemControl.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/SystemControl.java similarity index 98% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/SystemControl.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/SystemControl.java index dda26164f759c..c629308fd501c 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/SystemControl.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/SystemControl.java @@ -19,7 +19,6 @@ * * @author David Graeff - Initial contribution */ - public interface SystemControl extends IStateUpdatable { /** * Switches the AVR on/off (off equals network standby here). @@ -33,6 +32,7 @@ public interface SystemControl extends IStateUpdatable { /** * Enables party mode. + * * @param on * @throws IOException * @throws ReceivedMessageParseException @@ -41,6 +41,7 @@ public interface SystemControl extends IStateUpdatable { /** * Enables mute for party mode. + * * @param on * @throws IOException * @throws ReceivedMessageParseException @@ -49,6 +50,7 @@ public interface SystemControl extends IStateUpdatable { /** * Increment or decrement the volume for party mode. + * * @param increment * @throws IOException * @throws ReceivedMessageParseException diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ZoneAvailableInputs.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ZoneAvailableInputs.java similarity index 99% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ZoneAvailableInputs.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ZoneAvailableInputs.java index ba848819a44f1..f0ef97ad627be 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ZoneAvailableInputs.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ZoneAvailableInputs.java @@ -17,6 +17,5 @@ * * @author David Graeff - Initial contribution */ - public interface ZoneAvailableInputs extends IStateUpdatable { } diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ZoneControl.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ZoneControl.java similarity index 99% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ZoneControl.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ZoneControl.java index 16e03b4b59e9c..974969dd225c5 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ZoneControl.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/ZoneControl.java @@ -21,7 +21,6 @@ * * @author David Graeff - Initial contribution */ - public interface ZoneControl extends IStateUpdatable { /** * Switches the zone on/off (off equals network standby here). @@ -67,6 +66,7 @@ public interface ZoneControl extends IStateUpdatable { /** * Sets the active scene for the zone. + * * @param scene * @throws IOException * @throws ReceivedMessageParseException diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractInputControlXML.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractInputControlXML.java similarity index 97% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractInputControlXML.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractInputControlXML.java index c81974e63c68a..f048552514e7b 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractInputControlXML.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractInputControlXML.java @@ -12,18 +12,18 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; -import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Feature; -import org.openhab.binding.yamahareceiver.internal.config.YamahaUtils; -import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; -import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState; -import org.slf4j.Logger; - import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.*; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; +import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Feature; +import org.openhab.binding.yamahareceiver.internal.config.YamahaUtils; +import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; +import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState; +import org.slf4j.Logger; + /** * Provides basis for all input controls * @@ -52,7 +52,8 @@ private Map loadMapping() { return map; } - protected AbstractInputControlXML(Logger logger, String inputID, AbstractConnection con, DeviceInformationState deviceInformationState) { + protected AbstractInputControlXML(Logger logger, String inputID, AbstractConnection con, + DeviceInformationState deviceInformationState) { this.logger = logger; this.comReference = new WeakReference<>(con); this.inputID = inputID; diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/CommandTemplate.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/CommandTemplate.java similarity index 89% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/CommandTemplate.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/CommandTemplate.java index 8b66d46faacc8..fc92ff8b29190 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/CommandTemplate.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/CommandTemplate.java @@ -32,9 +32,7 @@ public CommandTemplate(String command) { } public CommandTemplate replace(String oldToken, String newToken) { - return new CommandTemplate( - command.replace(oldToken, newToken), - path.replace(oldToken, newToken)); + return new CommandTemplate(command.replace(oldToken, newToken), path.replace(oldToken, newToken)); } public String apply(Object... args) { diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceDescriptorXML.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceDescriptorXML.java similarity index 89% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceDescriptorXML.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceDescriptorXML.java index 5459a0c91ca1b..981a3a949fe7d 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceDescriptorXML.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceDescriptorXML.java @@ -12,6 +12,20 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; +import static java.util.stream.Collectors.*; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.getChildElements; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + import org.apache.commons.lang.StringUtils; import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Feature; import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone; @@ -23,15 +37,6 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; -import java.io.IOException; -import java.util.*; -import java.util.function.*; -import java.util.stream.Stream; - -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toSet; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.getChildElements; - /** * * Represents device descriptor for XML protocol @@ -61,6 +66,7 @@ public String getUnitName() { /** * Checks if the condition is met, on false result calls the runnable. + * * @param predicate * @param falseAction * @return @@ -73,7 +79,6 @@ public boolean hasFeature(Predicate predicate, Runnable fal return result; } - public static abstract class HasCommands { public final Set commands; @@ -81,7 +86,8 @@ public static abstract class HasCommands { public HasCommands(Element element) { Element cmdList = (Element) XMLUtils.getNode(element, "Cmd_List"); if (cmdList != null) { - commands = XMLUtils.toStream(cmdList.getElementsByTagName("Define")).map(x -> x.getTextContent()).collect(toSet()); + commands = XMLUtils.toStream(cmdList.getElementsByTagName("Define")).map(x -> x.getTextContent()) + .collect(toSet()); } else { commands = new HashSet<>(); } @@ -97,6 +103,7 @@ public boolean hasAnyCommandEnding(String... anyCommand) { /** * Checks if the command is available, on false result calls the runnable. + * * @param command * @param falseAction * @return @@ -146,6 +153,7 @@ public FeatureDescriptor(Feature feature, Element element) { /** * Get the descriptor XML from the AVR and parse + * * @param con */ public void load(XMLConnection con) { @@ -155,13 +163,10 @@ public void load(XMLConnection con) { unitName = descNode.getAttributes().getNamedItem("Unit_Name").getTextContent(); - system = buildFeatureLookup(descNode, "Unit", - tag -> tag, - (tag, e) -> new SystemDescriptor(e)) + system = buildFeatureLookup(descNode, "Unit", tag -> tag, (tag, e) -> new SystemDescriptor(e)) .getOrDefault("System", null); // there will be only one System entry - zones = buildFeatureLookup(descNode, "Subunit", - tag -> YamahaUtils.tryParseEnum(Zone.class, tag), + zones = buildFeatureLookup(descNode, "Subunit", tag -> YamahaUtils.tryParseEnum(Zone.class, tag), (zone, e) -> new ZoneDescriptor(zone, e)); features = buildFeatureLookup(descNode, "Source_Device", @@ -173,11 +178,12 @@ public void load(XMLConnection con) { /** * Tires to get the XML descriptor for the AVR + * * @param con * @return */ private Node tryGetDescriptor(XMLConnection con) { - for (String path: Arrays.asList("/YamahaRemoteControl/desc.xml", "/YamahaRemoteControl/UnitDesc.xml")) { + for (String path : Arrays.asList("/YamahaRemoteControl/desc.xml", "/YamahaRemoteControl/UnitDesc.xml")) { try { String descXml = con.getResponse(path); Document doc = XMLUtils.xml(descXml); @@ -200,7 +206,8 @@ private Node tryGetDescriptor(XMLConnection con) { return null; } - private Map buildFeatureLookup(Node descNode, String funcValue, Function converter, BiFunction factory) { + private Map buildFeatureLookup(Node descNode, String funcValue, Function converter, + BiFunction factory) { Map groupedElements = new HashMap<>(); if (descNode != null) { diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceInformationXML.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceInformationXML.java similarity index 79% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceInformationXML.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceInformationXML.java index 467053d827d77..b28fd803e3972 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceInformationXML.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceInformationXML.java @@ -12,6 +12,15 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone.*; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.Commands.*; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.*; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.*; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.Set; + import org.apache.commons.lang.StringUtils; import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Feature; import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone; @@ -24,24 +33,12 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Node; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.Set; - -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone.Main_Zone; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone.Zone_2; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.Commands.*; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.getResponse; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.getZoneResponse; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.getNode; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.getNodeContentOrEmpty; - /** * The system control protocol class is used to control basic non-zone functionality * of a Yamaha receiver with HTTP/xml. * No state will be saved in here, but in {@link SystemControlState} instead. * - * @author David Gräff + * @author David Graeff - Initial contribution * @author Tomasz Maruszak - DAB support, Spotify support, better feature detection */ public class DeviceInformationXML implements DeviceInformation { @@ -60,26 +57,26 @@ public DeviceInformationXML(AbstractConnection com, DeviceInformationState state * * Example: * - * 1 - * 1 - * 0 - * 0 - * 0 - * 1 - * 0 - * 0 - * 0 - * 0 - * 1 - * 0 - * 1 - * 1 - * 1 - * 1 - * 1 - * 1 - * 1 - * 1 + * 1 + * 1 + * 0 + * 0 + * 0 + * 1 + * 0 + * 0 + * 0 + * 0 + * 1 + * 0 + * 1 + * 1 + * 1 + * 1 + * 1 + * 1 + * 1 + * 1 * * * @throws IOException @@ -110,7 +107,8 @@ public void update() throws IOException, ReceivedMessageParseException { checkFeature(featureNode, zone.toString(), zone, state.zones); } - XMLConstants.FEATURE_BY_YNC_TAG.forEach((name, feature) -> checkFeature(featureNode, name, feature, state.features)); + XMLConstants.FEATURE_BY_YNC_TAG + .forEach((name, feature) -> checkFeature(featureNode, name, feature, state.features)); } else { // on older models (RX-V3900) the Feature_Existence element does not exist @@ -121,11 +119,12 @@ public void update() throws IOException, ReceivedMessageParseException { detectZoneBSupport(con); - logger.info("Found zones: {}, features: {}", state.zones, state.features); + logger.debug("Found zones: {}, features: {}", state.zones, state.features); } /** * Detect if Zone_B is supported (HTR-4069). This will allow Zone_2 to be emulated by the Zone_B feature. + * * @param con * @throws IOException * @throws ReceivedMessageParseException @@ -138,7 +137,7 @@ private void detectZoneBSupport(XMLConnection con) throws IOException, ReceivedM Node basicStatusNode = getZoneResponse(con, Main_Zone, ZONE_BASIC_STATUS_CMD, ZONE_BASIC_STATUS_PATH); String power = getNodeContentOrEmpty(basicStatusNode, "Power_Control/Zone_B_Power_Info"); if (StringUtils.isNotEmpty(power)) { - logger.info("Zone_2 emulation enabled via Zone_B"); + logger.debug("Zone_2 emulation enabled via Zone_B"); state.zones.add(Zone_2); state.features.add(Feature.ZONE_B); } diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputConverterXML.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputConverterXML.java similarity index 89% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputConverterXML.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputConverterXML.java index cddb29127577f..be9bc8abee0b9 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputConverterXML.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputConverterXML.java @@ -12,22 +12,24 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; +import static java.util.stream.Collectors.toSet; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.*; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + import org.apache.commons.lang.StringUtils; +import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone; import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; import org.openhab.binding.yamahareceiver.internal.protocol.InputConverter; import org.openhab.binding.yamahareceiver.internal.protocol.ReceivedMessageParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.*; -import java.util.stream.Stream; - -import static java.util.stream.Collectors.toSet; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.*; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.*; - /** * XML implementation of {@link InputConverter}. * @@ -83,7 +85,8 @@ private Map createMapFromSetting(String setting) { String value = keyValue[1]; if (map.putIfAbsent(key, value) != null) { - logger.warn("Invalid setting: {} entry: {} - key: {} was already provided before", setting, entry, key); + logger.warn("Invalid setting: {} entry: {} - key: {} was already provided before", setting, + entry, key); } } } @@ -93,23 +96,16 @@ private Map createMapFromSetting(String setting) { private Set createInputsWithoutMapping() throws IOException, ReceivedMessageParseException { // Tested on RX-S601D, RX-V479 - Set inputsWithoutMapping = Stream - .of(INPUT_SPOTIFY, INPUT_BLUETOOTH) - .collect(toSet()); + Set inputsWithoutMapping = Stream.of(INPUT_SPOTIFY, INPUT_BLUETOOTH).collect(toSet()); - Set nativeInputNames = XMLProtocolService.getInputs(comReference.get(), Zone.Main_Zone) - .stream() - .filter(x -> x.isWritable()) - .map(x -> x.getParam()) - .collect(toSet()); + Set nativeInputNames = XMLProtocolService.getInputs(comReference.get(), Zone.Main_Zone).stream() + .filter(x -> x.isWritable()).map(x -> x.getParam()).collect(toSet()); // When native input returned matches any of 'HDMIx', 'AUDIOx' or 'NET RADIO', ensure no conversion happens. // Tested on RX-S601D, RX-V479 nativeInputNames.stream() - .filter(x -> startsWithAndLength(x, "HDMI", 1) - || startsWithAndLength(x, "AUDIO", 1) - || x.equals(INPUT_NET_RADIO) - || x.equals(INPUT_MUSIC_CAST_LINK)) + .filter(x -> startsWithAndLength(x, "HDMI", 1) || startsWithAndLength(x, "AUDIO", 1) + || x.equals(INPUT_NET_RADIO) || x.equals(INPUT_MUSIC_CAST_LINK)) .forEach(x -> inputsWithoutMapping.add(x)); return inputsWithoutMapping; diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithNavigationControlXML.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithNavigationControlXML.java similarity index 94% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithNavigationControlXML.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithNavigationControlXML.java index e6d2bea1ec4b7..fa47cf72e96a9 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithNavigationControlXML.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithNavigationControlXML.java @@ -62,11 +62,8 @@ public class InputWithNavigationControlXML extends AbstractInputControlXML imple * @param inputID The input ID like USB or NET_RADIO. * @param con The Yamaha communication object to send http requests. */ - public InputWithNavigationControlXML(NavigationControlState state, - String inputID, - AbstractConnection con, - NavigationControlStateListener observer, - DeviceInformationState deviceInformationState) { + public InputWithNavigationControlXML(NavigationControlState state, String inputID, AbstractConnection con, + NavigationControlStateListener observer, DeviceInformationState deviceInformationState) { super(LoggerFactory.getLogger(InputWithNavigationControlXML.class), inputID, con, deviceInformationState); @@ -76,6 +73,7 @@ public InputWithNavigationControlXML(NavigationControlState state, /** * Sends a cursor command to Yamaha. + * * @param command * @throws IOException * @throws ReceivedMessageParseException @@ -95,7 +93,6 @@ public void goBack() throws IOException, ReceivedMessageParseException { navigateCursor("Back"); } - /** * Navigate up * @@ -162,7 +159,8 @@ public boolean goToRoot() throws IOException, ReceivedMessageParseException { } else { navigateCursor("Back to Home"); if (state.menuLayer > 0) { - observer.navigationError("The going back to root command failed for your receiver. Trying to use a different command."); + observer.navigationError( + "The going back to root command failed for your receiver. Trying to use a different command."); useAlternativeBackToHomeCmd = true; return goToRoot(); } @@ -258,7 +256,8 @@ private boolean selectItem(String name) throws IOException, ReceivedMessageParse int index = findItemOnCurrentPage(name); if (index > 0) { - com.send(wrInput("Line_" + String.valueOf(index) + "")); + com.send(wrInput( + "Line_" + String.valueOf(index) + "")); update(); return true; } @@ -327,7 +326,8 @@ public void update() throws IOException, ReceivedMessageParseException { state.maxLine = maxLines; for (int i = 1; i < 8; ++i) { - state.items[i - 1] = XMLUtils.getNodeContentOrDefault(currentMenu, "Current_List/Line_" + i + "/Txt", (String) null); + state.items[i - 1] = XMLUtils.getNodeContentOrDefault(currentMenu, "Current_List/Line_" + i + "/Txt", + (String) null); } if (observer != null) { diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPlayControlXML.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPlayControlXML.java similarity index 79% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPlayControlXML.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPlayControlXML.java index e95b0f23eed35..83c89877fc01d 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPlayControlXML.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPlayControlXML.java @@ -12,26 +12,25 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.INPUT_SPOTIFY; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.Commands.PLAYBACK_STATUS_CMD; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.getResponse; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.*; + +import java.io.IOException; + import org.apache.commons.lang.StringUtils; +import org.openhab.binding.yamahareceiver.internal.config.YamahaBridgeConfig; import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; import org.openhab.binding.yamahareceiver.internal.protocol.InputWithPlayControl; import org.openhab.binding.yamahareceiver.internal.protocol.ReceivedMessageParseException; -import org.openhab.binding.yamahareceiver.internal.config.YamahaBridgeConfig; import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState; import org.openhab.binding.yamahareceiver.internal.state.PlayInfoState; import org.openhab.binding.yamahareceiver.internal.state.PlayInfoStateListener; import org.openhab.binding.yamahareceiver.internal.state.PresetInfoState; -import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; -import java.io.IOException; - -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Inputs.*; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.Commands.PLAYBACK_STATUS_CMD; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.getResponse; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.*; - /** * This class implements the Yamaha Receiver protocol related to navigation functionally. USB, NET_RADIO, IPOD and * other inputs are using the same way of playback control. @@ -55,7 +54,8 @@ public class InputWithPlayControlXML extends AbstractInputControlXML implements private final PlayInfoStateListener observer; private final YamahaBridgeConfig bridgeConfig; - protected CommandTemplate playCmd = new CommandTemplate("%s", "Play_Info/Playback_Info"); + protected CommandTemplate playCmd = new CommandTemplate("%s", + "Play_Info/Playback_Info"); protected CommandTemplate skipCmd = new CommandTemplate("%s"); protected String skipForwardValue = "Skip Fwd"; protected String skipBackwardValue = "Skip Rev"; @@ -65,13 +65,10 @@ public class InputWithPlayControlXML extends AbstractInputControlXML implements * as controlling the playback and choosing a preset item. * * @param inputID The input ID like USB or NET_RADIO. - * @param com The Yamaha communication object to send http requests. + * @param com The Yamaha communication object to send http requests. */ - public InputWithPlayControlXML(String inputID, - AbstractConnection com, - PlayInfoStateListener observer, - YamahaBridgeConfig bridgeConfig, - DeviceInformationState deviceInformationState) { + public InputWithPlayControlXML(String inputID, AbstractConnection com, PlayInfoStateListener observer, + YamahaBridgeConfig bridgeConfig, DeviceInformationState deviceInformationState) { super(LoggerFactory.getLogger(InputWithPlayControlXML.class), inputID, com, deviceInformationState); this.observer = observer; @@ -204,29 +201,29 @@ public void update() throws IOException, ReceivedMessageParseException { return; } - // - // - // - // Ready - // Play - // - // Way Out West - // Tuesday Maybe - // Tuesday Maybe - // - // - // /YamahaRemoteControl/AlbumART/AlbumART3929.jpg - // 39290 - // JPEG - // - // - // /YamahaRemoteControl/Logos/logo005.png - // - // - // - // - // - // + // + // + // + // Ready + // Play + // + // Way Out West + // Tuesday Maybe + // Tuesday Maybe + // + // + // /YamahaRemoteControl/AlbumART/AlbumART3929.jpg + // 39290 + // JPEG + // + // + // /YamahaRemoteControl/Logos/logo005.png + // + // + // + // + // + // AbstractConnection con = comReference.get(); Node node = getResponse(con, wrInput(PLAYBACK_STATUS_CMD), inputElement); @@ -236,10 +233,14 @@ public void update() throws IOException, ReceivedMessageParseException { msg.playbackMode = getNodeContentOrDefault(node, playCmd.getPath(), msg.playbackMode); // elements for these are named differently per model and per input, so we try to match any known element - msg.station = getAnyNodeContentOrDefault(node, msg.station, "Play_Info/Meta_Info/Radio_Text_A", "Play_Info/Meta_Info/Station", "Play_Info/RDS/Program_Service"); - msg.artist = getAnyNodeContentOrDefault(node, msg.artist, "Play_Info/Meta_Info/Artist", "Play_Info/Title/Artist", "Play_Info/RDS/Radio_Text_A"); - msg.album = getAnyNodeContentOrDefault(node, msg.album, "Play_Info/Meta_Info/Album", "Play_Info/Title/Album", "Play_Info/RDS/Program_Type"); - msg.song = getAnyNodeContentOrDefault(node, msg.song, "Play_Info/Meta_Info/Track", "Play_Info/Meta_Info/Song", "Play_Info/Title/Song", "Play_Info/RDS/Radio_Text_B"); + msg.station = getAnyNodeContentOrDefault(node, msg.station, "Play_Info/Meta_Info/Radio_Text_A", + "Play_Info/Meta_Info/Station", "Play_Info/RDS/Program_Service"); + msg.artist = getAnyNodeContentOrDefault(node, msg.artist, "Play_Info/Meta_Info/Artist", + "Play_Info/Title/Artist", "Play_Info/RDS/Radio_Text_A"); + msg.album = getAnyNodeContentOrDefault(node, msg.album, "Play_Info/Meta_Info/Album", "Play_Info/Title/Album", + "Play_Info/RDS/Program_Type"); + msg.song = getAnyNodeContentOrDefault(node, msg.song, "Play_Info/Meta_Info/Track", "Play_Info/Meta_Info/Song", + "Play_Info/Title/Song", "Play_Info/RDS/Radio_Text_B"); // Spotify and NET RADIO input supports song cover image (at least on RX-S601D) String songImageUrl = getNodeContentOrEmpty(node, "Play_Info/Album_ART/URL"); @@ -247,8 +248,8 @@ public void update() throws IOException, ReceivedMessageParseException { ? String.format("http://%s%s", con.getHost(), songImageUrl) : bridgeConfig.getAlbumUrl(); - logger.trace("Playback: {}, Station: {}, Artist: {}, Album: {}, Song: {}, SongImageUrl: {}", - msg.playbackMode, msg.station, msg.artist, msg.album, msg.song, msg.songImageUrl); + logger.trace("Playback: {}, Station: {}, Artist: {}, Album: {}, Song: {}, SongImageUrl: {}", msg.playbackMode, + msg.station, msg.artist, msg.album, msg.song, msg.songImageUrl); observer.playInfoUpdated(msg); } diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPresetControlXML.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPresetControlXML.java similarity index 92% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPresetControlXML.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPresetControlXML.java index ef0eb082408fe..90afaba8cb242 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPresetControlXML.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPresetControlXML.java @@ -12,6 +12,12 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.GET_PARAM; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.getResponse; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.*; + +import java.io.IOException; + import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; @@ -24,12 +30,6 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Node; -import java.io.IOException; - -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.GET_PARAM; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.getResponse; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.*; - /** * This class implements the Yamaha Receiver protocol related to navigation functionally. USB, NET_RADIO, IPOD and * other inputs are using the same way of playback control. @@ -50,7 +50,9 @@ */ public class InputWithPresetControlXML extends AbstractInputControlXML implements InputWithPresetControl { - protected CommandTemplate preset = new CommandTemplate("%s", "Play_Control/Preset/Preset_Sel"); + protected CommandTemplate preset = new CommandTemplate( + "%s", + "Play_Control/Preset/Preset_Sel"); private final PresetInfoStateListener observer; @@ -61,10 +63,8 @@ public class InputWithPresetControlXML extends AbstractInputControlXML implement * @param inputID The input ID like USB or NET_RADIO. * @param con The Yamaha communication object to send http requests. */ - public InputWithPresetControlXML(String inputID, - AbstractConnection con, - PresetInfoStateListener observer, - DeviceInformationState deviceInformationState) { + public InputWithPresetControlXML(String inputID, AbstractConnection con, PresetInfoStateListener observer, + DeviceInformationState deviceInformationState) { super(LoggerFactory.getLogger(InputWithPresetControlXML.class), inputID, con, deviceInformationState); this.observer = observer; @@ -89,13 +89,16 @@ protected void applyModelVariations() { * * @throws Exception */ + @Override public void update() throws IOException, ReceivedMessageParseException { if (observer == null) { return; } AbstractConnection con = comReference.get(); - Node response = getResponse(con, wrInput("GetParam"), inputElement); + Node response = getResponse(con, + wrInput("GetParam"), + inputElement); PresetInfoState msg = new PresetInfoState(); @@ -166,6 +169,7 @@ private int convertToPresetNumber(String presetValue) { * @param presetChannel The preset position [1,40] * @throws Exception */ + @Override public void selectItemByPresetNumber(int presetChannel) throws IOException, ReceivedMessageParseException { String presetValue; diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithTunerDABControlXML.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithTunerDABControlXML.java similarity index 85% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithTunerDABControlXML.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithTunerDABControlXML.java index cc3cdfeedac3d..7c2b6501ac48f 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithTunerDABControlXML.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithTunerDABControlXML.java @@ -12,21 +12,25 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.*; + +import java.io.IOException; + import org.apache.commons.lang.StringUtils; import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; -import org.openhab.binding.yamahareceiver.internal.protocol.InputWithTunerBandControl; import org.openhab.binding.yamahareceiver.internal.protocol.InputWithPresetControl; +import org.openhab.binding.yamahareceiver.internal.protocol.InputWithTunerBandControl; import org.openhab.binding.yamahareceiver.internal.protocol.ReceivedMessageParseException; -import org.openhab.binding.yamahareceiver.internal.state.*; -import org.slf4j.Logger; +import org.openhab.binding.yamahareceiver.internal.state.DabBandState; +import org.openhab.binding.yamahareceiver.internal.state.DabBandStateListener; +import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState; +import org.openhab.binding.yamahareceiver.internal.state.PlayInfoState; +import org.openhab.binding.yamahareceiver.internal.state.PlayInfoStateListener; +import org.openhab.binding.yamahareceiver.internal.state.PresetInfoState; +import org.openhab.binding.yamahareceiver.internal.state.PresetInfoStateListener; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; -import java.io.IOException; - -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.getNode; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.getNodeContentOrDefault; - /** * This class implements the Yamaha Receiver protocol related to DAB tuners which allows to control band and preset. * This control is specific to dual band tuners only. @@ -40,7 +44,8 @@ * * @author Tomasz Maruszak - [yamaha] Tuner band selection and preset feature for dual band models (RX-S601D) */ -public class InputWithTunerDABControlXML extends AbstractInputControlXML implements InputWithTunerBandControl, InputWithPresetControl { +public class InputWithTunerDABControlXML extends AbstractInputControlXML + implements InputWithTunerBandControl, InputWithPresetControl { private static final String BAND_FM = "FM"; private static final String BAND_DAB = "DAB"; @@ -49,8 +54,10 @@ public class InputWithTunerDABControlXML extends AbstractInputControlXML impleme private final PresetInfoStateListener observerForPreset; private final PlayInfoStateListener observerForPlayInfo; - protected CommandTemplate band = new CommandTemplate("%s", "Play_Info/Band"); - protected CommandTemplate preset = new CommandTemplate("<%s>%d", ""); + protected CommandTemplate band = new CommandTemplate("%s", + "Play_Info/Band"); + protected CommandTemplate preset = new CommandTemplate( + "<%s>%d", ""); /** * Need to remember last band state to drive the preset @@ -64,12 +71,9 @@ public class InputWithTunerDABControlXML extends AbstractInputControlXML impleme * @param inputID The input ID - TUNER is going to be used here. * @param con The Yamaha communication object to send http requests. */ - public InputWithTunerDABControlXML(String inputID, - AbstractConnection con, - DabBandStateListener observerForBand, - PresetInfoStateListener observerForPreset, - PlayInfoStateListener observerForPlayInfo, - DeviceInformationState deviceInformationState) { + public InputWithTunerDABControlXML(String inputID, AbstractConnection con, DabBandStateListener observerForBand, + PresetInfoStateListener observerForPreset, PlayInfoStateListener observerForPlayInfo, + DeviceInformationState deviceInformationState) { super(LoggerFactory.getLogger(InputWithTunerDABControlXML.class), inputID, con, deviceInformationState); @@ -86,7 +90,8 @@ public InputWithTunerDABControlXML(String inputID, @Override public void update() throws IOException, ReceivedMessageParseException { - Node responseNode = XMLProtocolService.getResponse(comReference.get(), wrInput("GetParam"), inputElement); + Node responseNode = XMLProtocolService.getResponse(comReference.get(), + wrInput("GetParam"), inputElement); // @formatter:off @@ -169,7 +174,8 @@ public void update() throws IOException, ReceivedMessageParseException { bandState = msgForBand; if (StringUtils.isEmpty(msgForBand.band)) { - logger.warn("Band is unknown for input {}, therefore preset and playback information will not be available", inputID); + logger.warn("Band is unknown for input {}, therefore preset and playback information will not be available", + inputID); } else { Node playInfoNode = getNode(responseNode, "Play_Info/" + msgForBand.band); @@ -180,11 +186,14 @@ public void update() throws IOException, ReceivedMessageParseException { if (metaInfoNode != null) { msgForPlayInfo.album = getNodeContentOrDefault(metaInfoNode, "Program_Type", msgForPlayInfo.album); if (BAND_DAB.equals(msgForBand.band)) { - msgForPlayInfo.station = getNodeContentOrDefault(metaInfoNode, "Service_Label", msgForPlayInfo.station); - msgForPlayInfo.artist = getNodeContentOrDefault(metaInfoNode, "Ensemble_Label", msgForPlayInfo.artist); + msgForPlayInfo.station = getNodeContentOrDefault(metaInfoNode, "Service_Label", + msgForPlayInfo.station); + msgForPlayInfo.artist = getNodeContentOrDefault(metaInfoNode, "Ensemble_Label", + msgForPlayInfo.artist); msgForPlayInfo.song = getNodeContentOrDefault(metaInfoNode, "DLS", msgForPlayInfo.song); } else { - msgForPlayInfo.station = getNodeContentOrDefault(metaInfoNode, "Program_Service", msgForPlayInfo.station); + msgForPlayInfo.station = getNodeContentOrDefault(metaInfoNode, "Program_Service", + msgForPlayInfo.station); msgForPlayInfo.artist = getNodeContentOrDefault(metaInfoNode, "Station", msgForPlayInfo.artist); msgForPlayInfo.song = getNodeContentOrDefault(metaInfoNode, "Radio_Text", msgForPlayInfo.song); } diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/SystemControlXML.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/SystemControlXML.java similarity index 78% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/SystemControlXML.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/SystemControlXML.java index 2fa9d1eacad21..9e9a4d5662c87 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/SystemControlXML.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/SystemControlXML.java @@ -12,6 +12,17 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.*; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Models.RX_A2000; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.*; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.getResponse; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; import org.openhab.binding.yamahareceiver.internal.protocol.ReceivedMessageParseException; import org.openhab.binding.yamahareceiver.internal.protocol.SystemControl; @@ -22,18 +33,6 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Node; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.*; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Models.RX_A2000; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.*; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.getResponse; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.getZoneResponse; - /** * The system control protocol class is used to control basic non-zone functionality * of a Yamaha receiver with HTTP/xml. @@ -45,24 +44,27 @@ public class SystemControlXML implements SystemControl { private final Logger logger = LoggerFactory.getLogger(SystemControlXML.class); - + private static final Set MODELS_WITH_PARTY_SUPPORT = new HashSet<>(Arrays.asList(RX_A2000)); private WeakReference comReference; private SystemControlStateListener observer; private final DeviceDescriptorXML descriptorXML; - protected CommandTemplate power = new CommandTemplate("%s", "System/Power_Control/Power"); - protected CommandTemplate partyMode = new CommandTemplate("%s", "System/Party_Mode/Mode"); + protected CommandTemplate power = new CommandTemplate( + "%s", "System/Power_Control/Power"); + protected CommandTemplate partyMode = new CommandTemplate( + "%s", "System/Party_Mode/Mode"); protected boolean partyModeSupported; - protected CommandTemplate partyModeMute = new CommandTemplate("%s"); + protected CommandTemplate partyModeMute = new CommandTemplate( + "%s"); protected boolean partyModeMuteSupported; - protected CommandTemplate partyModeVolume = new CommandTemplate("%s"); + protected CommandTemplate partyModeVolume = new CommandTemplate( + "%s"); protected boolean partyModeVolumeSupported; - public SystemControlXML(AbstractConnection xml, - SystemControlStateListener observer, - DeviceInformationState deviceInformationState) { + public SystemControlXML(AbstractConnection xml, SystemControlStateListener observer, + DeviceInformationState deviceInformationState) { this.comReference = new WeakReference<>(xml); this.observer = observer; @@ -83,17 +85,20 @@ protected void applyModelVariations() { logger.trace("Compatibility detection"); partyModeSupported = descriptorXML.hasFeature( - d -> MODELS_WITH_PARTY_SUPPORT.contains(d.getUnitName()) || d.system.hasCommandEnding("System,Party_Mode,Mode"), - () -> logger.info("The {} channel is not supported on your model", CHANNEL_PARTY_MODE)); + d -> MODELS_WITH_PARTY_SUPPORT.contains(d.getUnitName()) + || d.system.hasCommandEnding("System,Party_Mode,Mode"), + () -> logger.debug("The {} channel is not supported on your model", CHANNEL_PARTY_MODE)); partyModeMuteSupported = descriptorXML.hasFeature( - d -> MODELS_WITH_PARTY_SUPPORT.contains(d.getUnitName()) || d.system.hasCommandEnding("System,Party_Mode,Volume,Mute"), - () -> logger.info("The {} channel is not supported on your model", CHANNEL_PARTY_MODE_MUTE)); + d -> MODELS_WITH_PARTY_SUPPORT.contains(d.getUnitName()) + || d.system.hasCommandEnding("System,Party_Mode,Volume,Mute"), + () -> logger.debug("The {} channel is not supported on your model", CHANNEL_PARTY_MODE_MUTE)); partyModeVolumeSupported = descriptorXML.hasFeature( - d -> MODELS_WITH_PARTY_SUPPORT.contains(d.getUnitName()) || d.system.hasCommandEnding("System,Party_Mode,Volume,Lvl"), - () -> logger.info("The {} channel is not supported on your model", CHANNEL_PARTY_MODE_VOLUME)); - } + d -> MODELS_WITH_PARTY_SUPPORT.contains(d.getUnitName()) + || d.system.hasCommandEnding("System,Party_Mode,Volume,Lvl"), + () -> logger.debug("The {} channel is not supported on your model", CHANNEL_PARTY_MODE_VOLUME)); + } @Override public void setPower(boolean power) throws IOException, ReceivedMessageParseException { @@ -151,8 +156,7 @@ public void update() throws IOException, ReceivedMessageParseException { state.partyMode = node != null && ON.equals(node.getTextContent()); } - logger.debug("System state - power: {}, partyMode: {}", - state.power, state.partyMode); + logger.debug("System state - power: {}, partyMode: {}", state.power, state.partyMode); observer.systemControlStateChanged(state); } diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLConnection.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLConnection.java similarity index 95% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLConnection.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLConnection.java index e551c17553f36..2d56413c8bb41 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLConnection.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLConnection.java @@ -12,11 +12,6 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; -import org.apache.commons.lang.StringUtils; -import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; @@ -31,6 +26,11 @@ import java.util.Arrays; import java.util.Optional; +import org.apache.commons.lang.StringUtils; +import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * All other protocol classes in this directory use this class for communication. An object * of HttpXMLSendReceive is always bound to a specific host. @@ -56,7 +56,8 @@ public interface CheckedConsumer { R apply(T t) throws IOException; } - private T postMessage(String prefix, String message, String suffix, CheckedConsumer responseConsumer) throws IOException { + private T postMessage(String prefix, String message, String suffix, + CheckedConsumer responseConsumer) throws IOException { if (message.startsWith(" charsetName = Arrays.stream(values) - .map(x -> x.trim()) + Optional charsetName = Arrays.stream(values).map(x -> x.trim()) .filter(x -> x.toLowerCase().startsWith(HEADER_CHARSET_PART)) - .map(x -> x.substring(HEADER_CHARSET_PART.length() + 1, x.length() - 1)) - .findFirst(); + .map(x -> x.substring(HEADER_CHARSET_PART.length() + 1, x.length() - 1)).findFirst(); if (charsetName.isPresent() && !StringUtils.isEmpty(charsetName.get())) { try { @@ -194,6 +194,7 @@ private Charset getResponseCharset(HttpURLConnection connection, Charset default /** * Creates an {@link URL} object to the Yamaha control endpoint + * * @return * @throws MalformedURLException */ @@ -203,6 +204,7 @@ private URL createCrlUrl() throws MalformedURLException { /** * Creates an {@link URL} object to Yamaha + * * @return * @throws MalformedURLException */ diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLConstants.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLConstants.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLConstants.java diff --git a/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLProtocolFactory.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLProtocolFactory.java new file mode 100644 index 0000000000000..5886236464326 --- /dev/null +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLProtocolFactory.java @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.yamahareceiver.internal.protocol.xml; + +import java.util.function.Supplier; + +import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants; +import org.openhab.binding.yamahareceiver.internal.config.YamahaBridgeConfig; +import org.openhab.binding.yamahareceiver.internal.config.YamahaZoneConfig; +import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; +import org.openhab.binding.yamahareceiver.internal.protocol.ConnectionStateListener; +import org.openhab.binding.yamahareceiver.internal.protocol.DeviceInformation; +import org.openhab.binding.yamahareceiver.internal.protocol.InputConverter; +import org.openhab.binding.yamahareceiver.internal.protocol.InputWithNavigationControl; +import org.openhab.binding.yamahareceiver.internal.protocol.InputWithPlayControl; +import org.openhab.binding.yamahareceiver.internal.protocol.InputWithPresetControl; +import org.openhab.binding.yamahareceiver.internal.protocol.InputWithTunerBandControl; +import org.openhab.binding.yamahareceiver.internal.protocol.ProtocolFactory; +import org.openhab.binding.yamahareceiver.internal.protocol.SystemControl; +import org.openhab.binding.yamahareceiver.internal.protocol.ZoneAvailableInputs; +import org.openhab.binding.yamahareceiver.internal.protocol.ZoneControl; +import org.openhab.binding.yamahareceiver.internal.state.AvailableInputStateListener; +import org.openhab.binding.yamahareceiver.internal.state.DabBandStateListener; +import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState; +import org.openhab.binding.yamahareceiver.internal.state.NavigationControlState; +import org.openhab.binding.yamahareceiver.internal.state.NavigationControlStateListener; +import org.openhab.binding.yamahareceiver.internal.state.PlayInfoStateListener; +import org.openhab.binding.yamahareceiver.internal.state.PresetInfoStateListener; +import org.openhab.binding.yamahareceiver.internal.state.SystemControlStateListener; +import org.openhab.binding.yamahareceiver.internal.state.ZoneControlStateListener; + +/** + * Implementation of {@link ProtocolFactory} for XML protocol. + * + * @author Tomasz Maruszak - Initial contribution. + */ +public class XMLProtocolFactory implements ProtocolFactory { + @Override + public void createConnection(String host, ConnectionStateListener connectionStateListener) { + connectionStateListener.connectionEstablished(new XMLConnection(host)); + } + + @Override + public SystemControl SystemControl(AbstractConnection connection, SystemControlStateListener listener, + DeviceInformationState deviceInformationState) { + + return new SystemControlXML(connection, listener, deviceInformationState); + } + + @Override + public InputWithPlayControl InputWithPlayControl(AbstractConnection connection, String currentInputID, + PlayInfoStateListener listener, YamahaBridgeConfig bridgeConfig, + DeviceInformationState deviceInformationState) { + + return new InputWithPlayControlXML(currentInputID, connection, listener, bridgeConfig, deviceInformationState); + } + + @Override + public InputWithPresetControl InputWithPresetControl(AbstractConnection connection, String currentInputID, + PresetInfoStateListener listener, DeviceInformationState deviceInformationState) { + + return new InputWithPresetControlXML(currentInputID, connection, listener, deviceInformationState); + } + + @Override + public InputWithTunerBandControl InputWithDabBandControl(String currentInputID, AbstractConnection connection, + DabBandStateListener observerForBand, PresetInfoStateListener observerForPreset, + PlayInfoStateListener observerForPlayInfo, DeviceInformationState deviceInformationState) { + + return new InputWithTunerDABControlXML(currentInputID, connection, observerForBand, observerForPreset, + observerForPlayInfo, deviceInformationState); + } + + @Override + public InputWithNavigationControl InputWithNavigationControl(AbstractConnection connection, + NavigationControlState state, String inputID, NavigationControlStateListener observer, + DeviceInformationState deviceInformationState) { + + return new InputWithNavigationControlXML(state, inputID, connection, observer, deviceInformationState); + } + + @Override + public ZoneControl ZoneControl(AbstractConnection connection, YamahaZoneConfig zoneSettings, + ZoneControlStateListener listener, Supplier inputConverterSupplier, + DeviceInformationState deviceInformationState) { + + if (isZoneB(zoneSettings.getZone(), deviceInformationState)) { + return new ZoneBControlXML(connection, zoneSettings, listener, deviceInformationState, + inputConverterSupplier); + } + return new ZoneControlXML(connection, zoneSettings.getZone(), zoneSettings, listener, deviceInformationState, + inputConverterSupplier); + } + + @Override + public ZoneAvailableInputs ZoneAvailableInputs(AbstractConnection connection, YamahaZoneConfig zoneSettings, + AvailableInputStateListener listener, Supplier inputConverterSupplier, + DeviceInformationState deviceInformationState) { + + if (isZoneB(zoneSettings.getZone(), deviceInformationState)) { + return new ZoneBAvailableInputsXML(connection, listener, inputConverterSupplier); + } + return new ZoneAvailableInputsXML(connection, zoneSettings.getZone(), listener, inputConverterSupplier); + } + + /** + * Checks if the specified Zone_2 should be emulated using Zone_B feature. + * + * @param zone + * @param deviceInformationState + * @return + */ + private boolean isZoneB(YamahaReceiverBindingConstants.Zone zone, DeviceInformationState deviceInformationState) { + return YamahaReceiverBindingConstants.Zone.Zone_2.equals(zone) + && deviceInformationState.features.contains(YamahaReceiverBindingConstants.Feature.ZONE_B); + } + + @Override + public DeviceInformation DeviceInformation(AbstractConnection connection, DeviceInformationState state) { + return new DeviceInformationXML(connection, state); + } + + @Override + public InputConverter InputConverter(AbstractConnection connection, String setting) { + + return new InputConverterXML(connection, setting); + } + +} diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLProtocolService.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLProtocolService.java similarity index 89% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLProtocolService.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLProtocolService.java index a2b49109046a4..837b76dad6c3c 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLProtocolService.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLProtocolService.java @@ -12,26 +12,21 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; +import static java.util.stream.Collectors.joining; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.Commands.*; + +import java.io.IOException; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone; import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; import org.openhab.binding.yamahareceiver.internal.protocol.ReceivedMessageParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; -import org.w3c.dom.Element; import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; - -import static java.util.stream.Collectors.joining; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.Commands.ZONE_INPUT_PATH; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.Commands.ZONE_INPUT_QUERY; /** * Provides services for XML protocol @@ -44,6 +39,7 @@ public class XMLProtocolService { /** * Sends a command to the specified zone. + * * @param con * @param zone * @param cmd @@ -51,12 +47,14 @@ public class XMLProtocolService { * @throws IOException * @throws ReceivedMessageParseException */ - public static Node getZoneResponse(AbstractConnection con, Zone zone, String cmd) throws IOException, ReceivedMessageParseException { + public static Node getZoneResponse(AbstractConnection con, Zone zone, String cmd) + throws IOException, ReceivedMessageParseException { return getResponse(con, XMLUtils.wrZone(zone, cmd), zone.toString()); } /** * Sends a command to the specified zone. + * * @param con * @param zone * @param cmd @@ -65,19 +63,22 @@ public static Node getZoneResponse(AbstractConnection con, Zone zone, String cmd * @throws IOException * @throws ReceivedMessageParseException */ - public static Node getZoneResponse(AbstractConnection con, Zone zone, String cmd, String path) throws IOException, ReceivedMessageParseException { + public static Node getZoneResponse(AbstractConnection con, Zone zone, String cmd, String path) + throws IOException, ReceivedMessageParseException { return getResponse(con, XMLUtils.wrZone(zone, cmd), zone + "/" + path); } /** * Send the command and retrieve the node at the specified element path. + * * @param cmd * @param path * @return * @throws IOException * @throws ReceivedMessageParseException */ - public static Node getResponse(AbstractConnection con, String cmd, String path) throws IOException, ReceivedMessageParseException { + public static Node getResponse(AbstractConnection con, String cmd, String path) + throws IOException, ReceivedMessageParseException { String response = con.sendReceive(cmd); Document doc = XMLUtils.xml(response); if (doc.getFirstChild() == null) { @@ -89,6 +90,7 @@ public static Node getResponse(AbstractConnection con, String cmd, String path) /** * Sends a request to retrieve the input values available for the zone. + * * @param con * @param zone * @return diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLUtils.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLUtils.java similarity index 93% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLUtils.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLUtils.java index ddb084ee678eb..6412164373cbe 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLUtils.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/XMLUtils.java @@ -12,6 +12,14 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; +import java.io.IOException; +import java.io.StringReader; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone; import org.openhab.binding.yamahareceiver.internal.protocol.ReceivedMessageParseException; import org.slf4j.Logger; @@ -23,13 +31,6 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.StringReader; -import java.util.stream.IntStream; -import java.util.stream.Stream; - /** * Utility methods for XML handling * @@ -63,9 +64,7 @@ static Stream getChildElements(Node node) { if (node == null) { return Stream.empty(); } - return toStream(node.getChildNodes()) - .filter(x -> x.getNodeType() == Node.ELEMENT_NODE) - .map(x -> (Element) x); + return toStream(node.getChildNodes()).filter(x -> x.getNodeType() == Node.ELEMENT_NODE).map(x -> (Element) x); } static Stream toStream(NodeList nodeList) { @@ -78,7 +77,8 @@ static Stream toStream(NodeList nodeList) { * @param root * @param nodePath * @return - * @throws ReceivedMessageParseException when the child node does not exist throws {@link ReceivedMessageParseException}. + * @throws ReceivedMessageParseException when the child node does not exist throws + * {@link ReceivedMessageParseException}. */ static Node getNodeOrFail(Node root, String nodePath) throws ReceivedMessageParseException { Node node = getNode(root, nodePath); @@ -125,7 +125,6 @@ public static String getAnyNodeContentOrDefault(Node root, String defaultValue, return defaultValue; } - /** * Finds the node starting with the root and following the path. If the node is found it's inner text is returned, * otherwise the default provided value. @@ -153,7 +152,8 @@ public static Integer getNodeContentOrDefault(Node root, String nodePath, Intege try { return Integer.valueOf(node.getTextContent()); } catch (NumberFormatException e) { - LOG.trace("The value '{}' of node with path {} could not been parsed to an integer. Applying default of {}", + LOG.trace( + "The value '{}' of node with path {} could not been parsed to an integer. Applying default of {}", node.getTextContent(), nodePath, defaultValue); } } @@ -170,8 +170,7 @@ public static Integer getNodeContentOrDefault(Node root, String nodePath, Intege public static Document xml(String message) throws IOException, ReceivedMessageParseException { // Ensure the message contains XML declaration - String response = message.startsWith("" + message; try { diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneAvailableInputsXML.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneAvailableInputsXML.java similarity index 81% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneAvailableInputsXML.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneAvailableInputsXML.java index a3e575edcc16e..9f1622ec13193 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneAvailableInputsXML.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneAvailableInputsXML.java @@ -12,23 +12,24 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; +import static java.util.stream.Collectors.joining; + import java.io.IOException; import java.lang.ref.WeakReference; import java.util.Collection; import java.util.function.Supplier; -import java.util.stream.Collectors; -import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants; import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone; -import org.openhab.binding.yamahareceiver.internal.protocol.*; +import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; +import org.openhab.binding.yamahareceiver.internal.protocol.InputConverter; +import org.openhab.binding.yamahareceiver.internal.protocol.ReceivedMessageParseException; +import org.openhab.binding.yamahareceiver.internal.protocol.ZoneAvailableInputs; import org.openhab.binding.yamahareceiver.internal.state.AvailableInputState; import org.openhab.binding.yamahareceiver.internal.state.AvailableInputStateListener; import org.openhab.binding.yamahareceiver.internal.state.ZoneControlState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static java.util.stream.Collectors.joining; - /** * The zone protocol class is used to control one zone of a Yamaha receiver with HTTP/xml. * No state will be saved in here, but in {@link ZoneControlState} instead. @@ -46,10 +47,8 @@ public class ZoneAvailableInputsXML implements ZoneAvailableInputs { private final Supplier inputConverterSupplier; private final Zone zone; - public ZoneAvailableInputsXML(AbstractConnection con, - Zone zone, - AvailableInputStateListener observer, - Supplier inputConverterSupplier) { + public ZoneAvailableInputsXML(AbstractConnection con, Zone zone, AvailableInputStateListener observer, + Supplier inputConverterSupplier) { this.conReference = new WeakReference<>(con); this.zone = zone; @@ -64,6 +63,7 @@ public Zone getZone() { return zone; } + @Override public void update() throws IOException, ReceivedMessageParseException { if (observer == null) { return; @@ -79,7 +79,8 @@ public void update() throws IOException, ReceivedMessageParseException { }); if (logger.isTraceEnabled()) { - logger.trace("Zone {} - available inputs: {}", getZone(), state.availableInputs.keySet().stream().collect(joining(", "))); + logger.trace("Zone {} - available inputs: {}", getZone(), + state.availableInputs.keySet().stream().collect(joining(", "))); } observer.availableInputsChanged(state); diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBAvailableInputsXML.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBAvailableInputsXML.java similarity index 85% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBAvailableInputsXML.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBAvailableInputsXML.java index 7fdfe5117b518..72db85cf8bd62 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBAvailableInputsXML.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBAvailableInputsXML.java @@ -12,14 +12,14 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; +import java.util.function.Supplier; + import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone; import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; import org.openhab.binding.yamahareceiver.internal.protocol.InputConverter; import org.openhab.binding.yamahareceiver.internal.state.AvailableInputStateListener; import org.slf4j.LoggerFactory; -import java.util.function.Supplier; - /** * Special case of {@link ZoneAvailableInputsXML} that emulates Zone_2 for Yamaha HTR-xxx using Zone_B features. * @@ -27,9 +27,8 @@ */ public class ZoneBAvailableInputsXML extends ZoneAvailableInputsXML { - public ZoneBAvailableInputsXML(AbstractConnection con, - AvailableInputStateListener observer, - Supplier inputConverterSupplier) { + public ZoneBAvailableInputsXML(AbstractConnection con, AvailableInputStateListener observer, + Supplier inputConverterSupplier) { super(con, Zone.Main_Zone, observer, inputConverterSupplier); this.logger = LoggerFactory.getLogger(ZoneBAvailableInputsXML.class); diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBControlXML.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBControlXML.java similarity index 77% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBControlXML.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBControlXML.java index 0df075f1d3a51..bd96b0d581c7c 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBControlXML.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBControlXML.java @@ -12,16 +12,16 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; -import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; -import org.openhab.binding.yamahareceiver.internal.protocol.InputConverter; +import java.util.function.Supplier; + import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone; import org.openhab.binding.yamahareceiver.internal.config.YamahaZoneConfig; +import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; +import org.openhab.binding.yamahareceiver.internal.protocol.InputConverter; import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState; import org.openhab.binding.yamahareceiver.internal.state.ZoneControlStateListener; import org.slf4j.LoggerFactory; -import java.util.function.Supplier; - /** * Special case of {@link ZoneControlXML} that emulates Zone_2 for Yamaha HTR-xxx using Zone_B features. * @@ -29,11 +29,8 @@ */ public class ZoneBControlXML extends ZoneControlXML { - public ZoneBControlXML(AbstractConnection con, - YamahaZoneConfig zoneSettings, - ZoneControlStateListener observer, - DeviceInformationState deviceInformationState, - Supplier inputConverterSupplier) { + public ZoneBControlXML(AbstractConnection con, YamahaZoneConfig zoneSettings, ZoneControlStateListener observer, + DeviceInformationState deviceInformationState, Supplier inputConverterSupplier) { // Commands will need to be send to Main_Zone super(con, Zone.Main_Zone, zoneSettings, observer, deviceInformationState, inputConverterSupplier); @@ -51,8 +48,11 @@ protected void applyModelVariations() { super.applyModelVariations(); // Apply custom templates for HTR-xxx - this.power = new CommandTemplate("%s", "Power_Control/Zone_B_Power_Info"); + this.power = new CommandTemplate("%s", + "Power_Control/Zone_B_Power_Info"); this.mute = new CommandTemplate("%s", "Volume/Zone_B/Mute"); - this.volume = new CommandTemplate("%d1dB", "Volume/Zone_B/Lvl/Val"); + this.volume = new CommandTemplate( + "%d1dB", + "Volume/Zone_B/Lvl/Val"); } } diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneControlXML.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneControlXML.java similarity index 81% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneControlXML.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneControlXML.java index a8ba8ed386f4a..628cf460aa810 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneControlXML.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneControlXML.java @@ -12,6 +12,16 @@ */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.*; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.*; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.Commands.*; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.getZoneResponse; +import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.*; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.function.Supplier; + import org.apache.commons.lang.StringUtils; import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone; import org.openhab.binding.yamahareceiver.internal.config.YamahaZoneConfig; @@ -26,19 +36,6 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Node; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.function.Supplier; - -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.Commands.ZONE_BASIC_STATUS_CMD; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.Commands.ZONE_BASIC_STATUS_PATH; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.CHANNEL_DIALOGUE_LEVEL; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.CHANNEL_SCENE; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.*; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.getZoneResponse; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.getNodeContentOrDefault; -import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.getNodeContentOrEmpty; - /** * The zone protocol class is used to control one zone of a Yamaha receiver with HTTP/xml. * No state will be saved in here, but in {@link ZoneControlState} instead. @@ -46,8 +43,8 @@ * @author David Gräff - Refactored * @author Eric Thill * @author Ben Jones - * @author Tomasz Maruszak - Refactoring, input mapping fix, added Straight surround, volume DB fix and config improvement. - * + * @author Tomasz Maruszak - Refactoring, input mapping fix, added Straight surround, volume DB fix and config + * improvement. */ public class ZoneControlXML implements ZoneControl { @@ -62,24 +59,30 @@ public class ZoneControlXML implements ZoneControl { private final YamahaZoneConfig zoneConfig; private final DeviceDescriptorXML.ZoneDescriptor zoneDescriptor; - protected CommandTemplate power = new CommandTemplate("%s", "Power_Control/Power"); + protected CommandTemplate power = new CommandTemplate("%s", + "Power_Control/Power"); protected CommandTemplate mute = new CommandTemplate("%s", "Volume/Mute"); - protected CommandTemplate volume = new CommandTemplate("%d1dB", "Volume/Lvl/Val"); - protected CommandTemplate inputSel = new CommandTemplate("%s", "Input/Input_Sel"); + protected CommandTemplate volume = new CommandTemplate( + "%d1dB", "Volume/Lvl/Val"); + protected CommandTemplate inputSel = new CommandTemplate("%s", + "Input/Input_Sel"); protected String inputSelNamePath = "Input/Input_Sel_Item_Info/Title"; - protected CommandTemplate surroundSelProgram = new CommandTemplate("%s", "Surround/Program_Sel/Current/Sound_Program"); - protected CommandTemplate surroundSelStraight = new CommandTemplate("On", "Surround/Program_Sel/Current/Straight"); + protected CommandTemplate surroundSelProgram = new CommandTemplate( + "%s", + "Surround/Program_Sel/Current/Sound_Program"); + protected CommandTemplate surroundSelStraight = new CommandTemplate( + "On", + "Surround/Program_Sel/Current/Straight"); protected CommandTemplate sceneSel = new CommandTemplate("%s"); protected boolean sceneSelSupported = false; - protected CommandTemplate dialogueLevel = new CommandTemplate("%d", "Sound_Video/Dialogue_Adjust/Dialogue_Lvl"); + protected CommandTemplate dialogueLevel = new CommandTemplate( + "%d", + "Sound_Video/Dialogue_Adjust/Dialogue_Lvl"); protected boolean dialogueLevelSupported = false; - public ZoneControlXML(AbstractConnection con, - Zone zone, - YamahaZoneConfig zoneSettings, - ZoneControlStateListener observer, - DeviceInformationState deviceInformationState, - Supplier inputConverterSupplier) { + public ZoneControlXML(AbstractConnection con, Zone zone, YamahaZoneConfig zoneSettings, + ZoneControlStateListener observer, DeviceInformationState deviceInformationState, + Supplier inputConverterSupplier) { this.comReference = new WeakReference<>(con); this.zone = zone; @@ -103,17 +106,18 @@ protected void applyModelVariations() { logger.trace("Zone {} - compatibility detection", getZone()); // Note: Detection if scene is supported - sceneSelSupported = zoneDescriptor.hasCommandEnding("Scene,Scene_Sel", - () -> logger.info("Zone {} - the {} channel is not supported on your model", getZone(), CHANNEL_SCENE)); + sceneSelSupported = zoneDescriptor.hasCommandEnding("Scene,Scene_Sel", () -> logger + .debug("Zone {} - the {} channel is not supported on your model", getZone(), CHANNEL_SCENE)); // Note: Detection if dialogue level is supported - dialogueLevelSupported = zoneDescriptor.hasAnyCommandEnding("Sound_Video,Dialogue_Adjust,Dialogue_Lvl", "Sound_Video,Dialogue_Adjust,Dialogue_Lift"); + dialogueLevelSupported = zoneDescriptor.hasAnyCommandEnding("Sound_Video,Dialogue_Adjust,Dialogue_Lvl", + "Sound_Video,Dialogue_Adjust,Dialogue_Lift"); if (zoneDescriptor.hasCommandEnding("Sound_Video,Dialogue_Adjust,Dialogue_Lift")) { dialogueLevel = dialogueLevel.replace("Dialogue_Lvl", "Dialogue_Lift"); logger.debug("Zone {} - adjusting command to: {}", getZone(), dialogueLevel); } if (!dialogueLevelSupported) { - logger.info("Zone {} - the {} channel is not supported on your model", getZone(), CHANNEL_DIALOGUE_LEVEL); + logger.debug("Zone {} - the {} channel is not supported on your model", getZone(), CHANNEL_DIALOGUE_LEVEL); } // Note: Detection for RX-V3900, which uses instead of @@ -128,14 +132,17 @@ protected void applyModelVariations() { try { // Note: Detection for RX-V3900, which has a different XML node for surround program - Node basicStatusNode = getZoneResponse(comReference.get(), getZone(), ZONE_BASIC_STATUS_CMD, ZONE_BASIC_STATUS_PATH); + Node basicStatusNode = getZoneResponse(comReference.get(), getZone(), ZONE_BASIC_STATUS_CMD, + ZONE_BASIC_STATUS_PATH); String surroundProgram = getNodeContentOrEmpty(basicStatusNode, "Surr/Pgm_Sel/Pgm"); if (StringUtils.isNotEmpty(surroundProgram)) { - surroundSelProgram = new CommandTemplate("Off%s", "Surr/Pgm_Sel/Pgm"); + surroundSelProgram = new CommandTemplate( + "Off%s", "Surr/Pgm_Sel/Pgm"); logger.debug("Zone {} - adjusting command to: {}", getZone(), surroundSelProgram); - surroundSelStraight = new CommandTemplate("On", "Surr/Pgm_Sel/Straight"); + surroundSelStraight = new CommandTemplate("On", + "Surr/Pgm_Sel/Straight"); logger.debug("Zone {} - adjusting command to: {}", getZone(), surroundSelStraight); } @@ -217,7 +224,8 @@ public void setVolume(float volume) throws IOException, ReceivedMessageParseExce * @throws IOException */ @Override - public void setVolumeRelative(ZoneControlState state, float percent) throws IOException, ReceivedMessageParseException { + public void setVolumeRelative(ZoneControlState state, float percent) + throws IOException, ReceivedMessageParseException { setVolume(zoneConfig.getVolumePercentage(state.volumeDB) + percent); } @@ -231,8 +239,7 @@ public void setInput(String name) throws IOException, ReceivedMessageParseExcept @Override public void setSurroundProgram(String name) throws IOException, ReceivedMessageParseException { - String cmd = name.equalsIgnoreCase(SURROUND_PROGRAM_STRAIGHT) - ? surroundSelStraight.apply() + String cmd = name.equalsIgnoreCase(SURROUND_PROGRAM_STRAIGHT) ? surroundSelStraight.apply() : surroundSelProgram.apply(name); sendCommand(cmd); @@ -299,8 +306,8 @@ public void update() throws IOException, ReceivedMessageParseException { value = getNodeContentOrDefault(statusNode, dialogueLevel.getPath(), "0"); state.dialogueLevel = Integer.parseInt(value); - logger.debug("Zone {} state - power: {}, mute: {}, volumeDB: {}, input: {}, surroundProgram: {}", - getZone(), state.power, state.mute, state.volumeDB, state.inputID, state.surroundProgram); + logger.debug("Zone {} state - power: {}, mute: {}, volumeDB: {}, input: {}, surroundProgram: {}", getZone(), + state.power, state.mute, state.volumeDB, state.inputID, state.surroundProgram); observer.zoneStateChanged(state); } diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/AvailableInputState.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/AvailableInputState.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/AvailableInputState.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/AvailableInputState.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/AvailableInputStateListener.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/AvailableInputStateListener.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/AvailableInputStateListener.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/AvailableInputStateListener.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DabBandState.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DabBandState.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DabBandState.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DabBandState.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DabBandStateListener.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DabBandStateListener.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DabBandStateListener.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DabBandStateListener.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DeviceInformationState.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DeviceInformationState.java similarity index 94% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DeviceInformationState.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DeviceInformationState.java index b391218bda3c4..0c62fbcd45c44 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DeviceInformationState.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/DeviceInformationState.java @@ -12,7 +12,10 @@ */ package org.openhab.binding.yamahareceiver.internal.state; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants; diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/Invalidateable.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/Invalidateable.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/Invalidateable.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/Invalidateable.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/NavigationControlState.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/NavigationControlState.java similarity index 99% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/NavigationControlState.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/NavigationControlState.java index 941ed715de315..94e2832640a60 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/NavigationControlState.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/NavigationControlState.java @@ -51,6 +51,7 @@ public void clearItems() { } } + @Override public void invalidate() { this.menuName = "N/A"; this.maxLine = 0; diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/NavigationControlStateListener.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/NavigationControlStateListener.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/NavigationControlStateListener.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/NavigationControlStateListener.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PlayInfoState.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PlayInfoState.java similarity index 87% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PlayInfoState.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PlayInfoState.java index 8501dab946c85..9c7b722f3d251 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PlayInfoState.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PlayInfoState.java @@ -12,8 +12,7 @@ */ package org.openhab.binding.yamahareceiver.internal.state; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.VALUE_EMPTY; -import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.VALUE_NA; +import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.*; /** * The play information state with current station, artist, song name @@ -24,7 +23,8 @@ */ public class PlayInfoState implements Invalidateable { - public String station = VALUE_NA; // NET_RADIO. Will also be used for TUNER where Radio_Text_A/B will be used instead. + public String station = VALUE_NA; // NET_RADIO. Will also be used for TUNER where Radio_Text_A/B will be used + // instead. public String artist = VALUE_NA; // USB, iPOD, PC public String album = VALUE_NA; // USB, iPOD, PC public String song = VALUE_NA; // USB, iPOD, PC @@ -32,6 +32,7 @@ public class PlayInfoState implements Invalidateable { public String playbackMode = "Stop"; // All inputs + @Override public void invalidate() { this.playbackMode = VALUE_NA; this.station = VALUE_NA; diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PlayInfoStateListener.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PlayInfoStateListener.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PlayInfoStateListener.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PlayInfoStateListener.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PresetInfoState.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PresetInfoState.java similarity index 86% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PresetInfoState.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PresetInfoState.java index ea32c785b398f..ee39ca5037899 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PresetInfoState.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PresetInfoState.java @@ -42,11 +42,14 @@ public int getValue() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Preset preset = (Preset) o; - return value == preset.value && - Objects.equals(name, preset.name); + return value == preset.value && Objects.equals(name, preset.name); } @Override @@ -59,6 +62,7 @@ public int hashCode() { public final List presetChannelNames = new ArrayList<>(); public boolean presetChannelNamesChanged = false; + @Override public void invalidate() { presetChannel = 0; presetChannelNames.clear(); diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PresetInfoStateListener.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PresetInfoStateListener.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PresetInfoStateListener.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/PresetInfoStateListener.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/SystemControlState.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/SystemControlState.java similarity index 98% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/SystemControlState.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/SystemControlState.java index c857999317292..e642a78070729 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/SystemControlState.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/SystemControlState.java @@ -23,6 +23,7 @@ public class SystemControlState implements Invalidateable { public boolean partyMode; // If we lost the connection, invalidate the state. + @Override public void invalidate() { power = false; partyMode = false; diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/SystemControlStateListener.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/SystemControlStateListener.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/SystemControlStateListener.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/SystemControlStateListener.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/ZoneControlState.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/ZoneControlState.java similarity index 96% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/ZoneControlState.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/ZoneControlState.java index 11df1b6938565..7470c6fc3bb69 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/ZoneControlState.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/ZoneControlState.java @@ -17,8 +17,7 @@ /** * The state of a specific zone of a Yamaha receiver. * - * @author David Graeff - * + * @author David Graeff - Initial contribution */ public class ZoneControlState { public boolean power = false; diff --git a/addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/ZoneControlStateListener.java b/bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/ZoneControlStateListener.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/ZoneControlStateListener.java rename to bundles/org.openhab.binding.yamahareceiver/src/main/java/org/openhab/binding/yamahareceiver/internal/state/ZoneControlStateListener.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.yamahareceiver/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.yamahareceiver/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.yamahareceiver/src/main/resources/ESH-INF/thing/thing-types.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/ESH-INF/thing/thing-types.xml rename to bundles/org.openhab.binding.yamahareceiver/src/main/resources/ESH-INF/thing/thing-types.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/ResponseLoader.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/ResponseLoader.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/ResponseLoader.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/ResponseLoader.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/TestModels.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/TestModels.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/TestModels.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/TestModels.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverHandlerTest.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverHandlerTest.java similarity index 92% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverHandlerTest.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverHandlerTest.java index e9f8c1229b36c..145db9eb15baf 100644 --- a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverHandlerTest.java +++ b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/YamahaReceiverHandlerTest.java @@ -12,6 +12,16 @@ */ package org.openhab.binding.yamahareceiver.internal; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.initMocks; + +import java.util.List; +import java.util.Optional; + import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.ThingStatus; @@ -21,6 +31,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.openhab.binding.yamahareceiver.internal.config.YamahaBridgeConfig; +import org.openhab.binding.yamahareceiver.internal.discovery.ZoneDiscoveryService; import org.openhab.binding.yamahareceiver.internal.handler.YamahaBridgeHandler; import org.openhab.binding.yamahareceiver.internal.protocol.ConnectionStateListener; import org.openhab.binding.yamahareceiver.internal.protocol.DeviceInformation; @@ -28,17 +39,6 @@ import org.openhab.binding.yamahareceiver.internal.protocol.SystemControl; import org.openhab.binding.yamahareceiver.internal.protocol.xml.AbstractXMLProtocolTest; -import java.util.List; -import java.util.Optional; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.*; -import static org.mockito.MockitoAnnotations.initMocks; - /** * Test cases for {@link YamahaBridgeHandler}. The tests provide mocks for supporting entities using Mockito. * @@ -76,7 +76,8 @@ protected void onSetUp() throws Exception { initMocks(this); ctx.prepareForModel(TestModels.RX_S601D); - ctx.respondWith("GetParam", "Main_Zone_Input_Input_Sel.xml"); + ctx.respondWith("GetParam", + "Main_Zone_Input_Input_Sel.xml"); when(bridgeConfig.getHostWithPort()).thenReturn(Optional.of("localhost:80")); when(bridgeConfig.getInputMapping()).thenReturn(""); @@ -89,6 +90,7 @@ protected void onSetUp() throws Exception { when(protocolFactory.SystemControl(any(), any(), any())).thenReturn(systemControl); subject = new YamahaBridgeHandler(bridge); + subject.setZoneDiscoveryService(mock(ZoneDiscoveryService.class)); subject.setProtocolFactory(protocolFactory); subject.setCallback(callback); diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/XMLProtocolFactoryTest.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/XMLProtocolFactoryTest.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/XMLProtocolFactoryTest.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/XMLProtocolFactoryTest.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractXMLProtocolTest.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractXMLProtocolTest.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractXMLProtocolTest.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractXMLProtocolTest.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractZoneControlXMLTest.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractZoneControlXMLTest.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractZoneControlXMLTest.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/AbstractZoneControlXMLTest.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceDescriptorXMLTest.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceDescriptorXMLTest.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceDescriptorXMLTest.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceDescriptorXMLTest.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceInformationXMLTest.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceInformationXMLTest.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceInformationXMLTest.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/DeviceInformationXMLTest.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputConverterXMLTest.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputConverterXMLTest.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputConverterXMLTest.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputConverterXMLTest.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPlayControlXMLTest.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPlayControlXMLTest.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPlayControlXMLTest.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPlayControlXMLTest.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPresetControlXMLTest.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPresetControlXMLTest.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPresetControlXMLTest.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/InputWithPresetControlXMLTest.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ModelContext.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ModelContext.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ModelContext.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ModelContext.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/SystemControlXMLTest.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/SystemControlXMLTest.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/SystemControlXMLTest.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/SystemControlXMLTest.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBControlXMLTest.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBControlXMLTest.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBControlXMLTest.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneBControlXMLTest.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneControlXMLTest.java b/bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneControlXMLTest.java similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneControlXMLTest.java rename to bundles/org.openhab.binding.yamahareceiver/src/test/java/org/openhab/binding/yamahareceiver/internal/protocol/xml/ZoneControlXMLTest.java diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/HTR-4069/Main_Zone_Basic_Status.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/HTR-4069/Main_Zone_Basic_Status.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/HTR-4069/Main_Zone_Basic_Status.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/HTR-4069/Main_Zone_Basic_Status.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/HTR-4069/Main_Zone_Input_Input_Sel_Item.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/HTR-4069/Main_Zone_Input_Input_Sel_Item.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/HTR-4069/Main_Zone_Input_Input_Sel_Item.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/HTR-4069/Main_Zone_Input_Input_Sel_Item.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/HTR-4069/System_Config.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/HTR-4069/System_Config.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/HTR-4069/System_Config.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/HTR-4069/System_Config.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver/docs/desc_HTR-4069.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/HTR-4069/desc_HTR-4069.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/docs/desc_HTR-4069.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/HTR-4069/desc_HTR-4069.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/Bluetooth_Play_Info.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/Bluetooth_Play_Info.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/Bluetooth_Play_Info.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/Bluetooth_Play_Info.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/Main_Zone_Basic_Status.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/Main_Zone_Basic_Status.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/Main_Zone_Basic_Status.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/Main_Zone_Basic_Status.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/Main_Zone_Input_Input_Sel.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/Main_Zone_Input_Input_Sel.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/Main_Zone_Input_Input_Sel.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/Main_Zone_Input_Input_Sel.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/NET_RADIO_Play_Control_Preset_Preset_Sel.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/NET_RADIO_Play_Control_Preset_Preset_Sel.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/NET_RADIO_Play_Control_Preset_Preset_Sel.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/NET_RADIO_Play_Control_Preset_Preset_Sel.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/NET_RADIO_Play_Control_Preset_Preset_Sel_Item.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/NET_RADIO_Play_Control_Preset_Preset_Sel_Item.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/NET_RADIO_Play_Control_Preset_Preset_Sel_Item.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/NET_RADIO_Play_Control_Preset_Preset_Sel_Item.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/NET_RADIO_Play_Info.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/NET_RADIO_Play_Info.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/NET_RADIO_Play_Info.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/NET_RADIO_Play_Info.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/Spotify_Play_Info.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/Spotify_Play_Info.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/Spotify_Play_Info.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/Spotify_Play_Info.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/System_Config.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/System_Config.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/System_Config.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/System_Config.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/System_Party_Mode_Mode.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/System_Party_Mode_Mode.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/System_Party_Mode_Mode.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/System_Party_Mode_Mode.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/System_Power_Control_Power.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/System_Power_Control_Power.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/System_Power_Control_Power.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/System_Power_Control_Power.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/desc_RX-S601D.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/desc_RX-S601D.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-S601D/desc_RX-S601D.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-S601D/desc_RX-S601D.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/Bluetooth_Play_Info.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/Bluetooth_Play_Info.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/Bluetooth_Play_Info.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/Bluetooth_Play_Info.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/Main_Zone_Basic_Status.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/Main_Zone_Basic_Status.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/Main_Zone_Basic_Status.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/Main_Zone_Basic_Status.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/Main_Zone_Input_Input_Sel_Item.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/Main_Zone_Input_Input_Sel_Item.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/Main_Zone_Input_Input_Sel_Item.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/Main_Zone_Input_Input_Sel_Item.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/NET_USB_Play_Control_Preset_Preset_Sel.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/NET_USB_Play_Control_Preset_Preset_Sel.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/NET_USB_Play_Control_Preset_Preset_Sel.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/NET_USB_Play_Control_Preset_Preset_Sel.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/NET_USB_Play_Control_Preset_Preset_Sel_Item.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/NET_USB_Play_Control_Preset_Preset_Sel_Item.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/NET_USB_Play_Control_Preset_Preset_Sel_Item.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/NET_USB_Play_Control_Preset_Preset_Sel_Item.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/NET_USB_Play_Info.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/NET_USB_Play_Info.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/NET_USB_Play_Info.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/NET_USB_Play_Info.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/System_Config.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/System_Config.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/System_Config.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/System_Config.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/System_Power_Control_Power.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/System_Power_Control_Power.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/System_Power_Control_Power.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/System_Power_Control_Power.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/Tuner_Play_Control_Preset_Preset_Sel.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/Tuner_Play_Control_Preset_Preset_Sel.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/Tuner_Play_Control_Preset_Preset_Sel.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/Tuner_Play_Control_Preset_Preset_Sel.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/Tuner_Play_Control_Preset_Preset_Sel_Item.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/Tuner_Play_Control_Preset_Preset_Sel_Item.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/Tuner_Play_Control_Preset_Preset_Sel_Item.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/Tuner_Play_Control_Preset_Preset_Sel_Item.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/Tuner_Play_Info.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/Tuner_Play_Info.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/Tuner_Play_Info.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/Tuner_Play_Info.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-V3900.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/desc_RX-V3900.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver/docs/desc_RX-V3900.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/desc_RX-V3900.xml diff --git a/addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/iPod_Play_Info.xml b/bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/iPod_Play_Info.xml similarity index 100% rename from addons/binding/org.openhab.binding.yamahareceiver.test/src/test/resources/RX-V3900/iPod_Play_Info.xml rename to bundles/org.openhab.binding.yamahareceiver/src/test/resources/RX-V3900/iPod_Play_Info.xml diff --git a/bundles/org.openhab.binding.zoneminder/.classpath b/bundles/org.openhab.binding.zoneminder/.classpath new file mode 100644 index 0000000000000..8d3240dab160c --- /dev/null +++ b/bundles/org.openhab.binding.zoneminder/.classpath @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.zoneminder/.project b/bundles/org.openhab.binding.zoneminder/.project new file mode 100644 index 0000000000000..d02845c47ec1d --- /dev/null +++ b/bundles/org.openhab.binding.zoneminder/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.zoneminder + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/bundles/org.openhab.binding.zoneminder/NOTICE b/bundles/org.openhab.binding.zoneminder/NOTICE new file mode 100644 index 0000000000000..e1f7f26de4ca7 --- /dev/null +++ b/bundles/org.openhab.binding.zoneminder/NOTICE @@ -0,0 +1,25 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab2-addons + +== Third-party Content + +jsoup +* License: MIT License +* Project: https://jsoup.org/ +* Source: https://github.com/jhy/jsoup + +zoneminder4j +* License: MIT License +* Project: https://github.com/Mr-Eskildsen/zoneminder4j +* Source: https://github.com/Mr-Eskildsen/zoneminder4j diff --git a/addons/binding/org.openhab.binding.zoneminder/README.md b/bundles/org.openhab.binding.zoneminder/README.md similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/README.md rename to bundles/org.openhab.binding.zoneminder/README.md diff --git a/addons/binding/org.openhab.binding.zoneminder/lib/zoneminder4j-0.9.7.jar b/bundles/org.openhab.binding.zoneminder/lib/zoneminder4j-0.9.7.jar similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/lib/zoneminder4j-0.9.7.jar rename to bundles/org.openhab.binding.zoneminder/lib/zoneminder4j-0.9.7.jar diff --git a/bundles/org.openhab.binding.zoneminder/pom.xml b/bundles/org.openhab.binding.zoneminder/pom.xml new file mode 100644 index 0000000000000..aea658368c347 --- /dev/null +++ b/bundles/org.openhab.binding.zoneminder/pom.xml @@ -0,0 +1,25 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.binding.zoneminder + + openHAB Add-ons :: Bundles :: Zoneminder Binding + + + + org.jsoup + jsoup + 1.10.1 + provided + + + + diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/DataRefreshPriorityEnum.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/DataRefreshPriorityEnum.java similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/DataRefreshPriorityEnum.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/DataRefreshPriorityEnum.java diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderConstants.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderConstants.java similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderConstants.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderConstants.java diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderHandlerFactory.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderHandlerFactory.java similarity index 87% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderHandlerFactory.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderHandlerFactory.java index 66e9983b2f68b..f7bc5fe654919 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderHandlerFactory.java +++ b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderHandlerFactory.java @@ -12,7 +12,10 @@ */ package org.openhab.binding.zoneminder.internal; +import java.util.Collections; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.smarthome.core.thing.Bridge; import org.eclipse.smarthome.core.thing.Thing; @@ -26,8 +29,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.Sets; - /** * The {@link ZoneMinderHandlerFactory} is responsible for creating things and thing * handlers. @@ -40,8 +41,9 @@ public class ZoneMinderHandlerFactory extends BaseThingHandlerFactory { private final Logger logger = LoggerFactory.getLogger(ZoneMinderHandlerFactory.class); - public static final Set SUPPORTED_THING_TYPES = Sets.union( - ZoneMinderServerBridgeHandler.SUPPORTED_THING_TYPES, ZoneMinderThingMonitorHandler.SUPPORTED_THING_TYPES); + public static final Set SUPPORTED_THING_TYPES = Collections + .unmodifiableSet(Stream.concat(ZoneMinderServerBridgeHandler.SUPPORTED_THING_TYPES.stream(), + ZoneMinderThingMonitorHandler.SUPPORTED_THING_TYPES.stream()).collect(Collectors.toSet())); @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderProperties.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderProperties.java similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderProperties.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderProperties.java diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderBridgeServerConfig.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderBridgeServerConfig.java similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderBridgeServerConfig.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderBridgeServerConfig.java diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderConfig.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderConfig.java similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderConfig.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderConfig.java diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderThingConfig.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderThingConfig.java similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderThingConfig.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderThingConfig.java diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderThingMonitorConfig.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderThingMonitorConfig.java similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderThingMonitorConfig.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderThingMonitorConfig.java diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/discovery/ZoneMinderDiscoveryService.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/discovery/ZoneMinderDiscoveryService.java similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/discovery/ZoneMinderDiscoveryService.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/discovery/ZoneMinderDiscoveryService.java diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderBaseThingHandler.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderBaseThingHandler.java similarity index 98% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderBaseThingHandler.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderBaseThingHandler.java index 3f9c05e7392fa..c5a2308e9d08c 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderBaseThingHandler.java +++ b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderBaseThingHandler.java @@ -61,12 +61,6 @@ public abstract class ZoneMinderBaseThingHandler extends BaseThingHandler implem /** This refresh status. */ private boolean thingRefreshed; - /** Unique Id of the thing in zoneminder. */ - private String zoneMinderId; - - /** ZoneMidner ConnectionInfo */ - private IZoneMinderConnectionInfo zoneMinderConnection; - private Lock lockSession = new ReentrantLock(); private IZoneMinderSession zoneMinderSession; @@ -150,7 +144,11 @@ public void dispose() { */ public ChannelUID getChannelUIDFromChannelId(String id) { Channel ch = thing.getChannel(id); - return ch.getUID(); + if (ch == null) { + return null; + } else { + return ch.getUID(); + } } protected abstract void onFetchData(); diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderHandler.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderHandler.java similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderHandler.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderHandler.java diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderServerBridgeHandler.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderServerBridgeHandler.java similarity index 95% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderServerBridgeHandler.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderServerBridgeHandler.java index f668e5c954b59..308527a2db981 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderServerBridgeHandler.java +++ b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderServerBridgeHandler.java @@ -16,6 +16,7 @@ import java.math.BigDecimal; import java.security.GeneralSecurityException; import java.util.ArrayList; +import java.util.Collections; import java.util.Hashtable; import java.util.List; import java.util.Map; @@ -37,7 +38,6 @@ import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.core.thing.ThingStatusInfo; import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; @@ -52,8 +52,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.Sets; - import name.eskildsen.zoneminder.IZoneMinderConnectionInfo; import name.eskildsen.zoneminder.IZoneMinderDaemonStatus; import name.eskildsen.zoneminder.IZoneMinderDiskUsage; @@ -76,8 +74,8 @@ public class ZoneMinderServerBridgeHandler extends BaseBridgeHandler implements public static final int TELNET_TIMEOUT = 5000; - public static final Set SUPPORTED_THING_TYPES = Sets - .newHashSet(ZoneMinderConstants.THING_TYPE_BRIDGE_ZONEMINDER_SERVER); + public static final Set SUPPORTED_THING_TYPES = Collections + .singleton(ZoneMinderConstants.THING_TYPE_BRIDGE_ZONEMINDER_SERVER); /** * Logger @@ -187,8 +185,6 @@ public void run() { for (Thing thing : getThing().getThings()) { try { if (thing.getThingTypeUID().equals(ZoneMinderConstants.THING_TYPE_THING_ZONEMINDER_MONITOR)) { - Thing thingMonitor = thing; - ZoneMinderBaseThingHandler thingHandler = (ZoneMinderBaseThingHandler) thing.getHandler(); if (thingHandler != null) { if (thingHandler.getRefreshPriority() == DataRefreshPriorityEnum.HIGH_PRIORITY) { @@ -336,22 +332,6 @@ public String getZoneMinderId() { return getThing().getUID().getAsString(); } - @Override - public void channelLinked(ChannelUID channelUID) { - // can be overridden by subclasses - ThingUID s1 = getThing().getUID(); - ThingTypeUID s2 = getThing().getThingTypeUID(); - logger.debug("{}: Channel '{}' was linked to '{}'", getLogIdentifier(), channelUID.getAsString(), - this.thing.getThingTypeUID()); - } - - @Override - public void channelUnlinked(ChannelUID channelUID) { - // can be overridden by subclasses - logger.debug("{}: Channel '{}' was unlinked from '{}'", getLogIdentifier(), channelUID.getAsString(), - this.thing.getThingTypeUID()); - } - protected ArrayList getMonitors(IZoneMinderSession session) { if (isConnected()) { return ZoneMinderFactory.getServerProxy(session).getMonitors(); @@ -374,7 +354,8 @@ public ZoneMinderBaseThingHandler getZoneMinderThingHandlerFromZoneMinderId(Thin for (Thing thing : things) { ZoneMinderBaseThingHandler thingHandler = (ZoneMinderBaseThingHandler) thing.getHandler(); - if ((thingHandler.getZoneMinderId().equals(zoneMinderId)) + + if ((thingHandler != null) && (thingHandler.getZoneMinderId().equals(zoneMinderId)) && (thing.getThingTypeUID().equals(thingTypeUID))) { return thingHandler; } @@ -439,10 +420,10 @@ protected synchronized void refreshThing(IZoneMinderSession session, boolean fet if (diskUsage == null) { logger.warn("{}: ZoneMinderDiskUsage dataset could not be obtained (received 'null')", getLogIdentifier()); - } else if (hostLoad.getHttpResponseCode() != 200) { + } else if (diskUsage.getHttpResponseCode() != 200) { logger.warn( "{}: ZoneMinderDiskUsage dataset could not be obtained (HTTP Response: Code='{}', Message='{}')", - getLogIdentifier(), hostLoad.getHttpResponseCode(), hostLoad.getHttpResponseMessage()); + getLogIdentifier(), diskUsage.getHttpResponseCode(), diskUsage.getHttpResponseMessage()); } else { channelDiskUsage = diskUsage.getDiskUsage(); } @@ -467,21 +448,14 @@ protected synchronized void refreshThing(IZoneMinderSession session, boolean fet for (Thing thing : things) { try { if (thing.getThingTypeUID().equals(ZoneMinderConstants.THING_TYPE_THING_ZONEMINDER_MONITOR)) { - Thing thingMonitor = thing; ZoneMinderBaseThingHandler thingHandler = (ZoneMinderBaseThingHandler) thing.getHandler(); - + if (thingHandler == null) { + continue; + } thingHandler.refreshThing(session, DataRefreshPriorityEnum.SCHEDULED); } - } catch (NullPointerException ex) { - // This isn't critical (unless it comes over and over). There seems to be a bug so that a null - // pointer exception is coming every now and then. - // HAve to find the reason for that. Until thenm, don't Spamm - logger.debug("{}: Method 'refreshThing()' for Bridge {} failed for thing='{}' - Exception='{}'", - getLogIdentifier(), this.getZoneMinderId(), thing.getUID(), ex.getMessage()); - - // Other exceptions has to be shown as errors - } catch (Exception ex) { - logger.error("{}: Method 'refreshThing()' for Bridge {} failed for thing='{}' - Exception='{}'", + } catch (Exception ex) { // Other exceptions has to be shown as errors + logger.warn("{}: Method 'refreshThing()' for Bridge {} failed for thing='{}' - Exception='{}'", getLogIdentifier(), this.getZoneMinderId(), thing.getUID(), ex.getMessage()); } } @@ -498,10 +472,6 @@ public boolean isOnline() { return online; } - private synchronized boolean getConnected() { - return this.connected; - } - /** * Set connection status. * @@ -542,15 +512,6 @@ private void setBridgeConnectionStatus(boolean connected) { setConnected(connected); } - /** - * Set channel 'bridge_connection'. - * - * @param connected - */ - private boolean getBridgeConnectionStatus() { - return getConnected(); - } - /** * Runs when connection established. * @@ -799,12 +760,16 @@ else if (!serverProxy.isTriggerOptionEnabled()) { } } + @SuppressWarnings("null") protected void updateBridgeStatus(ThingStatus newStatus, ThingStatusDetail statusDetail, String statusDescription) { ThingStatusInfo curStatusInfo = thing.getStatusInfo(); - String curDescription = StringUtils.isBlank(curStatusInfo.getDescription()) ? "" - : curStatusInfo.getDescription(); - // Status changed + String curDescription = curStatusInfo.getDescription(); + if (StringUtils.isBlank(curStatusInfo.getDescription())) { + curDescription = ""; + } + + // Status changed, curDescription is null checked if ((curStatusInfo.getStatus() != newStatus) || (curStatusInfo.getStatusDetail() != statusDetail) || (!curDescription.equals(statusDescription))) { // if (thing.getStatus() != newStatus) { diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderThingMonitorHandler.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderThingMonitorHandler.java similarity index 92% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderThingMonitorHandler.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderThingMonitorHandler.java index a8cbcadb636a5..7a7dfcdb7eee7 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderThingMonitorHandler.java +++ b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderThingMonitorHandler.java @@ -15,16 +15,14 @@ import java.io.IOException; import java.math.BigDecimal; import java.security.GeneralSecurityException; +import java.util.Collections; import java.util.Map; import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.security.auth.login.FailedLoginException; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.thing.Bridge; -import org.eclipse.smarthome.core.thing.Channel; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; @@ -41,8 +39,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.Sets; - import name.eskildsen.zoneminder.IZoneMinderConnectionInfo; import name.eskildsen.zoneminder.IZoneMinderDaemonStatus; import name.eskildsen.zoneminder.IZoneMinderEventData; @@ -64,19 +60,12 @@ * @author Martin S. Eskildsen - Initial contribution */ public class ZoneMinderThingMonitorHandler extends ZoneMinderBaseThingHandler implements IZoneMinderEventSubscriber { - - public static final Set SUPPORTED_THING_TYPES = Sets - .newHashSet(ZoneMinderConstants.THING_TYPE_THING_ZONEMINDER_MONITOR); - - private static final String MONITOR_STATUS_NOT_INIT = ""; - private static final int MAX_MONITOR_STATUS_WATCH_COUNT = 3; + public static final Set SUPPORTED_THING_TYPES = Collections + .singleton(ZoneMinderConstants.THING_TYPE_THING_ZONEMINDER_MONITOR); /** Make sure we can log errors, warnings or what ever somewhere */ private final Logger logger = LoggerFactory.getLogger(ZoneMinderThingMonitorHandler.class); - private String lastMonitorStatus = MONITOR_STATUS_NOT_INIT; - private Integer monitorStatusMatchCount = 3; - private ZoneMinderThingMonitorConfig config; private ZoneMinderEvent curEvent; @@ -260,7 +249,6 @@ else if (command == OnOffType.OFF) { String commandString = ""; if (ZoneMinderMonitorFunctionEnum.isValid(command.toString())) { commandString = ZoneMinderMonitorFunctionEnum.getEnum(command.toString()).toString(); - ZoneMinderServerBridgeHandler bridge = getZoneMinderBridgeHandler(); IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(), getZoneMinderId()); @@ -322,8 +310,6 @@ public void initialize() { public void onTrippedForceAlarm(ZoneMinderTriggerEvent event) { try { logger.info("{}: Received forceAlarm for monitor {}", getLogIdentifier(), event.getMonitorId()); - Channel channel = this.getThing().getChannel(ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS); - Channel chEventCause = this.getThing().getChannel(ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE); // Set Current Event to actual event if (event.getState()) { @@ -347,47 +333,15 @@ protected String getZoneMinderThingType() { return ZoneMinderConstants.THING_ZONEMINDER_MONITOR; } - private Boolean isDaemonRunning(Boolean daemonStatus, String daemonStatusText) { - Boolean result = false; - - Pattern pattern = Pattern - .compile("[0-9]{2}/[0-9]{2}/[0-9]{2}\\s+([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]"); - - Matcher matcher = pattern.matcher(daemonStatusText); - - if (matcher.find()) { - String currentMonitorStatus = daemonStatusText.substring(matcher.start(), matcher.end()); - if (lastMonitorStatus.equals(currentMonitorStatus)) { - monitorStatusMatchCount++; - } else if (lastMonitorStatus.equals(MONITOR_STATUS_NOT_INIT)) { - // We have just started, so we will assume that the monitor is running (don't set match count - // to Zero) - monitorStatusMatchCount++; - lastMonitorStatus = daemonStatusText.substring(matcher.start(), matcher.end()); - } else { - monitorStatusMatchCount = 0; - lastMonitorStatus = daemonStatusText.substring(matcher.start(), matcher.end()); - } - } - - else { - monitorStatusMatchCount = 0; - lastMonitorStatus = ""; - logger.debug("MONITOR-{}: Online(): No match found in status text.", getLogIdentifier()); - } - return daemonStatus; - } - @Override public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { - // Assume succes + // Assume success ThingStatus newThingStatus = ThingStatus.ONLINE; ThingStatusDetail thingStatusDetailed = ThingStatusDetail.NONE; String thingStatusDescription = ""; ThingStatus curThingStatus = this.getThing().getStatus(); - boolean connectionStatus = false; // Is connected to ZoneMinder and thing is ONLINE if (isConnected() && curThingStatus == ThingStatus.ONLINE) { updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription); @@ -395,7 +349,7 @@ public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { } try { - connectionStatus = ZoneMinderFactory.validateConnection(connection); + ZoneMinderFactory.validateConnection(connection); } catch (IllegalArgumentException e) { logger.error("{}: validateConnection failed with exception='{}'", getLogIdentifier(), e.getMessage()); newThingStatus = ThingStatus.OFFLINE; @@ -407,10 +361,11 @@ public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { try { String msg; - Bridge b = getBridge(); + + final Bridge bridge = getBridge(); // 1. Is there a Bridge assigned? - if (getBridge() == null) { + if (bridge == null) { msg = String.format("No Bridge assigned to monitor '%s'", thing.getUID()); logger.error("{}: {}", getLogIdentifier(), msg); newThingStatus = ThingStatus.OFFLINE; @@ -420,12 +375,12 @@ public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { return; } else { logger.debug("{}: ThingAvailability: Thing '{}' has Bridge '{}' defined (Check PASSED)", - getLogIdentifier(), thing.getUID(), getBridge().getBridgeUID()); + getLogIdentifier(), thing.getUID(), bridge.getBridgeUID()); } // 2. Is Bridge Online? - if (getBridge().getStatus() != ThingStatus.ONLINE) { - msg = String.format("Bridge '%s' is OFFLINE", getBridge().getBridgeUID()); + if (bridge.getStatus() != ThingStatus.ONLINE) { + msg = String.format("Bridge '%s' is OFFLINE", bridge.getBridgeUID()); newThingStatus = ThingStatus.OFFLINE; thingStatusDetailed = ThingStatusDetail.BRIDGE_OFFLINE; thingStatusDescription = msg; @@ -434,7 +389,7 @@ public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { return; } else { logger.debug("{}: ThingAvailability: Bridge '{}' is ONLINE (Check PASSED)", getLogIdentifier(), - getBridge().getBridgeUID()); + bridge.getBridgeUID()); } // 3. Is Configuration OK? @@ -598,7 +553,7 @@ public void updateChannel(ChannelUID channel) { } } catch (Exception ex) { logger.error("{}: Error when 'updateChannel' was called (channelId='{}'state='{}', exception'{}')", - getLogIdentifier(), channel.toString(), state.toString(), ex.getMessage()); + getLogIdentifier(), channel, state, ex.getMessage()); } } @@ -655,30 +610,28 @@ protected void recalculateChannelStates() { channelForceAlarm = false; channelEventCause = ""; break; - case PRE_ALARM: alarmedDetailedState = true; recordingDetailedState = true; channelForceAlarm = false; break; - case ALARM: alarmedDetailedState = true; recordingDetailedState = true; channelForceAlarm = true; break; - case ALERT: alarmedDetailedState = true; recordingDetailedState = true; channelForceAlarm = false; break; - case RECORDING: alarmedDetailedState = false; recordingDetailedState = true; channelForceAlarm = false; break; + case UNKNOWN: + } logger.debug( "{}: Recalculate channel states based on Detailed State: DetailedState='{}' -> alarmState='{}', recordingState='{}'", diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderThingType.java b/bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderThingType.java similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderThingType.java rename to bundles/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/handler/ZoneMinderThingType.java diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.zoneminder/src/main/resources/ESH-INF/binding/binding.xml similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/ESH-INF/binding/binding.xml rename to bundles/org.openhab.binding.zoneminder/src/main/resources/ESH-INF/binding/binding.xml diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/monitor-channels.xml b/bundles/org.openhab.binding.zoneminder/src/main/resources/ESH-INF/config/monitor-channels.xml similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/monitor-channels.xml rename to bundles/org.openhab.binding.zoneminder/src/main/resources/ESH-INF/config/monitor-channels.xml diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/zoneminderserver.xml b/bundles/org.openhab.binding.zoneminder/src/main/resources/ESH-INF/config/zoneminderserver.xml similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/zoneminderserver.xml rename to bundles/org.openhab.binding.zoneminder/src/main/resources/ESH-INF/config/zoneminderserver.xml diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/monitor-thing.xml b/bundles/org.openhab.binding.zoneminder/src/main/resources/ESH-INF/thing/monitor-thing.xml similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/monitor-thing.xml rename to bundles/org.openhab.binding.zoneminder/src/main/resources/ESH-INF/thing/monitor-thing.xml diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/server-bridge.xml b/bundles/org.openhab.binding.zoneminder/src/main/resources/ESH-INF/thing/server-bridge.xml similarity index 100% rename from addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/server-bridge.xml rename to bundles/org.openhab.binding.zoneminder/src/main/resources/ESH-INF/thing/server-bridge.xml diff --git a/bundles/org.openhab.extensionservice.marketplace.automation/src/main/java/org/openhab/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java b/bundles/org.openhab.extensionservice.marketplace.automation/src/main/java/org/openhab/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java index 7be6eeee1666d..8ef95f2fde978 100644 --- a/bundles/org.openhab.extensionservice.marketplace.automation/src/main/java/org/openhab/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java +++ b/bundles/org.openhab.extensionservice.marketplace.automation/src/main/java/org/openhab/extensionservice/marketplace/automation/internal/MarketplaceRuleTemplateProvider.java @@ -25,9 +25,9 @@ import org.openhab.core.automation.parser.ParsingException; import org.openhab.core.automation.template.RuleTemplate; import org.openhab.core.automation.template.RuleTemplateProvider; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.annotations.ReferencePolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,6 +46,11 @@ public class MarketplaceRuleTemplateProvider extends DefaultAbstractManagedProvi private Parser parser; + @Activate + public MarketplaceRuleTemplateProvider(final @Reference StorageService storageService) { + super(storageService); + } + @Override public RuleTemplate getTemplate(String uid, Locale locale) { return get(uid); @@ -78,7 +83,7 @@ protected void unsetParser(Parser parser) { /** * This adds a new rule template to the persistent storage. * - * @param uid the UID to be used for the template + * @param uid the UID to be used for the template * @param json the template content as a json string * * @throws ParsingException if the content cannot be parsed correctly @@ -100,15 +105,4 @@ public void addTemplateAsJSON(String uid, String json) throws ParsingException { } } - @Override - @Reference(policy = ReferencePolicy.DYNAMIC) - public void setStorageService(StorageService StorageService) { - super.setStorageService(StorageService); - } - - @Override - public void unsetStorageService(StorageService StorageService) { - super.unsetStorageService(StorageService); - } - } diff --git a/bundles/org.openhab.io.azureiothub/.classpath b/bundles/org.openhab.io.azureiothub/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.io.azureiothub/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.io.azureiothub/.project b/bundles/org.openhab.io.azureiothub/.project new file mode 100644 index 0000000000000..ef9ae19c5978d --- /dev/null +++ b/bundles/org.openhab.io.azureiothub/.project @@ -0,0 +1,23 @@ + + + org.openhab.io.azureiothub + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/io/org.openhab.io.azureiothub/NOTICE b/bundles/org.openhab.io.azureiothub/NOTICE similarity index 100% rename from addons/io/org.openhab.io.azureiothub/NOTICE rename to bundles/org.openhab.io.azureiothub/NOTICE diff --git a/addons/io/org.openhab.io.azureiothub/README.md b/bundles/org.openhab.io.azureiothub/README.md similarity index 100% rename from addons/io/org.openhab.io.azureiothub/README.md rename to bundles/org.openhab.io.azureiothub/README.md diff --git a/bundles/org.openhab.io.azureiothub/pom.xml b/bundles/org.openhab.io.azureiothub/pom.xml new file mode 100644 index 0000000000000..08fc1f0625301 --- /dev/null +++ b/bundles/org.openhab.io.azureiothub/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.io.azureiothub + + openHAB Add-ons :: Bundles :: Azure IoT Hub Connector + + + + com.microsoft.azure.sdk.iot + iot-device-client + 1.2.27 + provided + + + com.microsoft.azure.sdk.iot + iot-service-client + 1.4.20 + provided + + + com.microsoft.azure + azure-eventhubs + 0.14.0 + provided + + + javax.json + javax.json-api + 1.0 + provided + + + + diff --git a/addons/io/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/AzureDevice.java b/bundles/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/AzureDevice.java similarity index 100% rename from addons/io/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/AzureDevice.java rename to bundles/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/AzureDevice.java diff --git a/addons/io/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/AzureDevices.java b/bundles/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/AzureDevices.java similarity index 100% rename from addons/io/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/AzureDevices.java rename to bundles/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/AzureDevices.java diff --git a/addons/io/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/AzureEventCallback.java b/bundles/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/AzureEventCallback.java similarity index 100% rename from addons/io/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/AzureEventCallback.java rename to bundles/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/AzureEventCallback.java diff --git a/addons/io/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/CloudClient.java b/bundles/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/CloudClient.java similarity index 100% rename from addons/io/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/CloudClient.java rename to bundles/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/CloudClient.java diff --git a/addons/io/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/CloudService.java b/bundles/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/CloudService.java similarity index 100% rename from addons/io/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/CloudService.java rename to bundles/org.openhab.io.azureiothub/src/main/java/org/openhab/io/internal/azureiothub/CloudService.java diff --git a/addons/io/org.openhab.io.azureiothub/ESH-INF/config/config.xml b/bundles/org.openhab.io.azureiothub/src/main/resources/ESH-INF/config/config.xml similarity index 100% rename from addons/io/org.openhab.io.azureiothub/ESH-INF/config/config.xml rename to bundles/org.openhab.io.azureiothub/src/main/resources/ESH-INF/config/config.xml diff --git a/bundles/org.openhab.io.homekit/.classpath b/bundles/org.openhab.io.homekit/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.io.homekit/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.io.homekit/.project b/bundles/org.openhab.io.homekit/.project new file mode 100644 index 0000000000000..00e1ca6714021 --- /dev/null +++ b/bundles/org.openhab.io.homekit/.project @@ -0,0 +1,23 @@ + + + org.openhab.io.homekit + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/io/org.openhab.io.homekit/NOTICE b/bundles/org.openhab.io.homekit/NOTICE similarity index 100% rename from addons/io/org.openhab.io.homekit/NOTICE rename to bundles/org.openhab.io.homekit/NOTICE diff --git a/addons/io/org.openhab.io.homekit/README.md b/bundles/org.openhab.io.homekit/README.md similarity index 100% rename from addons/io/org.openhab.io.homekit/README.md rename to bundles/org.openhab.io.homekit/README.md diff --git a/bundles/org.openhab.io.homekit/pom.xml b/bundles/org.openhab.io.homekit/pom.xml new file mode 100644 index 0000000000000..0da8c2af1e78c --- /dev/null +++ b/bundles/org.openhab.io.homekit/pom.xml @@ -0,0 +1,67 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.io.homekit + + openHAB Add-ons :: Bundles :: Homekit + + + + org.glassfish + javax.json + 1.0.4 + provided + + + javax.json + javax.json-api + 1.0 + provided + + + io.netty + netty-all + 4.0.32.Final + provided + + + com.nimbusds + srp6a + 1.5.2 + provided + + + net.i2p.crypto + eddsa + 0.1.0 + provided + + + org.bouncycastle + bcprov-jdk15on + 1.51 + provided + + + org.zeromq + curve25519-java + 0.1.0 + provided + + + com.beowulfe.hap + hap + 1.1.4 + provided + + + + diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/Homekit.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/Homekit.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/Homekit.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/Homekit.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAccessoryRegistry.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAccessoryRegistry.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAccessoryRegistry.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAccessoryRegistry.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAccessoryUpdater.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAccessoryUpdater.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAccessoryUpdater.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAccessoryUpdater.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAuthInfoImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAuthInfoImpl.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAuthInfoImpl.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAuthInfoImpl.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitChangeListener.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitChangeListener.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitChangeListener.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitChangeListener.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitCharacteristicType.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitCharacteristicType.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitCharacteristicType.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitCharacteristicType.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitCommandExtension.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitCommandExtension.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitCommandExtension.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitCommandExtension.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitDeviceType.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitDeviceType.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitDeviceType.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitDeviceType.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitImpl.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitImpl.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitImpl.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitSettings.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitSettings.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitSettings.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitSettings.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitTaggedItem.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitTaggedItem.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitTaggedItem.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitTaggedItem.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitAccessoryImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitAccessoryImpl.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitAccessoryImpl.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitAccessoryImpl.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitLightbulbImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitLightbulbImpl.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitLightbulbImpl.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitLightbulbImpl.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractTemperatureHomekitAccessoryImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractTemperatureHomekitAccessoryImpl.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractTemperatureHomekitAccessoryImpl.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractTemperatureHomekitAccessoryImpl.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/GroupedAccessory.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/GroupedAccessory.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/GroupedAccessory.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/GroupedAccessory.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAccessoryFactory.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAccessoryFactory.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAccessoryFactory.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitAccessoryFactory.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitColorfulLightbulbImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitColorfulLightbulbImpl.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitColorfulLightbulbImpl.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitColorfulLightbulbImpl.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitContactSensorImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitContactSensorImpl.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitContactSensorImpl.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitContactSensorImpl.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitDimmableLightbulbImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitDimmableLightbulbImpl.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitDimmableLightbulbImpl.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitDimmableLightbulbImpl.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitHumiditySensorImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitHumiditySensorImpl.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitHumiditySensorImpl.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitHumiditySensorImpl.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitLightbulbImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitLightbulbImpl.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitLightbulbImpl.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitLightbulbImpl.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitSwitchImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitSwitchImpl.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitSwitchImpl.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitSwitchImpl.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitTemperatureSensorImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitTemperatureSensorImpl.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitTemperatureSensorImpl.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitTemperatureSensorImpl.java diff --git a/addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitThermostatImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitThermostatImpl.java similarity index 100% rename from addons/io/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitThermostatImpl.java rename to bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitThermostatImpl.java diff --git a/addons/io/org.openhab.io.homekit/ESH-INF/config/config.xml b/bundles/org.openhab.io.homekit/src/main/resources/ESH-INF/config/config.xml similarity index 100% rename from addons/io/org.openhab.io.homekit/ESH-INF/config/config.xml rename to bundles/org.openhab.io.homekit/src/main/resources/ESH-INF/config/config.xml diff --git a/bundles/org.openhab.io.hueemulation/.classpath b/bundles/org.openhab.io.hueemulation/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.io.hueemulation/.project b/bundles/org.openhab.io.hueemulation/.project new file mode 100644 index 0000000000000..177425a461cfb --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/.project @@ -0,0 +1,23 @@ + + + org.openhab.io.hueemulation + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.nikohomecontrol/NOTICE b/bundles/org.openhab.io.hueemulation/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.nikohomecontrol/NOTICE rename to bundles/org.openhab.io.hueemulation/NOTICE diff --git a/bundles/org.openhab.io.hueemulation/README.md b/bundles/org.openhab.io.hueemulation/README.md new file mode 100644 index 0000000000000..b5c87baf26036 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/README.md @@ -0,0 +1,197 @@ +# openHAB Hue Emulation Service + +Hue Emulation exposes openHAB items as Hue lights to other Hue API compatible applications like Amazon Echos, Google Homes or any Hue compatible application. + +Because Amazon Echo and Google Home control openHAB locally this way, it is a fast and reliable way to voice control your installation. +See the Troubleshoot section down below though. + +This service is independent of the also available Hue binding! + +Currently the following Hue functionality is supported: + +* Lights: Maps to items +* Groups: Maps to group items +* Rooms: Maps to group items with a specific tag +* Scenes: Maps to rules (new rule engine) that are tagged with "scene" +* Rules: Maps to rules (new rule engine) that are tagged with "hueemulation_rule" +* Schedule: Maps to rules (new rule engine) that are tagged with "hueemulation_schedule" + +You can create / modify and remove groups, rooms, scenes, rules and schedules from within Hue compatible devices and apps. + +## Discovery: + +As soon as the service is enabled, it will announce the presence of an (emulated) HUE bridge of the second generation (square bridge). +Hue bridges are using the Universal Plug and Play (UPnP) protocol for discovery. + +![Philips Hue Bridge](doc/Philips_Hue_Bridge.jpg) + +Like the real HUE bridge the service must be put into pairing mode before other applications can access it. +By default the pairing mode disables itself after 1 minute (can be configured). + +## Exposed lights + +It is important to note that you are exposing *Items* not *Things* or *Channels*. +Only Color, Dimmer, Rollershutter, Switch and Group type *Items* are supported. + +This service can emulate 3 different devices: + +* An OSRAM SMART+ Plug, +* a dimmable white color Philips A19 bulb and +* an a Philips Gen 3 LCT010 extended color bulb. + +The exposed Hue-type depends on some criteria: + +* If the item has the category "ColorLight": It will be exposed as a color bulb +* If the item has the category "Light": It will be exposed as a switch. + +This initial type determination is overridden if the item is tagged. + +The following default tags are setup: + +* "Switchable": Item will be exposed as an OSRAM SMART+ Plug +* "Lighting": Item will be exposed as a dimmable white bulb +* "ColorLighting": Item will be exposed as a color bulb + +It is the responsibility of binding developers to categories and default tag their available *Channels*, so that linked Items are automatically exposed with this service. + +You can tag items manually though as well. + +## Exposed names + +Your items labels are used for exposing! +The default naming schema for automatically linked items unfortunately names *Items* like their Channel names, +so usually "Brightness" or "Color". You want to rename those. + +## Configuration + +All options are available in the graphical interface and via textual configuration. +The following configurations can be adjusted. + +Pairing can be turned on and off: + +``` +org.openhab.hueemulation:pairingEnabled=false +``` + +You can define a pairing timeout in seconds. +After that timeout, the `pairingEnabled` is automatically set to `false`. + +``` +org.openhab.hueemulation:pairingTimeout=60 +``` + +Enable the following option in combination with pairing to create a new API key on the fly on every API endpoint, not only via the new-user-create API. + +Necessary for Amazon Echos and other devices where the API key cannot be reset. +After a new installation of openHAB or a configuration pruning the old API keys are gone but Amazon Echos will keep trying with their old, invalid keys. + +``` +org.openhab.hueemulation:createNewUserOnEveryEndpoint=false +``` + +Some Amazon Echo versions only allow V1 Hue bridges (the round ones, not the square ones) to be discovered. +If the following option is enabled in combination with the pairing mode, the service will pretend to be an old Hue bridge. + +This option resets automatically after pairing mode has been switched off by the timeout. + +``` +org.openhab.hueemulation:temporarilyEmulateV1bridge=false +``` + +Permanent V1 bridge emulation (no obvious reason to enable that): + +``` +org.openhab.hueemulation:permanentV1bridge=false +``` + +The hue emulation service will announce its existence via UPNP on every +of the openHAB configured primary addresses (IPv4 and IPv6). + +Usually you do not want to set this option, but change the primary address configuration of openHAB. + +This option allows you to override what addresses are used for the announcement. +You can have multiple comma separated entries. + +``` +org.openhab.hueemulation:discoveryIps=192.168.1.100,::FFFF:A9DB:0D85 +``` + +The hue emulation service supports three types of emulated bulbs. +You need to tell the service which item tag corresponds to which emulated bulb type. +One of the comma separated tags must match for the item to be exposed. +Can be empty to match an item based on other criteria. + +``` +org.openhab.hueemulation:restrictToTagsSwitches=Switchable +org.openhab.hueemulation:restrictToTagsWhiteLights=Lighting +org.openhab.hueemulation:restrictToTagsColorLights=ColorLighting +``` + +The above default assignment means that every item that has the tag "Switchable" will be emulated as a Zigbee Switch. +If you want your switches to be exposed as lights instead (because your Amazon Echo does not support switches for example), you want to have: + +``` +org.openhab.hueemulation:restrictToTagsSwitches=NONE +org.openhab.hueemulation:restrictToTagsWhiteLights=Lighting,Switchable +org.openhab.hueemulation:restrictToTagsColorLights=ColorLighting +``` + +The service tries to expose as much items as possible (greedy), based on some criteria as explained in the section above. +If you want to exclude items, you need to tag them. Define the tags with the following option: + +``` +org.openhab.hueemulation:ignoreItemsWithTags=internal +``` + +The default is to not expose any items that have the "internal" tag assigned. +You want this tag for all items that are purely used for rules, as proxy items etc. + +## Troubleshooting + +Some devices like Amazon Echo, Google Home and all Philips devices (TVs, Apps) expect a Hue bridge to run on port 80. +You must either + +* port forward your openHAB installation to port 80, + (`iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080`) +* install a reverse proxy on port 80, for example nginx with the following configuration: + ``` + server { + listen 80; + proxy_pass http://localhost:8080/; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + ``` +* or let openHAB run on port 80 (the entire java process requires elevated privileges). + +Please open port 80 tcp and port 1900 udp in your firewall installation. + +You can test if the hue emulation does its job by enabling pairing mode including the option "Amazon Echo device discovery fix". + +1. Navigate with your browser to "http://your-openhab-ip/description.xml" to check the discovery response. + Check the IP address in there. +2. Navigate with your browser to "http://your-openhab-ip/api/status" to check the self test report. + +If you use the port forwarding way, the self-test page will not be able to correctly determine if your installation works on port 80. +A reverse proxy is recommended. + +Depending on the firmware version of your Amazon Echo, it may not support colored bulbs or switches. +Please assign "ColorLighting" and "Switchable" to the `WhiteLights` type as explained above. + +Also note that Amazon Echos are stubborn as. +You might need to remove all former recognized devices multiple times and perform the search via different Echos and also the web or mobile application. + +It might help to (temporarly) lower the emulated bridge version in the configuration as described above, + +## Text configuration example + +The item label will be used as the Hue device name. Please be aware that textual defined items are generally a bad idea. +In this case renaming items in Hue compatible Apps will fail. + +``` +Switch TestSwitch "Kitchen Switch" [ "Switchable" ] {channel="..."} +Color TestColorBulb "Bathroom" [ "ColorLighting" ] {channel="..."} +Dimmer TestDimmer "Hallway" [ "Lighting" ] {channel="..."} +``` diff --git a/bundles/org.openhab.io.hueemulation/doc/Philips_Hue_Bridge.jpg b/bundles/org.openhab.io.hueemulation/doc/Philips_Hue_Bridge.jpg new file mode 100644 index 0000000000000..6d49b53444d73 Binary files /dev/null and b/bundles/org.openhab.io.hueemulation/doc/Philips_Hue_Bridge.jpg differ diff --git a/bundles/org.openhab.io.hueemulation/pom.xml b/bundles/org.openhab.io.hueemulation/pom.xml new file mode 100644 index 0000000000000..18439c0894713 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/pom.xml @@ -0,0 +1,60 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.io.hueemulation + + openHAB Add-ons :: Bundles :: Hue Emulation Service + + + + + org.glassfish.grizzly + grizzly-http-server + 2.4.4 + test + + + org.glassfish.grizzly.osgi + grizzly-httpservice + 2.4.4 + test + + + javax.servlet + javax.servlet-api + + + + + org.glassfish.jersey.containers + jersey-container-grizzly2-http + 2.28 + test + + + org.glassfish.jersey.inject + jersey-hk2 + 2.28 + test + + + org.jupnp + org.jupnp + 2.5.2 + provided + + + + + org.glassfish.jersey.*;resolution:="optional" + + diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/glassfish/jersey/servlet/init/JerseyServletContainerInitializer.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/glassfish/jersey/servlet/init/JerseyServletContainerInitializer.java new file mode 100644 index 0000000000000..26e16582dde2a --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/glassfish/jersey/servlet/init/JerseyServletContainerInitializer.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.glassfish.jersey.servlet.init; + +import java.util.Set; + +import javax.servlet.ServletContainerInitializer; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; + +/** + * Shut up pax-web, which scans for this initializer class and loudly complains if it can't be found. + * + * @author David Graeff - Initial contribution + */ +public class JerseyServletContainerInitializer implements ServletContainerInitializer { + @Override + public void onStartup(Set> c, ServletContext ctx) throws ServletException { + + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/ConfigStore.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/ConfigStore.java new file mode 100644 index 0000000000000..fdb0a256f4b2c --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/ConfigStore.java @@ -0,0 +1,358 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.core.ConfigurableService; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.common.ThreadPoolManager; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.Metadata; +import org.eclipse.smarthome.core.items.MetadataKey; +import org.eclipse.smarthome.core.items.MetadataRegistry; +import org.eclipse.smarthome.core.net.CidrAddress; +import org.eclipse.smarthome.core.net.NetUtil; +import org.eclipse.smarthome.core.net.NetworkAddressService; +import org.openhab.io.hueemulation.internal.dto.HueAuthorizedConfig; +import org.openhab.io.hueemulation.internal.dto.HueDataStore; +import org.openhab.io.hueemulation.internal.dto.HueGroupEntry; +import org.openhab.io.hueemulation.internal.dto.HueLightEntry; +import org.openhab.io.hueemulation.internal.dto.HueRuleEntry; +import org.openhab.io.hueemulation.internal.dto.HueSensorEntry; +import org.openhab.io.hueemulation.internal.dto.response.HueSuccessGeneric; +import org.openhab.io.hueemulation.internal.dto.response.HueSuccessResponseStateChanged; +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Modified; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventAdmin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * This component sets up the hue data store and gets the service configuration. + * It also determines the address for the upnp service by the given configuration. + *

+ * Also manages the pairing timeout. The service is restarted after a pairing timeout, due to the ConfigAdmin + * configuration change. + *

+ * This is a central component and required by all other components and may not + * depend on anything in this bundle. + * + * @author David Graeff - Initial contribution + */ +@Component(immediate = false, service = { ConfigStore.class }, configurationPid = { + HueEmulationService.CONFIG_PID }, property = { "com.eclipsesource.jaxrs.publish=false", + ConfigurableService.SERVICE_PROPERTY_DESCRIPTION_URI + "=io:hueemulation", + ConfigurableService.SERVICE_PROPERTY_CATEGORY + "=io", + ConfigurableService.SERVICE_PROPERTY_LABEL + "=Hue Emulation" }) +@NonNullByDefault +public class ConfigStore { + + public static final String METAKEY = "HUEEMU"; + public static final String EVENT_ADDRESS_CHANGED = "ESH_EMU_CONFIG_ADDR_CHANGED"; + + private final Logger logger = LoggerFactory.getLogger(ConfigStore.class); + + public HueDataStore ds = new HueDataStore(); + + protected @NonNullByDefault({}) ScheduledExecutorService scheduler; + private @Nullable ScheduledFuture pairingOffFuture; + private @Nullable ScheduledFuture writeUUIDFuture; + + /** + * This is the main gson instance, to be obtained by all components that operate on the dto data fields + */ + public final Gson gson = new GsonBuilder().registerTypeAdapter(HueLightEntry.class, new HueLightEntry.Serializer()) + .registerTypeAdapter(HueSensorEntry.class, new HueSensorEntry.Serializer()) + .registerTypeAdapter(HueRuleEntry.Condition.class, new HueRuleEntry.SerializerCondition()) + .registerTypeAdapter(HueAuthorizedConfig.class, new HueAuthorizedConfig.Serializer()) + .registerTypeAdapter(HueSuccessGeneric.class, new HueSuccessGeneric.Serializer()) + .registerTypeAdapter(HueSuccessResponseStateChanged.class, new HueSuccessResponseStateChanged.Serializer()) + .registerTypeAdapter(HueGroupEntry.class, new HueGroupEntry.Serializer(this)).create(); + + @Reference + protected @NonNullByDefault({}) ConfigurationAdmin configAdmin; + + @Reference + protected @NonNullByDefault({}) NetworkAddressService networkAddressService; + + @Reference + protected @NonNullByDefault({}) MetadataRegistry metadataRegistry; + + @Reference + protected @NonNullByDefault({}) EventAdmin eventAdmin; + + //// objects, set within activate() + private Set discoveryIps = Collections.emptySet(); + protected volatile @NonNullByDefault({}) HueEmulationConfig config; + + public Set switchFilter = Collections.emptySet(); + public Set colorFilter = Collections.emptySet(); + public Set whiteFilter = Collections.emptySet(); + public Set ignoreItemsFilter = Collections.emptySet(); + + private int highestAssignedHueID = 1; + + public ConfigStore() { + scheduler = ThreadPoolManager.getScheduledPool(ThreadPoolManager.THREAD_POOL_NAME_COMMON); + } + + /** + * For test dependency injection + * + * @param networkAddressService The network address service + * @param configAdmin The configuration admin service + * @param metadataRegistry The metadataRegistry service + */ + public ConfigStore(NetworkAddressService networkAddressService, ConfigurationAdmin configAdmin, + @Nullable MetadataRegistry metadataRegistry, ScheduledExecutorService scheduler) { + this.networkAddressService = networkAddressService; + this.configAdmin = configAdmin; + this.metadataRegistry = metadataRegistry; + this.scheduler = scheduler; + } + + @Activate + public void activate(Map properties) { + this.config = new Configuration(properties).as(HueEmulationConfig.class); + + determineHighestAssignedHueID(); + + if (config.uuid.isEmpty()) { + config.uuid = UUID.randomUUID().toString(); + writeUUIDFuture = scheduler.schedule(() -> { + logger.info("No unique ID assigned yet. Assigning {} and restarting...", config.uuid); + WriteConfig.setUUID(configAdmin, config.uuid); + }, 100, TimeUnit.MILLISECONDS); + return; + } else { + modified(properties); + } + } + + private @Nullable InetAddress byName(@Nullable String address) { + if (address == null) { + return null; + } + try { + return InetAddress.getByName(address); + } catch (UnknownHostException e) { + logger.warn("Given IP address could not be resolved: {}", address, e); + return null; + } + } + + @Modified + public void modified(Map properties) { + this.config = new Configuration(properties).as(HueEmulationConfig.class); + + switchFilter = Collections.unmodifiableSet( + Stream.of(config.restrictToTagsSwitches.split(",")).map(String::trim).collect(Collectors.toSet())); + + colorFilter = Collections.unmodifiableSet( + Stream.of(config.restrictToTagsColorLights.split(",")).map(String::trim).collect(Collectors.toSet())); + + whiteFilter = Collections.unmodifiableSet( + Stream.of(config.restrictToTagsWhiteLights.split(",")).map(String::trim).collect(Collectors.toSet())); + + ignoreItemsFilter = Collections.unmodifiableSet( + Stream.of(config.ignoreItemsWithTags.split(",")).map(String::trim).collect(Collectors.toSet())); + + // Use either the user configured + InetAddress configuredAddress = null; + int networkPrefixLength = 24; // Default for most networks: 255.255.255.0 + + if (config.discoveryIps != null) { + discoveryIps = Collections.unmodifiableSet(Stream.of(config.discoveryIps.split(",")).map(String::trim) + .map(this::byName).filter(e -> e != null).collect(Collectors.toSet())); + } else { + discoveryIps = new HashSet<>(); + configuredAddress = byName(networkAddressService.getPrimaryIpv4HostAddress()); + if (configuredAddress != null) { + discoveryIps.add(configuredAddress); + } + for (CidrAddress a : NetUtil.getAllInterfaceAddresses()) { + if (a.getAddress().equals(configuredAddress)) { + networkPrefixLength = a.getPrefix(); + } else { + discoveryIps.add(a.getAddress()); + } + } + } + + if (discoveryIps.size() == 0) { + logger.warn("No interface IP address configured or found. Hue emulation service disabled!"); + return; + } + + if (configuredAddress == null) { + configuredAddress = InetAddress.getLoopbackAddress(); + } + + // Get and apply configurations + ds.config.createNewUserOnEveryEndpoint = config.createNewUserOnEveryEndpoint; + ds.config.networkopenduration = config.pairingTimeout; + ds.config.devicename = config.devicename; + + ds.config.uuid = config.uuid; + ds.config.bridgeid = config.uuid.replace("-", "").toUpperCase(); + if (ds.config.bridgeid.length() > 12) { + ds.config.bridgeid = ds.config.bridgeid.substring(0, 12); + } + + if (config.permanentV1bridge) { + ds.config.makeV1bridge(); + } + + setLinkbutton(config.pairingEnabled, config.createNewUserOnEveryEndpoint, config.temporarilyEmulateV1bridge); + ds.config.mac = NetworkUtils.getMAC(configuredAddress); + ds.config.ipaddress = configuredAddress.getHostAddress(); + ds.config.netmask = networkPrefixLength < 32 ? NetUtil.networkPrefixLengthToNetmask(networkPrefixLength) + : "255.255.255.0"; + + if (eventAdmin != null) { + eventAdmin.postEvent(new Event(EVENT_ADDRESS_CHANGED, Collections.emptyMap())); + } + } + + @Deactivate + public void deactive(int reason) { + ScheduledFuture future = pairingOffFuture; + if (future != null) { + future.cancel(false); + } + future = writeUUIDFuture; + if (future != null) { + future.cancel(false); + } + } + + protected void determineHighestAssignedHueID() { + for (Metadata metadata : metadataRegistry.getAll()) { + if (!metadata.getUID().getNamespace().equals(METAKEY)) { + continue; + } + try { + int hueId = Integer.parseInt(metadata.getValue()); + if (hueId > highestAssignedHueID) { + highestAssignedHueID = hueId; + } + } catch (NumberFormatException e) { + logger.warn("A non numeric hue ID '{}' was assigned. Ignoring!", metadata.getValue()); + } + } + } + + /** + * Although hue IDs are strings, a lot of implementations out there assume them to be numbers. Therefore + * we map each item to a number and store that in the meta data provider. + * + * @param item The item to map + * @return A stringified integer number + */ + public String mapItemUIDtoHueID(Item item) { + MetadataKey key = new MetadataKey(METAKEY, item.getUID()); + Metadata metadata = metadataRegistry.get(key); + int hueId = 0; + if (metadata != null) { + try { + hueId = Integer.parseInt(metadata.getValue()); + } catch (NumberFormatException e) { + logger.warn("A non numeric hue ID '{}' was assigned. Ignore and reassign a different id now!", + metadata.getValue()); + } + } + if (hueId == 0) { + ++highestAssignedHueID; + hueId = highestAssignedHueID; + metadataRegistry.add(new Metadata(key, String.valueOf(hueId), null)); + } + + return String.valueOf(hueId); + } + + public boolean isReady() { + return discoveryIps.size() > 0; + } + + public HueEmulationConfig getConfig() { + return config; + } + + public int getHighestAssignedHueID() { + return highestAssignedHueID; + } + + /** + * Sets the link button state. + * + * Starts a pairing timeout thread if set to true. + * Stops any already running timers. + * + * @param linkbutton New link button state + */ + public void setLinkbutton(boolean linkbutton, boolean createUsersOnEveryEndpoint, + boolean temporarilyEmulateV1bridge) { + ds.config.linkbutton = linkbutton; + config.createNewUserOnEveryEndpoint = createUsersOnEveryEndpoint; + if (temporarilyEmulateV1bridge) { + ds.config.makeV1bridge(); + } else if (!config.permanentV1bridge) { + ds.config.makeV2bridge(); + } + ScheduledFuture future = pairingOffFuture; + if (future != null) { + future.cancel(false); + } + if (!linkbutton) { + logger.info("Hue Emulation pairing disabled"); + return; + } + + logger.info("Hue Emulation pairing enabled for {}s", ds.config.networkopenduration); + pairingOffFuture = scheduler.schedule(() -> { + logger.info("Hue Emulation disable pairing..."); + if (!config.permanentV1bridge) { // Restore bridge version + ds.config.makeV2bridge(); + } + config.createNewUserOnEveryEndpoint = false; + config.temporarilyEmulateV1bridge = false; + WriteConfig.unsetPairingMode(configAdmin); + }, ds.config.networkopenduration * 1000, TimeUnit.MILLISECONDS); + } + + public Set getDiscoveryIps() { + return discoveryIps; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/DeviceType.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/DeviceType.java new file mode 100644 index 0000000000000..57e2e5b2c3e57 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/DeviceType.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal; + +/** + * The pure item type is not enough to decide how we expose an item. + * We need to consider the assigned tags and category as well. This + * computed device type is stored next to the item itself. + * + * @author David Graeff - Initial contribution + */ +public enum DeviceType { + SwitchType, + WhiteType, + WhiteTemperatureType, + ColorType +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationConfig.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationConfig.java new file mode 100644 index 0000000000000..c2cfbdd5902ab --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationConfig.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The configuration for {@link HueEmulationService}. + * + * @author David Graeff - Initial Contribution + */ +@NonNullByDefault +public class HueEmulationConfig { + public boolean pairingEnabled = false; + public static final String CONFIG_PAIRING_ENABLED = "pairingEnabled"; + + /** + * The Amazon echos have no means to recreate a new api key and they don't care about the 403-forbidden http status + * code. If the addon has pruned its api-key list, echos will not be able to discover new devices. Set this option + * to just create a new user on the fly. + */ + public boolean createNewUserOnEveryEndpoint = false; + public static final String CONFIG_CREATE_NEW_USER_ON_THE_FLY = "createNewUserOnEveryEndpoint"; + public boolean temporarilyEmulateV1bridge = false; + public static final String CONFIG_EMULATE_V1 = "temporarilyEmulateV1bridge"; + public boolean permanentV1bridge = false; + + /** Pairing timeout in seconds */ + public int pairingTimeout = 60; + public @Nullable String discoveryIps; + public int discoveryHttpPort = 0; + /** Comma separated list of tags */ + public String restrictToTagsSwitches = "Switchable"; + /** Comma separated list of tags */ + public String restrictToTagsColorLights = "ColorLighting"; + /** Comma separated list of tags */ + public String restrictToTagsWhiteLights = "Lighting"; + /** Comma separated list of tags */ + public String ignoreItemsWithTags = "internal"; + + public static final String CONFIG_UUID = "uuid"; + public String uuid = ""; + public String devicename = "openHAB"; +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationService.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationService.java new file mode 100644 index 0000000000000..dce7b2bc1bb92 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationService.java @@ -0,0 +1,215 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.stream.Collectors; + +import javax.servlet.ServletException; +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.core.Application; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.servlet.ServletContainer; +import org.glassfish.jersey.servlet.ServletProperties; +import org.openhab.io.hueemulation.internal.rest.ConfigurationAccess; +import org.openhab.io.hueemulation.internal.rest.LightsAndGroups; +import org.openhab.io.hueemulation.internal.rest.Rules; +import org.openhab.io.hueemulation.internal.rest.Scenes; +import org.openhab.io.hueemulation.internal.rest.Schedules; +import org.openhab.io.hueemulation.internal.rest.Sensors; +import org.openhab.io.hueemulation.internal.rest.StatusResource; +import org.openhab.io.hueemulation.internal.rest.UserManagement; +import org.openhab.io.hueemulation.internal.upnp.UpnpServer; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Modified; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventAdmin; +import org.osgi.service.event.EventConstants; +import org.osgi.service.event.EventHandler; +import org.osgi.service.http.HttpService; +import org.osgi.service.http.NamespaceException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides a Hue compatible HTTP REST API on /api. + *

+ * References all different rest endpoints implemented as JAX-RS annotated classes in the sub-package "rest". + * Those are very modular and have (almost) no inter-dependencies. The UPnP related part is encapsulated in + * the also referenced {@link UpnpServer}. + *

+ * openHAB items via the {@link org.eclipse.smarthome.core.items.ItemRegistry} are for example mapped to + * /api/{username}/lights and /api/{username}/groups in {@link LightsAndGroups}. + *

+ * The user management is realized in the {@link UserManagement} component, that is referenced by almost all + * other components and is the only inter-component dependency. + * + * @author David Graeff - Initial Contribution + */ +@NonNullByDefault +@Component(immediate = true, service = { HueEmulationService.class }, property = { + "com.eclipsesource.jaxrs.publish=false" }) +public class HueEmulationService implements EventHandler { + + public static final String CONFIG_PID = "org.openhab.hueemulation"; + public static final String RESTAPI_PATH = "/api"; + + @ApplicationPath(RESTAPI_PATH) + public static class JerseyApplication extends Application { + + } + + public class LogAccessInterceptor implements ContainerResponseFilter { + @NonNullByDefault({}) + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) + throws IOException { + + if (!logger.isDebugEnabled()) { + return; + } + + InputStream stream = requestContext.getEntityStream(); + String body = stream != null + ? new BufferedReader(new InputStreamReader(stream)).lines().collect(Collectors.joining("\n")) + : ""; + + logger.debug("REST request {} {}: {}", requestContext.getMethod(), requestContext.getUriInfo().getPath(), + body); + logger.debug("REST response: {}", responseContext.getEntity()); + } + + } + + private final Logger logger = LoggerFactory.getLogger(HueEmulationService.class); + private final LogAccessInterceptor accessInterceptor = new LogAccessInterceptor(); + + //// Required services //// + // Don't fail the service if the upnp server does not come up + // That part is required for discovery only but does not affect already configured hue applications + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY) + protected @NonNullByDefault({}) UpnpServer discovery; + @Reference + protected @NonNullByDefault({}) ConfigStore cs; + @Reference + protected @NonNullByDefault({}) UserManagement userManagement; + @Reference + protected @NonNullByDefault({}) ConfigurationAccess configurationAccess; + @Reference + protected @NonNullByDefault({}) LightsAndGroups lightItems; + @Reference + protected @NonNullByDefault({}) Sensors sensors; + @Reference + protected @NonNullByDefault({}) Scenes scenes; + @Reference + protected @NonNullByDefault({}) Schedules schedules; + @Reference + protected @NonNullByDefault({}) Rules rules; + @Reference + protected @NonNullByDefault({}) StatusResource statusResource; + + @Reference + protected @NonNullByDefault({}) HttpService httpService; + private @NonNullByDefault({}) ServiceRegistration eventHandler; + + @Activate + protected void activate(BundleContext bc) { + Dictionary properties = new Hashtable<>(); + properties.put(EventConstants.EVENT_TOPIC, ConfigStore.EVENT_ADDRESS_CHANGED); + eventHandler = bc.registerService(EventHandler.class, this, properties); + if (cs.isReady()) { + handleEvent(null); + } + } + + // Don't restart the service on config change + @Modified + protected void modified() { + } + + @Deactivate + protected void deactivate() { + try { + if (eventHandler != null) { + eventHandler.unregister(); + } + } catch (IllegalStateException ignore) { + } + try { + httpService.unregister(RESTAPI_PATH); + } catch (IllegalArgumentException ignore) { + } + } + + /** + * We have a hard dependency on the {@link ConfigStore} and that it has initialized the Hue DataStore config + * completely. That initialization happens asynchronously and therefore we cannot rely on OSGi activate/modified + * state changes. Instead the {@link EventAdmin} is used and we listen for the + * {@link ConfigStore#EVENT_ADDRESS_CHANGED} event that is fired as soon as the config is ready. + */ + @Override + public void handleEvent(@Nullable Event event) { + try { // Only receive this event once + eventHandler.unregister(); + eventHandler = null; + } catch (IllegalStateException ignore) { + } + + ResourceConfig resourceConfig = ResourceConfig.forApplicationClass(JerseyApplication.class); + resourceConfig.property(ServerProperties.APPLICATION_NAME, "HueEmulation"); + // don't look for implementations described by META-INF/services/* + resourceConfig.property(ServerProperties.METAINF_SERVICES_LOOKUP_DISABLE, true); + // disable auto discovery on server, as it's handled via OSGI + resourceConfig.property(ServerProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true); + + resourceConfig.property(ServerProperties.PROCESSING_RESPONSE_ERRORS_ENABLED, true); + + resourceConfig.registerInstances(userManagement, configurationAccess, lightItems, sensors, scenes, schedules, + rules, statusResource, accessInterceptor); + + try { + Hashtable initParams = new Hashtable<>(); + initParams.put("com.sun.jersey.api.json.POJOMappingFeature", "false"); + initParams.put(ServletProperties.PROVIDER_WEB_APP, "false"); + httpService.registerServlet(RESTAPI_PATH, new ServletContainer(resourceConfig), initParams, null); + if (discovery == null) { + logger.warn("The UPnP Server service has not been started!"); + } else if (!discovery.upnpAnnouncementThreadRunning()) { + discovery.handleEvent(null); + } + statusResource.startUpnpSelfTest(); + logger.info("Hue Emulation service available under {}", RESTAPI_PATH); + } catch (ServletException | NamespaceException e) { + logger.warn("Could not start Hue Emulation service: {}", e.getMessage(), e); + } + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/NetworkUtils.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/NetworkUtils.java new file mode 100644 index 0000000000000..ffb46e93c6fb0 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/NetworkUtils.java @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.io.hueemulation.internal.dto.response.HueResponse; +import org.openhab.io.hueemulation.internal.dto.response.HueResponse.HueErrorMessage; +import org.openhab.io.hueemulation.internal.dto.response.HueResponseSuccessSimple; +import org.openhab.io.hueemulation.internal.dto.response.HueSuccessGeneric; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +/** + * Network utility methods + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class NetworkUtils { + /** + * Try to get the ethernet interface MAC for the network interface that belongs to the given IP address. + * Returns a default MAC on any failure. + * + * @param address IP address + * @return A MAC of the form "00:00:88:00:bb:ee" + */ + static String getMAC(InetAddress address) { + NetworkInterface networkInterface; + final byte[] mac; + try { + networkInterface = NetworkInterface.getByInetAddress(address); + if (networkInterface == null) { + return "00:00:88:00:bb:ee"; + } + mac = networkInterface.getHardwareAddress(); + if (mac == null) { + return "00:00:88:00:bb:ee"; + } + } catch (SocketException e) { + return "00:00:88:00:bb:ee"; + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < mac.length; i++) { + sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? ":" : "")); + } + return sb.toString(); + } + + /** + * Adds cors headers to the given response and returns it. + */ + public static ResponseBuilder ResponseWithCors(ResponseBuilder response) { + return response.encoding(StandardCharsets.UTF_8.name()) // + .header("Access-Control-Allow-Origin", "*") + .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization") + .header("Access-Control-Allow-Credentials", "true") + .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD") + .header("Access-Control-Max-Age", "1209600"); + } + + /** + * Creates a json response with the correct Hue error code + * + * @param gson A gson instance + * @param uri The original uri of the request + * @param type Any of HueResponse.* + * @param message A message + * @return + */ + public static Response singleError(Gson gson, UriInfo uri, int type, String message) { + HueResponse e = new HueResponse(new HueErrorMessage(type, uri.getPath().replace("/api", ""), message)); + String str = gson.toJson(Collections.singleton(e), new TypeToken>() { + }.getType()); + int httpCode = 500; + switch (type) { + case HueResponse.UNAUTHORIZED: + httpCode = 403; + break; + case HueResponse.METHOD_NOT_ALLOWED: + httpCode = 405; + break; + case HueResponse.NOT_AVAILABLE: + httpCode = 404; + break; + case HueResponse.ARGUMENTS_INVALID: + case HueResponse.LINK_BUTTON_NOT_PRESSED: + httpCode = 200; + break; + } + return Response.status(httpCode).entity(str).build(); + } + + public static Response singleSuccess(Gson gson, String message, String uriPart) { + List responses = new ArrayList<>(); + responses.add(new HueResponse(new HueSuccessGeneric(message, uriPart))); + return Response.ok(gson.toJson(responses, new TypeToken>() { + }.getType())).build(); + } + + public static Response singleSuccess(Gson gson, String message) { + List responses = new ArrayList<>(); + responses.add(new HueResponseSuccessSimple(message)); + return Response.ok(gson.toJson(responses, new TypeToken>() { + }.getType())).build(); + } + + public static Response successList(Gson gson, List successList) { + List responses = new ArrayList<>(); + for (HueSuccessGeneric s : successList) { + if (s.isValid()) { + responses.add(new HueResponse(s)); + } + } + return Response.ok(gson.toJson(responses, new TypeToken>() { + }.getType())).build(); + } + +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/RuleUtils.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/RuleUtils.java new file mode 100644 index 0000000000000..e25c080e1500a --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/RuleUtils.java @@ -0,0 +1,386 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Random; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.core.Configuration; +import org.openhab.core.automation.Action; +import org.openhab.core.automation.Trigger; +import org.openhab.core.automation.util.ModuleBuilder; +import org.openhab.io.hueemulation.internal.dto.HueDataStore; +import org.openhab.io.hueemulation.internal.dto.HueGroupEntry; +import org.openhab.io.hueemulation.internal.dto.HueLightEntry; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueCommand; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Rule utility methods. The Hue scheduler and Hue rules support is based on the automation engine. + * This class provides methods to convert between Hue entries and automation rules. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class RuleUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(RuleUtils.class); + public static Random random = new Random(); // public for test mock + + /** + * Splits the given base time (pattern "hh:mm[:ss]") on the colons and return the resulting array. + * If an upper time is given (same pattern), a random number between base and upper time is chosen. + * + * @param baseTime A base time with the pattern hh:mm[:ss] + * @param upperTime An optional upper time or null or an empty string + * @return Time components (hour, minute, seconds). + */ + public static String[] computeRandomizedDayTime(String baseTime, @Nullable String upperTime) { + if (!baseTime.matches("[0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2})?")) { + throw new IllegalStateException("Time pattern incorrect. Must be 'hh:mm[:ss]'. " + baseTime); + } + + String randomizedTime[] = baseTime.split(":"); + + if (upperTime != null && !upperTime.isEmpty()) { + String[] upperTimeParts = upperTime.split(":"); + if (!upperTime.matches("[0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2})?") + || randomizedTime.length != upperTimeParts.length) { + throw new IllegalStateException("Random Time pattern incorrect. Must be 'hh:mm[:ss]'. " + upperTime); + } + for (int i = 0; i < randomizedTime.length; ++i) { + int n = Integer.parseInt(randomizedTime[i]); + int n2 = Integer.parseInt(upperTimeParts[i]); + int diff = Math.abs(n2 - n); // Example: 12 and 14 -> diff = 2 + if (diff > 0) { // diff = rnd [0,3) + diff = random.nextInt(diff + 1); + } + randomizedTime[i] = String.valueOf(n2 > n ? n + diff : n2 + diff); + } + } + + return randomizedTime; + } + + /** + * Validates a hue http address used in schedules and hue rules. + * + * @param ds A hue datastore to verify that referred lights/groups do exist + * @param address Relative hue API address. Example: "/api//groups/1/action" or + * "/api//lights/1/state" + * @throws IllegalStateException Thrown if address is invalid + */ + @SuppressWarnings({ "unused", "null" }) + public static void validateHueHttpAddress(HueDataStore ds, String address) throws IllegalStateException { + String[] validation = address.split("/"); + if (validation.length < 6 || !validation[0].isEmpty() || !validation[1].equals("api")) { + throw new IllegalStateException("Given address invalid!"); + } + if ("groups".equals(validation[3]) && "action".equals(validation[5])) { + HueGroupEntry entry = ds.groups.get(validation[4]); + if (entry == null) { + throw new IllegalStateException("Group does not exist: " + validation[4]); + } + } else if ("lights".equals(validation[3]) && "state".equals(validation[5])) { + HueLightEntry entry = ds.lights.get(validation[4]); + if (entry == null) { + throw new IllegalStateException("Light does not exist: " + validation[4]); + } + } else { + throw new IllegalStateException("Can only handle groups and lights"); + } + } + + public static class ConfigHttpAction { + public String url = ""; + public String method = ""; + public String body = ""; + } + + @SuppressWarnings({ "unused", "null" }) + public static @Nullable HueCommand httpActionToHueCommand(HueDataStore ds, Action a, @Nullable String ruleName) { + ConfigHttpAction config = a.getConfiguration().as(ConfigHttpAction.class); + + // Example: "/api//groups/1/action" or "/api//lights/1/state" + String[] validation = config.url.split("/"); + if (validation.length < 6 || !validation[0].isEmpty() || !"api".equals(validation[1])) { + LOGGER.warn("Hue Rule '{}': Given address in action {} invalid!", ruleName, a.getLabel()); + return null; + } + + if ("groups".equals(validation[3]) && "action".equals(validation[5])) { + HueGroupEntry gentry = ds.groups.get(validation[4]); + if (gentry == null) { + LOGGER.warn("Hue Rule '{}': Given address in action {} invalid. Group does not exist: {}", ruleName, + a.getLabel(), validation[4]); + return null; + } + return new HueCommand(config.url, config.method, config.body); + } else if ("lights".equals(validation[3]) && "state".equals(validation[5])) { + HueLightEntry lentry = ds.lights.get(validation[4]); + if (lentry == null) { + LOGGER.warn("Hue Rule '{}': Given address in action {} invalid. Light does not exist: {}", ruleName, + a.getLabel(), validation[4]); + return null; + } + return new HueCommand(config.url, config.method, config.body); + } else { + LOGGER.warn("Hue Rule '{}': Given address in action {} invalid. Can only handle lights and groups, not {}", + ruleName, a.getLabel(), validation[3]); + return null; + } + } + + public static Action createHttpAction(HueCommand command, String id) { + final Configuration actionConfig = new Configuration(); + actionConfig.put("method", command.method); + actionConfig.put("url", command.address); + actionConfig.put("body", command.body); + return ModuleBuilder.createAction().withId(id).withTypeUID("rules.HttpAction").withConfiguration(actionConfig) + .build(); + } + + // Recurring pattern "W[bbb]/T[hh]:[mm]:[ss]A[hh]:[mm]:[ss]" + private static Trigger createRecurringFromTimeString(String localtime) { + Pattern timePattern = Pattern.compile("W(.*)/T(.*)A(.*)"); + Matcher m = timePattern.matcher(localtime); + if (!m.matches() || m.groupCount() < 3) { + throw new IllegalStateException("Recurring time pattern incorrect"); + } + + String weekdays = m.group(1); + String time = m.group(2); + String randomize = m.group(3); + + // Monday = 64, Tuesday = 32, Wednesday = 16, Thursday = 8, Friday = 4, Saturday = 2, Sunday = 1 + int weekdaysBinaryEncoded = Integer.valueOf(weekdays); + // For the last part of the cron expression ("day"): + // A comma separated list of days starting with 0=sunday to 7=sunday + List cronWeekdays = new ArrayList<>(); + for (int bin = 64, c = 1; bin > 0; bin /= 2, c += 1) { + if (weekdaysBinaryEncoded / bin == 1) { + weekdaysBinaryEncoded = weekdaysBinaryEncoded % bin; + cronWeekdays.add(String.valueOf(c)); + } + } + String hourMinSec[] = RuleUtils.computeRandomizedDayTime(time, randomize); + + // Cron expression: min hour day month weekdays + String cronExpression = hourMinSec[1] + " " + hourMinSec[0] + " * * " + String.join(",", cronWeekdays); + + final Configuration triggerConfig = new Configuration(); + triggerConfig.put("cronExpression", cronExpression); + return ModuleBuilder.createTrigger().withId("crontrigger").withTypeUID("timer.GenericCronTrigger") + .withConfiguration(triggerConfig).build(); + } + + // Timer pattern: R[nn]/PT[hh]:[mm]:[ss]A[hh]:[mm]:[ss] + private static Trigger createTimerFromTimeString(String localtime) { + Pattern timePattern = Pattern.compile("R(.*)/PT(.*)A(.*)"); + Matcher m = timePattern.matcher(localtime); + if (!m.matches() || m.groupCount() < 3) { + throw new IllegalStateException("Timer pattern incorrect"); + } + + String run = m.group(1); + String time = m.group(2); + String randomize = m.group(3); + + final Configuration triggerConfig = new Configuration(); + if (!time.matches("[0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2})?")) { + throw new IllegalStateException("Time pattern incorrect. Must be 'hh:mm[:ss]'. " + time); + } + triggerConfig.put("time", time); + if (randomize.matches("[0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2})?")) { + triggerConfig.put("randomizeTime", randomize); + } + if (!run.isEmpty()) { + if (!run.matches("[0-9]{1,2}")) { + throw new IllegalStateException("Run pattern incorrent. Must be a number'. " + run); + } else { + triggerConfig.put("repeat", run); + } + } else { // Infinite + triggerConfig.put("repeat", "-1"); + } + + return ModuleBuilder.createTrigger().withId("timertrigger").withTypeUID("timer.TimerTrigger") + .withConfiguration(triggerConfig).build(); + } + + // Absolute date/time pattern "[YYYY]:[MM]:[DD]T[hh]:[mm]:[ss]A[hh]:[mm]:[ss]" + private static Trigger createAbsoluteDateTimeFromTimeString(String localtime) { + Pattern timePattern = Pattern.compile("(.*)T(.*)A(.*)"); + Matcher m = timePattern.matcher(localtime); + if (!m.matches() || m.groupCount() < 3) { + throw new IllegalStateException("Absolute date/time pattern incorrect"); + } + + String date = m.group(1); + String time = m.group(2); + if (!date.matches("[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}")) { + throw new IllegalStateException("Date pattern incorrect. Must be 'yyyy-mm-dd'. " + date); + } + if (!time.matches("[0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2})?")) { + throw new IllegalStateException("Time pattern incorrect. Must be 'hh:mm[:ss]'. " + time); + } + final Configuration triggerConfig = new Configuration(); + + triggerConfig.put("date", date); + triggerConfig.put("time", time); + + time = m.group(3); + if (time.matches("[0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2})?")) { + triggerConfig.put("randomizeTime", time); + } + + return ModuleBuilder.createTrigger().withId("absolutetrigger").withTypeUID("timer.AbsoluteDateTimeTrigger") + .withConfiguration(triggerConfig).build(); + } + + /** + * Creates a trigger based on the given time string. + * According to the Hue + * documentation this can be: + *

+ *

    + *
  • Absolute time [YYYY]-[MM]-[DD]T[hh]:[mm]:[ss] ([date]T[time]) + *
  • Randomized time [YYYY]:[MM]:[DD]T[hh]:[mm]:[ss]A[hh]:[mm]:[ss] ([date]T[time]A[time]) + *
  • Recurring times W[bbb]/T[hh]:[mm]:[ss] + *
  • Every day of the week given by bbb at given time + *
  • Recurring randomized times W[bbb]/T[hh]:[mm]:[ss]A[hh]:[mm]:[ss] + *
  • Every weekday given by bbb at given left side time, randomized by right side time. Right side time has to be + * smaller than 12 hours + *
  • + *
      + * Timers + *
    • PT[hh]:[mm]:[ss] Timer, expiring after given time + *
    • PT[hh]:[mm]:[ss] Timer, expiring after given time + *
    • PT[hh]:[mm]:[ss]A[hh]:[mm]:[ss] Timer with random element + *
    • R[nn]/PT[hh]:[mm]:[ss] Recurring timer + *
    • R/PT[hh]:[mm]:[ss] Recurring timer + *
    • R[nn]/PT[hh]:[mm]:[ss]A[hh]:[mm]:[ss] Recurring timer with random element + *
    + *
+ * + * @param localtimeParameter An absolute time or recurring time or timer pattern + * @return A trigger based on {@link org.openhab.io.hueemulation.internal.automation.AbsoluteDateTimeTriggerHandler} + * or {@link org.openhab.io.hueemulation.internal.automation.TimerTriggerHandler} + */ + public static Trigger createTriggerForTimeString(final String localtimeParameter) throws IllegalStateException { + String localtime = localtimeParameter; + Trigger ruleTrigger; + + // Normalize timer patterns + if (localtime.startsWith("PT")) { + localtime = "R1/" + localtime; + } + if (!localtime.contains("A")) { + localtime += "A"; + } + + // Recurring pattern "W[bbb]/T[hh]:[mm]:[ss]A[hh]:[mm]:[ss]" + if (localtime.startsWith("W")) { + ruleTrigger = createRecurringFromTimeString(localtime); + } // Timer pattern: R[nn]/PT[hh]:[mm]:[ss]A[hh]:[mm]:[ss] + else if (localtime.startsWith("R")) { + ruleTrigger = createTimerFromTimeString(localtime); + } // Absolute date/time pattern "[YYYY]:[MM]:[DD]T[hh]:[mm]:[ss]A[hh]:[mm]:[ss]" + else { + ruleTrigger = createAbsoluteDateTimeFromTimeString(localtime); + } + + return ruleTrigger; + } + + public static class TimerConfig { + public String time = ""; + public String randomizeTime = ""; + public @Nullable Integer repeat; + } + + public static @Nullable String timeStringFromTrigger(List triggers) { + Optional trigger; + + trigger = triggers.stream().filter(p -> p.getId().equals("crontrigger")).findFirst(); + if (trigger.isPresent()) { + String cronParts[] = ((String) trigger.get().getConfiguration().get("cronExpression")).split(" "); + if (cronParts.length != 5) { + LOGGER.warn("Cron trigger has no valid cron expression: {}", String.join(",", cronParts)); + return null; + } + // Monday = 64, Tuesday = 32, Wednesday = 16, Thursday = 8, Friday = 4, Saturday = 2, Sunday = 1 + int weekdays = 0; + String[] cronWeekdays = cronParts[4].split(","); + for (String wdayT : cronWeekdays) { + int wday = Integer.parseInt(wdayT); + switch (wday) { + case 0: + case 7: + weekdays += 1; + break; + case 1: + weekdays += 64; + break; + case 2: + weekdays += 32; + break; + case 3: + weekdays += 16; + break; + case 4: + weekdays += 8; + break; + case 5: + weekdays += 4; + break; + case 6: + weekdays += 2; + break; + } + } + return String.format("W%d/T%s:%s:00", weekdays, cronParts[1], cronParts[0]); + } + + trigger = triggers.stream().filter(p -> p.getId().equals("timertrigger")).findFirst(); + if (trigger.isPresent()) { + + TimerConfig c = trigger.get().getConfiguration().as(TimerConfig.class); + if (c.repeat == null) { + return String.format(c.randomizeTime.isEmpty() ? "PT%s" : "PT%sA%s", c.time, c.randomizeTime); + } else if (c.repeat == -1) { + return String.format(c.randomizeTime.isEmpty() ? "R/PT%s" : "R/PT%sA%s", c.time, c.randomizeTime); + } else { + return String.format(c.randomizeTime.isEmpty() ? "R%d/PT%s" : "R%d/PT%sA%s", c.repeat, c.time, + c.randomizeTime); + } + } else { + trigger = triggers.stream().filter(p -> p.getId().equals("absolutetrigger")).findFirst(); + if (trigger.isPresent()) { + String date = (String) trigger.get().getConfiguration().get("date"); + String time = (String) trigger.get().getConfiguration().get("time"); + String randomizeTime = (String) trigger.get().getConfiguration().get("randomizeTime"); + return String.format(randomizeTime == null ? "%sT%s" : "%sT%sA%s", date, time, randomizeTime); + } else { + LOGGER.warn("No recognised trigger type"); + return null; + } + } + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/StateUtils.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/StateUtils.java new file mode 100644 index 0000000000000..171adaa868bf0 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/StateUtils.java @@ -0,0 +1,416 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.library.CoreItemFactory; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.openhab.io.hueemulation.internal.dto.AbstractHueState; +import org.openhab.io.hueemulation.internal.dto.HueStateBulb; +import org.openhab.io.hueemulation.internal.dto.HueStateColorBulb; +import org.openhab.io.hueemulation.internal.dto.HueStateColorBulb.ColorMode; +import org.openhab.io.hueemulation.internal.dto.HueStatePlug; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueStateChange; +import org.openhab.io.hueemulation.internal.dto.response.HueResponse; +import org.openhab.io.hueemulation.internal.dto.response.HueResponse.HueErrorMessage; +import org.openhab.io.hueemulation.internal.dto.response.HueSuccessResponseStateChanged; + +/** + * This utility class provides all kind of functions to convert between openHAB item states to Hue states and back + * as well as applying a hue state change request to a hue state or openHAB item state. + *

+ * It also provides methods to determine the hue type (plug, white bulb, coloured bulb), given an item. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class StateUtils { + + /** + * Compute the hue state from a given item state and a device type. + * + * @param itemState The item state + * @param deviceType The device type + * @return A hue light state + */ + public static AbstractHueState colorStateFromItemState(State itemState, @Nullable DeviceType deviceType) { + if (deviceType == null) { + return new HueStatePlug(false); + } + AbstractHueState state; + switch (deviceType) { + case ColorType: + if (itemState instanceof HSBType) { + state = new HueStateColorBulb((HSBType) itemState); + } else if (itemState instanceof PercentType) { + state = new HueStateColorBulb((PercentType) itemState, ((PercentType) itemState).intValue() > 0); + } else if (itemState instanceof OnOffType) { + OnOffType t = (OnOffType) itemState; + state = new HueStateColorBulb(t == OnOffType.ON); + } else { + state = new HueStateColorBulb(new HSBType()); + } + break; + case WhiteType: + case WhiteTemperatureType: + if (itemState instanceof HSBType) { + PercentType brightness = ((HSBType) itemState).getBrightness(); + state = new HueStateBulb(brightness, brightness.intValue() > 0); + } else if (itemState instanceof PercentType) { + PercentType brightness = (PercentType) itemState; + state = new HueStateBulb(brightness, brightness.intValue() > 0); + } else if (itemState instanceof OnOffType) { + OnOffType t = (OnOffType) itemState; + state = new HueStateBulb(t == OnOffType.ON); + } else { + state = new HueStateBulb(new PercentType(0), false); + } + break; + case SwitchType: + default: + if (itemState instanceof OnOffType) { + OnOffType t = (OnOffType) itemState; + state = new HueStatePlug(t == OnOffType.ON); + } else { + state = new HueStatePlug(false); + } + } + return state; + } + + /** + * Computes an openHAB item state, given a hue state. + * + *

+ * This only proxies to the respective call + * on the concrete hue state implementation. + * + * @throws IllegalStateException Thrown if the concrete hue state is not yet handled by this method. + */ + public static State itemStateByHueState(AbstractHueState state) throws IllegalStateException { + if (state instanceof HueStateColorBulb) { + return state.as(HueStateColorBulb.class).toHSBType(); + } else if (state instanceof HueStateBulb) { + return state.as(HueStateBulb.class).toBrightnessType(); + } else if (state instanceof HueStatePlug) { + return state.as(HueStatePlug.class).toOnOffType(); + } else { + throw new IllegalStateException(); + } + } + + /** + * An openHAB state is usually also a command. Cast the state. + * + * @throws IllegalStateException Throws if the cast fails. + */ + public static Command commandByItemState(State state) throws IllegalStateException { + if (state instanceof Command) { + return (Command) state; + } else { + throw new IllegalStateException(); + } + } + + /** + * Computes an openHAB command, given a hue state change request. + * + * @param changeRequest The change request + */ + public static @Nullable Command computeCommandByChangeRequest(HueStateChange changeRequest) { + List responses = new ArrayList<>(); + return computeCommandByState(responses, "", new HueStateColorBulb(false), changeRequest); + } + + /** + * Apply the new received state from the REST PUT request. + * + * @param responses Creates a response entry for each success and each error. There is one entry per non-null field + * of {@link HueStateChange} created. + * @param prefix The response entry prefix, for example "/groups/mygroupid/state/" + * @param state The current item state + * @param newState A state change DTO + * @return Return a command computed via the incoming state object. + */ + public static @Nullable Command computeCommandByState(List responses, String prefix, + AbstractHueState state, HueStateChange newState) { + + // Apply new state and collect success, error items + Map successApplied = new TreeMap<>(); + List errorApplied = new ArrayList<>(); + + Command command = null; + if (newState.on != null) { + try { + state.as(HueStatePlug.class).on = newState.on; + command = OnOffType.from(newState.on); + successApplied.put("on", newState.on); + } catch (ClassCastException e) { + errorApplied.add("on"); + } + } + + if (newState.bri != null) { + try { + state.as(HueStateBulb.class).bri = newState.bri; + command = new PercentType((int) (newState.bri * 100.0 / HueStateBulb.MAX_BRI + 0.5)); + successApplied.put("bri", newState.bri); + } catch (ClassCastException e) { + errorApplied.add("bri"); + } + } + + if (newState.bri_inc != null) { + try { + int newBri = state.as(HueStateBulb.class).bri + newState.bri_inc; + if (newBri < 0 || newBri > HueStateBulb.MAX_BRI) { + throw new IllegalArgumentException(); + } + command = new PercentType((int) (newBri * 100.0 / HueStateBulb.MAX_BRI + 0.5)); + successApplied.put("bri", newState.bri); + } catch (ClassCastException e) { + errorApplied.add("bri_inc"); + } catch (IllegalArgumentException e) { + errorApplied.add("bri_inc"); + } + } + + if (newState.sat != null) { + + try { + HueStateColorBulb c = state.as(HueStateColorBulb.class); + c.sat = newState.sat; + c.colormode = ColorMode.hs; + command = c.toHSBType(); + successApplied.put("sat", newState.sat); + } catch (ClassCastException e) { + errorApplied.add("sat"); + } + } + + if (newState.sat_inc != null) { + try { + HueStateColorBulb c = state.as(HueStateColorBulb.class); + int newV = c.sat + newState.sat_inc; + if (newV < 0 || newV > HueStateColorBulb.MAX_SAT) { + throw new IllegalArgumentException(); + } + c.colormode = ColorMode.hs; + c.sat = newV; + command = c.toHSBType(); + successApplied.put("sat", newState.sat); + } catch (ClassCastException e) { + errorApplied.add("sat_inc"); + } catch (IllegalArgumentException e) { + errorApplied.add("sat_inc"); + } + } + + if (newState.hue != null) { + try { + HueStateColorBulb c = state.as(HueStateColorBulb.class); + c.colormode = ColorMode.hs; + c.hue = newState.hue; + command = c.toHSBType(); + successApplied.put("hue", newState.hue); + } catch (ClassCastException e) { + errorApplied.add("hue"); + } + } + + if (newState.hue_inc != null) { + try { + HueStateColorBulb c = state.as(HueStateColorBulb.class); + int newV = c.hue + newState.hue_inc; + if (newV < 0 || newV > HueStateColorBulb.MAX_HUE) { + throw new IllegalArgumentException(); + } + c.colormode = ColorMode.hs; + c.hue = newV; + command = c.toHSBType(); + successApplied.put("hue", newState.hue); + } catch (ClassCastException e) { + errorApplied.add("hue_inc"); + } catch (IllegalArgumentException e) { + errorApplied.add("hue_inc"); + } + } + + if (newState.ct != null) { + try { + // We can't do anything here with a white color temperature. + // The core ESH color type does not support setting it. + + // Adjusting the color temperature implies setting the mode to ct + if (state instanceof HueStateColorBulb) { + HueStateColorBulb c = state.as(HueStateColorBulb.class); + c.sat = 0; + c.colormode = ColorMode.ct; + command = c.toHSBType(); + } + successApplied.put("colormode", ColorMode.ct); + successApplied.put("sat", 0); + successApplied.put("ct", newState.ct); + } catch (ClassCastException e) { + errorApplied.add("ct"); + } + } + + if (newState.ct_inc != null) { + try { + // We can't do anything here with a white color temperature. + // The core ESH color type does not support setting it. + + // Adjusting the color temperature implies setting the mode to ct + if (state instanceof HueStateColorBulb) { + HueStateColorBulb c = state.as(HueStateColorBulb.class); + if (c.colormode != ColorMode.ct) { + c.sat = 0; + command = c.toHSBType(); + successApplied.put("colormode", c.colormode); + } + } + successApplied.put("ct", newState.ct); + } catch (ClassCastException e) { + errorApplied.add("ct_inc"); + } + } + + if (newState.transitiontime != null) { + successApplied.put("transitiontime", newState.transitiontime); // Pretend that worked + } + if (newState.alert != null) { + successApplied.put("alert", newState.alert); // Pretend that worked + } + if (newState.effect != null) { + successApplied.put("effect", newState.effect); // Pretend that worked + } + if (newState.xy != null) { + try { + HueStateColorBulb c = state.as(HueStateColorBulb.class); + c.colormode = ColorMode.xy; + c.bri = state.as(HueStateBulb.class).bri; + c.xy[0] = newState.xy.get(0); + c.xy[1] = newState.xy.get(1); + command = c.toHSBType(); + successApplied.put("xy", newState.xy); + } catch (ClassCastException e) { + errorApplied.add("xy"); + } + } + if (newState.xy_inc != null) { + try { + HueStateColorBulb c = state.as(HueStateColorBulb.class); + double newX = c.xy[0] + newState.xy_inc.get(0); + double newY = c.xy[1] + newState.xy_inc.get(1); + if (newX < 0 || newX > 1 || newY < 0 || newY > 1) { + throw new IllegalArgumentException(); + } + c.colormode = ColorMode.xy; + c.bri = state.as(HueStateBulb.class).bri; + c.xy[0] = newX; + c.xy[1] = newY; + command = c.toHSBType(); + successApplied.put("xy", newState.xy_inc); + } catch (ClassCastException e) { + errorApplied.add("xy_inc"); + } catch (IllegalArgumentException e) { + errorApplied.add("xy_inc"); + } + } + + // Generate the response. The response consists of a list with an entry each for all + // submitted change requests. If for example "on" and "bri" was send, 2 entries in the response are + // expected. + successApplied.forEach((t, v) -> { + responses.add(new HueResponse(new HueSuccessResponseStateChanged(prefix + "/" + t, v))); + }); + errorApplied.forEach(v -> { + responses.add( + new HueResponse(new HueErrorMessage(HueResponse.NOT_AVAILABLE, prefix + "/" + v, "Could not set"))); + }); + + return command; + } + + public static @Nullable DeviceType determineTargetType(ConfigStore cs, Item element) { + String category = element.getCategory(); + String type = element.getType(); + Set tags = element.getTags(); + + // Determine type, heuristically + DeviceType t = null; + + // The user wants this item to be not exposed + if (cs.ignoreItemsFilter.stream().anyMatch(tags::contains)) { + return null; + } + + // First consider the category + if (category != null) { + switch (category) { + case "ColorLight": + t = DeviceType.ColorType; + break; + case "Light": + t = DeviceType.SwitchType; + } + } + + // Then the tags + if (cs.switchFilter.stream().anyMatch(tags::contains)) { + t = DeviceType.SwitchType; + } + if (cs.whiteFilter.stream().anyMatch(tags::contains)) { + t = DeviceType.WhiteTemperatureType; + } + if (cs.colorFilter.stream().anyMatch(tags::contains)) { + t = DeviceType.ColorType; + } + + // Last but not least, the item type + if (t == null) { + switch (type) { + case CoreItemFactory.COLOR: + if (cs.colorFilter.size() == 0) { + t = DeviceType.ColorType; + } + break; + case CoreItemFactory.DIMMER: + case CoreItemFactory.ROLLERSHUTTER: + if (cs.whiteFilter.size() == 0) { + t = DeviceType.WhiteTemperatureType; + } + break; + case CoreItemFactory.SWITCH: + if (cs.switchFilter.size() == 0) { + t = DeviceType.SwitchType; + } + break; + } + } + return t; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/WriteConfig.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/WriteConfig.java new file mode 100644 index 0000000000000..bc587b546a17b --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/WriteConfig.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal; + +import java.io.IOException; +import java.util.Dictionary; +import java.util.Hashtable; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.osgi.service.cm.Configuration; +import org.osgi.service.cm.ConfigurationAdmin; + +/** + * Find all configuration admin interactions in this class + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class WriteConfig { + public static void setUUID(ConfigurationAdmin configAdmin, String uuid) { + try { + Configuration configuration = configAdmin.getConfiguration(HueEmulationService.CONFIG_PID, null); + Dictionary dictionary = configuration.getProperties(); + if (dictionary == null) { + dictionary = new Hashtable<>(); + } + dictionary.put(HueEmulationConfig.CONFIG_UUID, uuid); + configuration.update(dictionary); // This will restart the service (and call activate again) + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Persists the link mode off state + */ + public static void unsetPairingMode(ConfigurationAdmin configAdmin) { + try { + org.osgi.service.cm.Configuration configuration = configAdmin + .getConfiguration(HueEmulationService.CONFIG_PID, null); + Dictionary dictionary = configuration.getProperties(); + dictionary.put(HueEmulationConfig.CONFIG_PAIRING_ENABLED, false); + dictionary.put(HueEmulationConfig.CONFIG_CREATE_NEW_USER_ON_THE_FLY, false); + dictionary.put(HueEmulationConfig.CONFIG_EMULATE_V1, false); + configuration.update(dictionary); + } catch (IOException ignore) { + } + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/AbsoluteDateTimeTriggerHandler.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/AbsoluteDateTimeTriggerHandler.java new file mode 100644 index 0000000000000..54a0e966ac578 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/AbsoluteDateTimeTriggerHandler.java @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.automation; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; + +import org.eclipse.smarthome.core.scheduler.ScheduledCompletableFuture; +import org.eclipse.smarthome.core.scheduler.Scheduler; +import org.eclipse.smarthome.core.scheduler.SchedulerRunnable; +import org.openhab.core.automation.ModuleHandlerCallback; +import org.openhab.core.automation.Trigger; +import org.openhab.core.automation.handler.BaseTriggerModuleHandler; +import org.openhab.core.automation.handler.TriggerHandlerCallback; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Extends the core provided time related module type by an absolute day/time trigger. + *

+ * It allows to set a date and a time as separate configuration values (easier to manipulate from + * other actions / rules etc) and also allows the user to setup a random factor + * (presence simulation). + * + * @author David Graeff - Initial contribution + */ +public class AbsoluteDateTimeTriggerHandler extends BaseTriggerModuleHandler implements SchedulerRunnable { + + private final Logger logger = LoggerFactory.getLogger(AbsoluteDateTimeTriggerHandler.class); + + public static final String MODULE_TYPE_ID = "timer.AbsoluteDateTimeTrigger"; + public static final String CALLBACK_CONTEXT_NAME = "CALLBACK"; + public static final String MODULE_CONTEXT_NAME = "MODULE"; + + public static final String CFG_DATE = "date"; + public static final String CFG_TIME = "time"; + public static final String CFG_TIME_RND = "randomizeTime"; + + private final Scheduler scheduler; + private final Instant dateTime; + private ScheduledCompletableFuture schedule; + private static final String DATE_FORMAT = "yyyy-MM-dd"; + private static final String TIME_FORMAT = "HH:mm:ss"; + private static final String DATETIME_FORMAT = DATE_FORMAT + " " + TIME_FORMAT; + private final DateTimeFormatter dateTimeformatter; + + public AbsoluteDateTimeTriggerHandler(Trigger module, Scheduler scheduler) { + super(module); + this.scheduler = scheduler; + dateTimeformatter = DateTimeFormatter.ofPattern(DATETIME_FORMAT); + + DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DATE_FORMAT); + DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(TIME_FORMAT); + + // Take optional date into account + String cfgDate = (String) module.getConfiguration().get(CFG_DATE); + LocalDateTime dateTime = cfgDate == null || cfgDate.isEmpty() ? LocalDate.now().atStartOfDay() + : LocalDateTime.from(dateFormatter.parse(cfgDate)); + + // Take optional time into account + String cfgDTime = (String) module.getConfiguration().get(CFG_TIME); + if (cfgDTime != null && !cfgDTime.isEmpty()) { + TemporalAccessor temporalAccessor = timeFormatter.parse(cfgDTime); + dateTime.plusHours(temporalAccessor.getLong(ChronoField.HOUR_OF_DAY)); + dateTime.plusMinutes(temporalAccessor.getLong(ChronoField.MINUTE_OF_HOUR)); + } + + this.dateTime = dateTime.atZone(ZoneId.systemDefault()).toInstant(); + } + + @Override + public synchronized void setCallback(ModuleHandlerCallback callback) { + super.setCallback(callback); + scheduleJob(); + } + + private void scheduleJob() { + schedule = scheduler.at(this, dateTime); + logger.debug("Scheduled absolute date/time '{} {}' for trigger '{}'.", dateTimeformatter.format(dateTime), + module.getId()); + } + + @Override + public synchronized void dispose() { + super.dispose(); + if (schedule != null) { + schedule.cancel(true); + logger.debug("cancelled job for trigger '{}'.", module.getId()); + } + } + + @Override + public void run() { + ((TriggerHandlerCallback) callback).triggered(module, null); + schedule = null; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/HttpActionHandler.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/HttpActionHandler.java new file mode 100644 index 0000000000000..2a59b24ff2012 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/HttpActionHandler.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.automation; + +import java.net.URI; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.smarthome.io.net.http.HttpClientFactory; +import org.openhab.core.automation.Action; +import org.openhab.core.automation.handler.ActionHandler; +import org.openhab.core.automation.handler.BaseModuleHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The action module type handled by this class allows to execute a http request (GET, POST, PUT, etc) + * on a given address. Relative addresses are supported. + *

+ * The optional mimetype and body configuration parameters allow to send arbitrary data with a request. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HttpActionHandler extends BaseModuleHandler implements ActionHandler { + private final Logger logger = LoggerFactory.getLogger(HttpActionHandler.class); + + public static final String MODULE_TYPE_ID = "rules.HttpAction"; + public static final String CALLBACK_CONTEXT_NAME = "CALLBACK"; + public static final String MODULE_CONTEXT_NAME = "MODULE"; + + public static final String CFG_METHOD = "method"; + public static final String CFG_URL = "url"; + public static final String CFG_BODY = "body"; + public static final String CFG_MIMETYPE = "mimetype"; + public static final String CFG_TIMEOUT = "timeout"; + + private static class Config { + HttpMethod method = HttpMethod.GET; + String url = ""; + String body = ""; + String mimetype = "application/json"; + int timeout = 5; + } + + private Config config = new Config(); + private HttpClient httpClient; + + public HttpActionHandler(final Action module, HttpClientFactory httpFactory) { + super(module); + + this.config = module.getConfiguration().as(Config.class); + if (config.url.isEmpty()) { + throw new IllegalArgumentException("URL not set!"); + } + // convert relative path to absolute one + String url = config.url; + if (url.startsWith("/")) { + config.url = "http://localhost:" + Integer.getInteger("org.osgi.service.http.port", 8080).toString() + url; + } + + httpClient = httpFactory.createHttpClient("HttpActionHandler_" + module.getId()); + } + + @Override + public @Nullable Map execute(Map context) { + + try { + Request request = httpClient.newRequest(URI.create(config.url)).method(config.method) + .timeout(config.timeout, TimeUnit.SECONDS); + if (config.method == HttpMethod.POST || config.method == HttpMethod.PUT) { + request.content(new StringContentProvider(config.body), config.mimetype); + } + request.send(); + } catch (InterruptedException | TimeoutException | ExecutionException e) { + logger.warn("Failed to send http request", e); + } + return null; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/HueHandlerFactory.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/HueHandlerFactory.java new file mode 100644 index 0000000000000..63156ef52ec61 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/HueHandlerFactory.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.automation; + +import java.util.Arrays; +import java.util.Collection; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.automation.Condition; +import org.openhab.core.automation.Module; +import org.openhab.core.automation.handler.BaseModuleHandlerFactory; +import org.openhab.core.automation.handler.ModuleHandler; +import org.openhab.core.automation.handler.ModuleHandlerFactory; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Triggers, Conditions, Actions specific to the Hue emulation service + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +@Component(immediate = true, service = ModuleHandlerFactory.class) +public class HueHandlerFactory extends BaseModuleHandlerFactory { + + private final Logger logger = LoggerFactory.getLogger(HueHandlerFactory.class); + + private static final Collection TYPES = Arrays + .asList(new String[] { HueRuleConditionHandler.MODULE_TYPE_ID }); + + @Reference + protected @NonNullByDefault({}) ConfigStore configStore; + + @Override + @Deactivate + public void deactivate() { + super.deactivate(); + } + + @Override + public Collection getTypes() { + return TYPES; + } + + @Override + protected @Nullable ModuleHandler internalCreate(Module module, String ruleUID) { + logger.trace("create {} -> {}", module.getId(), module.getTypeUID()); + String moduleTypeUID = module.getTypeUID(); + if (HueRuleConditionHandler.MODULE_TYPE_ID.equals(moduleTypeUID) && module instanceof Condition) { + return new HueRuleConditionHandler((Condition) module, configStore.ds); + } else { + logger.error("The module handler type '{}' is not supported.", moduleTypeUID); + } + return null; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/HueRuleConditionHandler.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/HueRuleConditionHandler.java new file mode 100644 index 0000000000000..3acb2dc2d7714 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/HueRuleConditionHandler.java @@ -0,0 +1,240 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.automation; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoField; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.OpenClosedType; +import org.eclipse.smarthome.core.types.State; +import org.openhab.core.automation.Condition; +import org.openhab.core.automation.handler.BaseModuleHandler; +import org.openhab.core.automation.handler.ConditionHandler; +import org.openhab.io.hueemulation.internal.dto.HueDataStore; +import org.openhab.io.hueemulation.internal.dto.HueGroupEntry; +import org.openhab.io.hueemulation.internal.dto.HueLightEntry; +import org.openhab.io.hueemulation.internal.dto.HueRuleEntry; +import org.openhab.io.hueemulation.internal.dto.HueRuleEntry.Operator; +import org.openhab.io.hueemulation.internal.dto.HueSensorEntry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This condition is parameterized with Hue rule condition arguments. A Hue rule works + * on the Hue datastore and considers lights / groups / sensors that are available there. + *

+ * Implementation details: The predicate function for the condition is computed in the constructor of + * this condition. It will only be called and evaluated for isSatisfied. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueRuleConditionHandler extends BaseModuleHandler implements ConditionHandler { + + private final Logger logger = LoggerFactory.getLogger(HueRuleConditionHandler.class); + + public static final String MODULE_TYPE_ID = "hue.ruleCondition"; + public static final String CALLBACK_CONTEXT_NAME = "CALLBACK"; + public static final String MODULE_CONTEXT_NAME = "MODULE"; + + public static final String CFG_ADDRESS = "address"; + public static final String CFG_OP = "operator"; + public static final String CFG_VALUE = "value"; + + private static final String TIME_FORMAT = "HH:mm:ss"; + + protected final HueRuleEntry.Condition config; + protected String itemUID; + + protected Predicate predicate; + private final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(TIME_FORMAT); + private final Pattern timePattern = Pattern.compile("W(.*)/T(.*)/T(.*)"); + // weekdays range from Monday to Sunday (1-7). The first entry is not used + private final boolean weekDaysAllowed[] = { false, false, false, false, false, false, false, false }; + + @SuppressWarnings({ "null", "unused" }) + public HueRuleConditionHandler(Condition module, HueDataStore ds) { + super(module); + config = module.getConfiguration().as(HueRuleEntry.Condition.class); + + // pattern: "/sensors/2/state/buttonevent" or "/config/localtime" + String validation[] = config.address.split("/"); + String uid = validation[2]; + + if ("groups".equals(validation[1]) && "action".equals(validation[3])) { + HueGroupEntry entry = ds.groups.get(uid); + if (entry == null) { + throw new IllegalStateException("Group does not exist: " + uid); + } + itemUID = entry.groupItem.getUID(); + } else if ("lights".equals(validation[1]) && "state".equals(validation[3])) { + HueLightEntry entry = ds.lights.get(uid); + if (entry == null) { + throw new IllegalStateException("Light does not exist: " + uid); + } + itemUID = entry.item.getUID(); + } else if ("sensors".equals(validation[1]) && "state".equals(validation[3])) { + HueSensorEntry entry = ds.sensors.get(uid); + if (entry == null) { + throw new IllegalStateException("Sensor does not exist: " + uid); + } + itemUID = entry.item.getUID(); + } else if ("config".equals(validation[1]) && "localtime".equals(validation[2])) { + // Item not used + itemUID = ""; + } else { + throw new IllegalStateException("Can only handle groups and lights"); + } + + if (itemUID == null) { + throw new IllegalStateException("Can only handle groups and lights"); + } + + final String value = config.value; + switch (config.operator) { + case eq: + if (value == null) { + throw new IllegalStateException("Equal operator requires a value!"); + } + predicate = state -> { + if (state instanceof Number) { + return Integer.valueOf(value) == ((Number) state).intValue(); + } else if (state instanceof OnOffType) { + return Boolean.valueOf(value) == (((OnOffType) state) == OnOffType.ON); + } else if (state instanceof OpenClosedType) { + return Boolean.valueOf(value) == (((OpenClosedType) state) == OpenClosedType.OPEN); + } + return state.toFullString().equals(value); + }; + break; + case gt: + if (value == null) { + throw new IllegalStateException("GreaterThan operator requires a value!"); + } else { + final Integer integer = Integer.valueOf(value); + + predicate = state -> { + if (state instanceof Number) { + return integer < ((Number) state).intValue(); + } else { + return false; + } + }; + } + break; + case lt: + if (value == null) { + throw new IllegalStateException("LowerThan operator requires a value!"); + } else { + final Integer integer = Integer.valueOf(value); + + predicate = state -> { + if (state instanceof Number) { + return integer > ((Number) state).intValue(); + } else { + return false; + } + }; + } + break; + case in: + case not_in: + if (value == null) { + throw new IllegalStateException("InRange operator requires a value!"); + } + + Matcher m = timePattern.matcher(!value.startsWith("W") ? "W127/" + value : value); + if (!m.matches() || m.groupCount() < 3) { + throw new IllegalStateException( + "Time pattern incorrect for in/not_in hue rule condition: " + value); + } + + final LocalTime timeStart = LocalTime.from(timeFormatter.parse(m.group(2))); + final LocalTime timeEnd = LocalTime.from(timeFormatter.parse(m.group(3))); + + // Monday = 64, Tuesday = 32, Wednesday = 16, Thursday = 8, Friday = 4, Saturday = 2, Sunday = 1 + int weekdaysBinaryEncoded = Integer.valueOf(m.group(1)); + List cronWeekdays = new ArrayList<>(); + for (int bin = 64, c = 1; bin > 0; bin /= 2, c += 1) { + if (weekdaysBinaryEncoded / bin == 1) { + weekdaysBinaryEncoded = weekdaysBinaryEncoded % bin; + weekDaysAllowed[c] = true; + } + } + + predicate = state -> { + LocalDateTime now = getNow(); + int dow = now.get(ChronoField.DAY_OF_WEEK); + LocalTime localTime = now.toLocalTime(); + return weekDaysAllowed[dow] && localTime.isAfter(timeStart) && localTime.isBefore(timeEnd) + && config.operator == Operator.in; + }; + break; + default: + predicate = s -> true; + break; + } + } + + // For test injection + protected LocalDateTime getNow() { + return LocalDateTime.now(); + } + + @NonNullByDefault({}) + @Override + public boolean isSatisfied(Map context) { + switch (config.operator) { + case in: + case not_in: + return predicate.test(OnOffType.ON); + default: + } + + State state = (State) context.get("newState"); + State oldState = (State) context.get("oldState"); + + if (state == null || oldState == null) { + logger.warn("Expected a state and oldState input or an in/not_in operator!"); + return false; + } + + switch (config.operator) { + case ddx: // Item changes always satisfies the "hue change" and "hue change delay" condition + case dx: + return true; + case eq: + case gt: + case lt: + return predicate.test(state); + case not_stable: // state changed? + return (!state.toFullString().equals(oldState.toFullString())); + case stable: // state stable? + return (state.toFullString().equals(oldState.toFullString())); + case unknown: + default: + logger.warn("Operator not handled! " + config.operator.name()); + return false; + } + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/RemoveRuleActionHandler.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/RemoveRuleActionHandler.java new file mode 100644 index 0000000000000..c7286b1bec280 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/RemoveRuleActionHandler.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.automation; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.core.Configuration; +import org.openhab.core.automation.Action; +import org.openhab.core.automation.RuleRegistry; +import org.openhab.core.automation.handler.ActionHandler; +import org.openhab.core.automation.handler.BaseModuleHandler; + +/** + * This action module type allows to remove a rule from the rule registry. + *

+ * This is very useful for rules that should execute only once etc. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class RemoveRuleActionHandler extends BaseModuleHandler implements ActionHandler { + public static final String MODULE_TYPE_ID = "rules.RemoveRuleAction"; + public static final String CALLBACK_CONTEXT_NAME = "CALLBACK"; + public static final String MODULE_CONTEXT_NAME = "MODULE"; + + public static final String CFG_REMOVE_UID = "removeuid"; + private final String ruleUID; + + private RuleRegistry ruleRegistry; + + @SuppressWarnings({ "null", "unused" }) + public RemoveRuleActionHandler(final Action module, RuleRegistry ruleRegistry) { + super(module); + this.ruleRegistry = ruleRegistry; + final Configuration config = module.getConfiguration(); + if (config.getProperties().isEmpty()) { + throw new IllegalArgumentException("'Configuration' can not be empty."); + } + + ruleUID = (String) config.get(CFG_REMOVE_UID); + if (ruleUID == null) { + throw new IllegalArgumentException("'ruleUIDs' property must not be null."); + } + } + + @Override + public @Nullable Map execute(Map context) { + ruleRegistry.remove(ruleUID); + return null; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/RulesHandlerFactory.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/RulesHandlerFactory.java new file mode 100644 index 0000000000000..3af1212bdbf07 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/RulesHandlerFactory.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.automation; + +import java.util.Arrays; +import java.util.Collection; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.io.net.http.HttpClientFactory; +import org.openhab.core.automation.Action; +import org.openhab.core.automation.Module; +import org.openhab.core.automation.RuleRegistry; +import org.openhab.core.automation.handler.BaseModuleHandlerFactory; +import org.openhab.core.automation.handler.ModuleHandler; +import org.openhab.core.automation.handler.ModuleHandlerFactory; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This factory is responsible for rule and http related module types. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +@Component(immediate = true, service = ModuleHandlerFactory.class) +public class RulesHandlerFactory extends BaseModuleHandlerFactory { + + private final Logger logger = LoggerFactory.getLogger(RulesHandlerFactory.class); + + private static final Collection TYPES = Arrays + .asList(new String[] { RemoveRuleActionHandler.MODULE_TYPE_ID, HttpActionHandler.MODULE_TYPE_ID }); + + @Reference + protected @NonNullByDefault({}) RuleRegistry ruleRegistry; + + @Reference + protected @NonNullByDefault({}) HttpClientFactory httpFactory; + + @Override + @Deactivate + public void deactivate() { + super.deactivate(); + } + + @Override + public Collection getTypes() { + return TYPES; + } + + @Override + protected @Nullable ModuleHandler internalCreate(Module module, String ruleUID) { + logger.trace("create {} -> {}", module.getId(), module.getTypeUID()); + String moduleTypeUID = module.getTypeUID(); + if (RemoveRuleActionHandler.MODULE_TYPE_ID.equals(moduleTypeUID) && module instanceof Action) { + return new RemoveRuleActionHandler((Action) module, ruleRegistry); + } else if (HttpActionHandler.MODULE_TYPE_ID.equals(moduleTypeUID) && module instanceof Action) { + return new HttpActionHandler((Action) module, httpFactory); + } else { + logger.error("The module handler type '{}' is not supported.", moduleTypeUID); + } + return null; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/TimerModuleExHandlerFactory.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/TimerModuleExHandlerFactory.java new file mode 100644 index 0000000000000..ca9d345f88cda --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/TimerModuleExHandlerFactory.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.automation; + +import java.util.Arrays; +import java.util.Collection; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.scheduler.Scheduler; +import org.openhab.core.automation.Module; +import org.openhab.core.automation.Trigger; +import org.openhab.core.automation.handler.BaseModuleHandlerFactory; +import org.openhab.core.automation.handler.ModuleHandler; +import org.openhab.core.automation.handler.ModuleHandlerFactory; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This factory is responsible for timer related module types. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +@Component(immediate = true, service = ModuleHandlerFactory.class) +public class TimerModuleExHandlerFactory extends BaseModuleHandlerFactory { + + private final Logger logger = LoggerFactory.getLogger(TimerModuleExHandlerFactory.class); + + private static final Collection TYPES = Arrays + .asList(new String[] { AbsoluteDateTimeTriggerHandler.MODULE_TYPE_ID, TimerTriggerHandler.MODULE_TYPE_ID }); + + @Reference + private @NonNullByDefault({}) Scheduler scheduler; + + @Override + @Deactivate + public void deactivate() { + super.deactivate(); + } + + @Override + public Collection getTypes() { + return TYPES; + } + + @Override + protected @Nullable ModuleHandler internalCreate(Module module, String ruleUID) { + logger.trace("create {} -> {}", module.getId(), module.getTypeUID()); + String moduleTypeUID = module.getTypeUID(); + if (AbsoluteDateTimeTriggerHandler.MODULE_TYPE_ID.equals(moduleTypeUID) && module instanceof Trigger) { + return new AbsoluteDateTimeTriggerHandler((Trigger) module, scheduler); + } else if (TimerTriggerHandler.MODULE_TYPE_ID.equals(moduleTypeUID) && module instanceof Trigger) { + return new TimerTriggerHandler((Trigger) module, scheduler); + } else { + logger.error("The module handler type '{}' is not supported.", moduleTypeUID); + } + return null; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/TimerTriggerHandler.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/TimerTriggerHandler.java new file mode 100644 index 0000000000000..a6e0cbec60ba9 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/TimerTriggerHandler.java @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.automation; + +import java.time.Duration; +import java.util.Random; +import java.util.concurrent.Callable; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.scheduler.ScheduledCompletableFuture; +import org.eclipse.smarthome.core.scheduler.Scheduler; +import org.openhab.core.automation.ModuleHandlerCallback; +import org.openhab.core.automation.Trigger; +import org.openhab.core.automation.handler.BaseTriggerModuleHandler; +import org.openhab.core.automation.handler.TriggerHandlerCallback; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This trigger module time allows a trigger that is setup with a time (hours:minutes:seconds). + * As soon as that time has run up, it will trigger. + *

+ * A random factor and repeat times can also be configured. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class TimerTriggerHandler extends BaseTriggerModuleHandler implements Callable { + + private final Logger logger = LoggerFactory.getLogger(TimerTriggerHandler.class); + + public static final String MODULE_TYPE_ID = "timer.TimerTrigger"; + public static final String CALLBACK_CONTEXT_NAME = "CALLBACK"; + public static final String MODULE_CONTEXT_NAME = "MODULE"; + + public static final String CFG_REPEAT = "repeat"; + public static final String CFG_TIME = "time"; + public static final String CFG_TIME_RND = "randomizeTime"; + + private final Scheduler scheduler; + private final Duration duration; + private @Nullable ScheduledCompletableFuture schedule; + + private static class Config { + int repeat = 1; + String time = ""; + String randomizeTime = ""; + } + + Config config; + + public TimerTriggerHandler(Trigger module, Scheduler scheduler) { + super(module); + this.scheduler = scheduler; + config = module.getConfiguration().as(Config.class); + + String[] fields = config.time.split(":"); + Duration d1 = Duration.parse(String.format("P%dH%dM%sS", fields[0], fields[1], fields[2])); + + // Take optional random time (a range-like parameter) into account + if (!config.randomizeTime.isEmpty()) { + fields = config.randomizeTime.split(":"); + Duration d2 = Duration.parse(String.format("P%dH%dM%sS", fields[0], fields[1], fields[2])); + // The random time must be later a bigger value than time + if (d1.compareTo(d2) >= 0) { + throw new IllegalArgumentException(); + } + // Compute the difference, turn in to seconds, get a random second value between 0 and that upper bound + // and then add it to the base time + Duration difference = d2.minus(d1); + duration = d1.plus(Duration.ofSeconds(randomSeconds(difference.getSeconds()))); + } else { + duration = d1; + } + } + + protected long randomSeconds(long maximum) { + return Math.abs(new Random().nextLong()) % maximum; + } + + @Override + public synchronized void setCallback(ModuleHandlerCallback callback) { + super.setCallback(callback); + if (config.repeat != 0) { + scheduleJob(); + } + } + + private void scheduleJob() { + schedule = scheduler.after(this, duration); + logger.debug("Scheduled timer to expire in '{}' for trigger '{}'.", duration, module.getId()); + } + + @Override + public synchronized void dispose() { + super.dispose(); + ScheduledCompletableFuture future = schedule; + if (future != null) { + future.cancel(true); + logger.debug("cancelled job for trigger '{}'.", module.getId()); + } + } + + @Override + public Duration call() throws Exception { + ((TriggerHandlerCallback) callback).triggered(module, null); + config.repeat -= 1; + if (config.repeat == 0) { + schedule = null; + } else { + scheduleJob(); + } + return duration; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/dto/HueRuleTriggerConfig.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/dto/HueRuleTriggerConfig.java new file mode 100644 index 0000000000000..2678aeec864c6 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/dto/HueRuleTriggerConfig.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.automation.dto; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.io.hueemulation.internal.dto.HueRuleEntry; + +/** + * A configuration holder class for the rule trigger handler + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueRuleTriggerConfig { + public String address = ""; + public HueRuleEntry.Operator operator = HueRuleEntry.Operator.dx; + public @Nullable String value; +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/dto/ItemCommandActionConfig.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/dto/ItemCommandActionConfig.java new file mode 100644 index 0000000000000..5de274f9ae051 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/automation/dto/ItemCommandActionConfig.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.automation.dto; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * A configuration holder class for the item command handler + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class ItemCommandActionConfig { + public String itemName = ""; + public String command = ""; +} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/AbstractHueState.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/AbstractHueState.java similarity index 87% rename from addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/AbstractHueState.java rename to bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/AbstractHueState.java index 729bd4804c386..5bb112d7dcf94 100644 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/AbstractHueState.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/AbstractHueState.java @@ -31,4 +31,8 @@ public static enum AlertEnum { } public String alert = AlertEnum.none.name(); + + public T as(Class type) throws ClassCastException { + return type.cast(this); + } } diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueAuthorizedConfig.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueAuthorizedConfig.java new file mode 100644 index 0000000000000..d564a3f40e977 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueAuthorizedConfig.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto; + +import java.lang.reflect.Type; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +import com.google.gson.JsonElement; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * Hue API config object + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueAuthorizedConfig extends HueUnauthorizedConfig { + public String uuid = ""; // Example: 5673dfa7-272c-4315-9955-252cdd86131c + + public String timeformat = "24h"; + public String timezone = ZonedDateTime.now().getOffset().getId().replace("Z", "+00:00"); + public String UTC = "2018-11-10T15:24:23"; + public String localtime = "2018-11-10T16:24:23"; + + public String devicename = "openHAB"; + + public String fwversion = "0x262e0500"; + + public boolean rfconnected = true; + public int zigbeechannel = 15; + public boolean linkbutton = false; + public transient boolean createNewUserOnEveryEndpoint = false; + public int panid = 19367; + + public boolean dhcp = true; + public String gateway = "192.168.0.1"; + public String ipaddress = ""; // Example: 192.168.0.46 + public String netmask = ""; // Example: 255.255.255.0 + public int networkopenduration = 60; + + public String proxyaddress = "none"; + public int proxyport = 0; + + public final Map whitelist = new TreeMap<>(); + + public void makeV1bridge() { + apiversion = "1.16.0"; + datastoreversion = "60"; + modelid = "BSB001"; + swversion = "01041302"; + fwversion = "0x262e0500"; + } + + public void makeV2bridge() { + apiversion = "1.22.0"; + datastoreversion = "60"; + modelid = "BSB002"; + swversion = "1901181309"; + fwversion = "0x262e0500"; + } + + /** + * Return a json serializer that behaves like the default one, but updates the UTC and localtime fields + * before each serializion. + */ + @NonNullByDefault({}) + public static class Serializer implements JsonSerializer { + static class HueAuthorizedConfigHelper extends HueAuthorizedConfig { + + } + + @Override + public JsonElement serialize(HueAuthorizedConfig src, Type typeOfSrc, JsonSerializationContext context) { + src.UTC = LocalDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); + src.localtime = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); + JsonElement jsonSubscription = context.serialize(src, HueAuthorizedConfigHelper.class); + return jsonSubscription; + } + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueCapability.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueCapability.java new file mode 100644 index 0000000000000..d17d9b727421d --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueCapability.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto; + +/** + * Hue Capabilities per endpoint + * Enpoint: /api/{username}/capabilities + *

+ * https://developers.meethue.com/develop/hue-api/10-capabilities-api/ + * + * @author David Graeff - Initial contribution + */ +public class HueCapability { + public int available = 10; + public int total = 10000; +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueDataStore.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueDataStore.java new file mode 100644 index 0000000000000..1f3e56634d9e6 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueDataStore.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto; + +import java.util.Collections; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Hue data store object. Contains all lights, configuration, user whitelist etc. + * Is used as a data store but also as API DTO. + * + * @author Dan Cunningham - Initial contribution + * @author David Graeff - Add groups,scenes,rules,sensors,resourcelinks and config entries + * + */ +@NonNullByDefault +public class HueDataStore { + public HueAuthorizedConfig config = new HueAuthorizedConfig(); + public TreeMap lights = new TreeMap<>(); + public TreeMap groups = new TreeMap<>(); + public Map scenes = new TreeMap<>(); + public Map rules = new TreeMap<>(); + public Map sensors = new TreeMap<>(); + public Map schedules = new TreeMap<>(); + public Map resourcelinks = Collections.emptyMap(); + public Map capabilities = new TreeMap<>(); + + public HueDataStore() { + resetGroupsAndLights(); + capabilities.put("lights", new HueCapability()); + capabilities.put("groups", new HueCapability()); + capabilities.put("scenes", new HueCapability()); + capabilities.put("rules", new HueCapability()); + capabilities.put("sensors", new HueCapability()); + capabilities.put("schedules", new HueCapability()); + capabilities.put("resourcelinks", new HueCapability()); + } + + public void resetGroupsAndLights() { + groups.clear(); + lights.clear(); + // There must be a group 0 all the time! + groups.put("0", new HueGroupEntry("All lights", null, null)); + } + + public void resetSensors() { + sensors.clear(); + } + + public static class Dummy { + } + + /** + * Return a unique group id. + */ + public String nextGroupID() { + int nextId = groups.size(); + while (true) { + String id = "hueemulation" + String.valueOf(nextId); + if (!groups.containsKey(id)) { + return id; + } + ++nextId; + } + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueGroupEntry.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueGroupEntry.java new file mode 100644 index 0000000000000..8a2a883c521e5 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueGroupEntry.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto; + +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.items.GroupItem; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.DeviceType; + +import com.google.gson.JsonElement; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.annotations.SerializedName; + +/** + * Hue API group object + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueGroupEntry { + public static enum TypeEnum { + LightGroup, // 1.4 + Luminaire, // 1.4 + LightSource, // 1.4 + Room, // 1.11 + Entertainment, // 1.22 + Zone // 1.30 + } + + public AbstractHueState action = new HueStatePlug(); + + // The group type + public String type = TypeEnum.LightGroup.name(); + + // A unique, editable name given to the group. + public String name; + + @SerializedName("class") + public String roomclass = "Other"; + + // The IDs of the lights that are in the group. + public List lights = Collections.emptyList(); + public List sensors = Collections.emptyList(); + + public transient @NonNullByDefault({}) GroupItem groupItem; + public transient @Nullable DeviceType deviceType; + + // For deserialisation + HueGroupEntry() { + name = ""; + } + + public HueGroupEntry(String name, @Nullable GroupItem groupItem, @Nullable DeviceType deviceType) { + this.name = name; + this.groupItem = groupItem; + this.deviceType = deviceType; + } + + public void updateItem(GroupItem element) { + groupItem = element; + } + + /** + * This custom serializer computes the {@link HueGroupEntry#lights} list, before serializing. + * It does so, by looking up all item members of the references groupItem. + */ + @NonNullByDefault({}) + public static class Serializer implements JsonSerializer { + + private ConfigStore cs; + + public Serializer(ConfigStore cs) { + this.cs = cs; + } + + static class HueGroupHelper extends HueGroupEntry { + + } + + @Override + public JsonElement serialize(HueGroupEntry product, Type type, JsonSerializationContext context) { + + GroupItem item = product.groupItem; + if (item != null) { + product.lights = item.getMembers().stream().map(gitem -> cs.mapItemUIDtoHueID(gitem)) + .collect(Collectors.toList()); + } + + JsonElement jsonSubscription = context.serialize(product, HueGroupHelper.class); + return jsonSubscription; + } + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueLightEntry.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueLightEntry.java new file mode 100644 index 0000000000000..f9f896acf8ab0 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueLightEntry.java @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto; + +import java.lang.reflect.Type; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.items.GenericItem; +import org.eclipse.smarthome.core.library.items.StringItem; +import org.openhab.io.hueemulation.internal.DeviceType; +import org.openhab.io.hueemulation.internal.StateUtils; + +import com.google.gson.JsonElement; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * Hue API device object + * + * @author Dan Cunningham - Initial contribution + * @author David Graeff - Color lights and plugs + * @author Florian Lentz - XY Support + */ +@NonNullByDefault +public class HueLightEntry { + public AbstractHueState state = new AbstractHueState(); + public final String type; + public final String modelid; + public final String uniqueid; + public final String manufacturername; + public final @Nullable String productname; + public final String swversion; + public final @Nullable String luminaireuniqueid = null; + public final @Nullable String swconfigid; + public final @Nullable String productid; + public @Nullable Boolean friendsOfHue = true; + public final @Nullable String colorGamut; + public @Nullable Boolean hascolor = null; + + public String name; + /** Associated item UID */ + public @NonNullByDefault({}) transient GenericItem item; + public transient DeviceType deviceType; + + public static class Config { + public final String archetype = "classicbulb"; + public final String function = "functional"; + public final String direction = "omnidirectional"; + }; + + public Config config = new Config(); + + public static class Streaming { + public boolean renderer = false; + public boolean proxy = false; + }; + + public static class Capabilities { + public boolean certified = false; + public final Streaming streaming = new Streaming(); + public final Object control = new Object(); + }; + + public Capabilities capabilities = new Capabilities(); + + private HueLightEntry() { + this(new StringItem(""), "", DeviceType.SwitchType); + } + + /** + * Create a hue device. + * + * @param item The associated item + * @param uniqueid The unique id + * @param deviceType The device type decides which capabilities this device has + */ + public HueLightEntry(GenericItem item, String uniqueid, DeviceType deviceType) { + String label = item.getLabel(); + this.item = item; + this.deviceType = deviceType; + this.uniqueid = uniqueid; + switch (deviceType) { + case ColorType: + this.name = label != null ? label : ""; + this.type = "Extended Color light"; + this.modelid = "LCT010"; + this.colorGamut = "C"; + this.manufacturername = "Philips"; + this.swconfigid = "F921C859"; + this.swversion = "1.15.2_r19181"; + this.productid = "Philips-LCT010-1-A19ECLv4"; + this.productname = null; + this.hascolor = true; + this.capabilities.certified = true; + break; + case WhiteType: + /** Hue White A19 - 3nd gen - white, 2700K only */ + this.name = label != null ? label : ""; + this.type = "Dimmable light"; + this.modelid = "LWB006"; + this.colorGamut = null; + this.manufacturername = "Philips"; + this.swconfigid = null; + this.swversion = "66012040"; + this.productid = null; + this.hascolor = false; + this.productname = null; + this.capabilities.certified = true; + break; + case WhiteTemperatureType: + this.name = label != null ? label : ""; + this.type = "Color temperature light"; + this.modelid = "LTW001"; + this.colorGamut = "2200K-6500K"; + this.manufacturername = "Philips"; + this.swconfigid = null; + this.swversion = "66012040"; + this.productid = null; + this.hascolor = false; + this.productname = null; + this.capabilities.certified = true; + break; + case SwitchType: + default: + /** + * Pretend to be an OSRAM plug, there is no native Philips Hue plug on the market. + * Those are supported by most of the external apps and Alexa. + */ + this.name = label != null ? label : ""; + this.type = "On/off light"; + this.modelid = "Plug 01"; + this.colorGamut = null; + this.manufacturername = "OSRAM"; + this.productname = "On/Off plug"; + this.swconfigid = null; + this.swversion = "V1.04.12"; + this.productid = null; + this.hascolor = false; + this.friendsOfHue = null; + break; + } + + state = StateUtils.colorStateFromItemState(item.getState(), deviceType); + } + + /** + * This custom serializer updates the light state and label, before serializing. + */ + @NonNullByDefault({}) + public static class Serializer implements JsonSerializer { + static class HueDeviceHelper extends HueLightEntry { + + } + + @Override + public JsonElement serialize(HueLightEntry product, Type type, JsonSerializationContext context) { + + product.state = StateUtils.colorStateFromItemState(product.item.getState(), product.deviceType); + String label = product.item.getLabel(); + if (label != null) { + product.name = label; + } + + JsonElement jsonSubscription = context.serialize(product, HueDeviceHelper.class); + return jsonSubscription; + } + } + + /** + * Replaces the associated openHAB item of this hue device with the given once + * and also synchronizes/updates the color information of this hue device with the item. + * + * @param element A replace item + */ + public void updateItem(GenericItem element) { + item = element; + state = StateUtils.colorStateFromItemState(item.getState(), deviceType); + + String label = element.getLabel(); + if (label != null) { + name = label; + } + } +} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueNewLights.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueNewLights.java similarity index 100% rename from addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueNewLights.java rename to bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueNewLights.java diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueRuleEntry.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueRuleEntry.java new file mode 100644 index 0000000000000..6a6bc558b7946 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueRuleEntry.java @@ -0,0 +1,140 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueCommand; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * Hue API rule object + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueRuleEntry { + // A unique, editable name given to the group. + public String name = ""; + public String description = ""; + + public String owner = ""; + + public static enum Operator { + unknown, + eq, // equals, Used for bool and int. + gt, // greater than, Allowed on int values. + lt, // less than, Allowed on int values. + dx, // value has changed, Time (timestamps) int and bool values. Only dx or ddx is allowed, but not both. + ddx, // delayed value has changed + stable, // Time (timestamps) int and bool values. An attribute has or has not changed for a given time. + not_stable, + in, // Current time is in or not in given time interval (only for /config/localtime, not UTC). “in†rule will be + // triggered on starttime and “not in†rule will be triggered on endtime. Only one “in†operator is allowed + // in a rule. Multiple “not in†operators are allowed in a rule. + not_in + } + + /** + * A complete condition could look like this: + *

+ * + *

+     * {
+     *       "address": "/sensors/2/state/buttonevent",
+     *       "operator": "eq",
+     *       "value": "16"
+     * },
+     * 
+ */ + public static class Condition { + /** + * A hue resource address like "/config/localtime" or "/sensors/2/state/buttonevent" + */ + public String address = ""; + public Operator operator = Operator.unknown; + /** + * A value like "16" + */ + public @Nullable String value; + + public Condition() { + } + + public Condition(String address, Operator operator, @Nullable String value) { + this.address = address; + this.value = value; + this.operator = operator; + } + } + + // The IDs of the lights that are in the group. + public List conditions = new ArrayList<>(); + public List actions = new ArrayList<>(); + + HueRuleEntry() { + name = ""; + } + + public HueRuleEntry(@Nullable String name) { + this.name = name != null ? name : ""; + } + + /** + * This custom serializer (de)serialize the Condition class and translates between the enum values that have + * an underbar and the json strings that have a whitespace instead. + */ + @NonNullByDefault({}) + public static class SerializerCondition + implements JsonSerializer, JsonDeserializer { + @Override + public JsonElement serialize(HueRuleEntry.Condition product, Type type, JsonSerializationContext context) { + JsonObject jObj = new JsonObject(); + jObj.addProperty("address", product.address); + String value = product.value; + if (value != null) { + jObj.addProperty("value", value); + } + jObj.addProperty("operator", product.operator.name().replace("_", " ")); + + return jObj; + } + + @Override + public Condition deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jObj = json.getAsJsonObject(); + Condition c = new Condition(); + + c.address = jObj.get("address").getAsString(); + if (jObj.has("value")) { + c.value = jObj.get("value").getAsString(); + } + String operator = jObj.get("operator").getAsString().replace(" ", "_"); + c.operator = Operator.valueOf(operator); + return c; + } + } + +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueSceneEntry.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueSceneEntry.java new file mode 100644 index 0000000000000..e5a6ab78e34c9 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueSceneEntry.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto; + +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Hue API scene object + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueSceneEntry { + public static enum TypeEnum { + LightScene, // 1.28 + GroupScene, // 1.28 + } + + public TypeEnum type = TypeEnum.LightScene; + + // A unique, editable name given to the group. + public String name; + public String description = ""; + + public String owner = ""; + public boolean recycle = false; + public boolean locked = false; + + final int version = 2; + + public String appdata = ""; + public String picture = ""; + + // The IDs of the lights that are in the group. + public @Nullable List lights; + public @Nullable String group; + + HueSceneEntry() { + name = ""; + } + + public HueSceneEntry(@Nullable String name) { + this.name = name != null ? name : ""; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueSceneWithLightstates.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueSceneWithLightstates.java new file mode 100644 index 0000000000000..ae77db63784ec --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueSceneWithLightstates.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto; + +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Hue API scene object with light states + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueSceneWithLightstates extends HueSceneEntry { + + public Map lightstates = new TreeMap<>(); + + HueSceneWithLightstates() { + super(); + } + + public HueSceneWithLightstates(HueSceneEntry e) { + this.type = e.type; + this.name = e.name; + this.description = e.description; + this.owner = e.owner; + this.recycle = e.recycle; + this.locked = e.locked; + this.appdata = e.appdata; + this.picture = e.picture; + this.lights = e.lights; + this.group = e.group; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueScheduleEntry.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueScheduleEntry.java new file mode 100644 index 0000000000000..b38b17e58cf54 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueScheduleEntry.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto; + +import org.openhab.io.hueemulation.internal.dto.changerequest.HueCommand; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueChangeScheduleEntry; + +/** + * Hue API scan result object. + * Enpoint: /api/{username}/lights/new + * + * @author David Graeff - Initial contribution + */ +public class HueScheduleEntry extends HueChangeScheduleEntry { + /** + * Assign default values to all fields that are otherwise nullable by the {@link HueChangeScheduleEntry}. + */ + public HueScheduleEntry() { + command = new HueCommand(); + name = ""; + description = ""; + localtime = ""; + status = "disabled"; + recycle = false; + autodelete = true; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueSensorEntry.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueSensorEntry.java new file mode 100644 index 0000000000000..fa9015d62c907 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueSensorEntry.java @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto; + +import java.lang.reflect.Type; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.items.GenericItem; +import org.eclipse.smarthome.core.library.CoreItemFactory; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.OpenClosedType; +import org.eclipse.smarthome.core.types.State; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * Hue API scene object + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueSensorEntry { + // A unique, editable name given to the group. + public String name; + public String type; + public String modelid; + public String manufacturername = "openHab"; + public String swversion = "1.0"; + public Object config = new Object(); + public String uniqueid; + + public final @NonNullByDefault({}) transient GenericItem item; + + private HueSensorEntry() { + item = null; + name = ""; + type = ""; + modelid = ""; + uniqueid = ""; + } + + public HueSensorEntry(GenericItem item) throws IllegalArgumentException { + this.item = item; + String label = item.getLabel(); + this.name = label != null ? label : item.getName(); + this.modelid = "openHAB_" + item.getType(); + this.uniqueid = item.getUID(); + switch (item.getType()) { + case CoreItemFactory.CONTACT: + this.type = "CLIPOpenClose"; // "open" + break; + case CoreItemFactory.ROLLERSHUTTER: + case CoreItemFactory.DIMMER: + this.type = "CLIPGenericStatus"; // "status" (int) + break; + case CoreItemFactory.NUMBER: + if (item.hasTag("temperature")) { + this.type = "CLIPTemperature"; // "temperature" + } else { + this.type = "CLIPLightLevel"; // "lightlevel" (int), "dark" (bool), "daylight" (bool) + } + break; + case CoreItemFactory.COLOR: + this.type = "CLIPLightLevel"; // "lightlevel" (int), "dark" (bool), "daylight" (bool) + break; + case CoreItemFactory.SWITCH: + this.type = "CLIPGenericFlag"; // "flag" (bool) + break; + default: + throw new IllegalArgumentException("Item type not supported as sensor"); + } + } + + /** + * This custom serializer computes the {@link HueGroupEntry#lights} list, before serializing. + * It does so, by looking up all item members of the references groupItem. + */ + @NonNullByDefault({}) + public static class Serializer implements JsonSerializer { + + static class HueHelper extends HueSensorEntry { + + } + + @Override + public JsonElement serialize(HueSensorEntry product, Type type, JsonSerializationContext context) { + JsonElement json = context.serialize(product, HueHelper.class); + JsonObject state = new JsonObject(); + State itemState = product.item.getState(); + switch (product.type) { + case "CLIPOpenClose": + if (itemState instanceof OpenClosedType) { + state.addProperty("open", ((OpenClosedType) product.item.getState()) == OpenClosedType.OPEN); + } else { + state.addProperty("open", false); + } + break; + case "CLIPGenericStatus": + if (itemState instanceof DecimalType) { + state.addProperty("status", ((DecimalType) product.item.getState()).intValue()); + } else { + state.addProperty("status", 0); + } + break; + case "CLIPTemperature": + if (itemState instanceof DecimalType) { + state.addProperty("temperature", ((DecimalType) product.item.getState()).intValue()); + } else { + state.addProperty("status", 0); + } + break; + case "CLIPLightLevel": + if (itemState instanceof DecimalType) { + state.addProperty("lightlevel", ((DecimalType) product.item.getState()).intValue()); + } else { + state.addProperty("status", 0); + } + state.addProperty("dark", false); + state.addProperty("daylight", false); + break; + case "CLIPGenericFlag": + if (itemState instanceof OnOffType) { + state.addProperty("flag", ((OnOffType) product.item.getState()) == OnOffType.ON); + } else { + state.addProperty("open", false); + } + break; + } + json.getAsJsonObject().add("state", state); + return json; + } + } +} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateBulb.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateBulb.java similarity index 89% rename from addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateBulb.java rename to bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateBulb.java index 22e396b454373..6503592048f0b 100644 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateBulb.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateBulb.java @@ -50,6 +50,15 @@ public HueStateBulb(PercentType brightness, boolean on) { this.bri = (int) (brightness.intValue() * MAX_BRI / 100.0 + 0.5); } + public PercentType toBrightnessType() { + int bri = this.bri * 100 / MAX_BRI; + + if (!this.on) { + bri = 0; + } + return new PercentType(bri); + } + @Override public String toString() { return "on: " + on + ", brightness: " + bri + ", reachable: " + reachable; diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateColorBulb.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateColorBulb.java similarity index 80% rename from addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateColorBulb.java rename to bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateColorBulb.java index d0b0a86498eb6..b313ffaed643a 100644 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateColorBulb.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateColorBulb.java @@ -85,14 +85,15 @@ public HueStateColorBulb(HSBType hsb) { public HSBType toHSBType() { if (colormode == ColorMode.xy) { int i; - double d = this.xy[0]; - d = this.xy[1]; - double y = ((double) this.bri) / 100.0d; + double y = (this.bri) / 100.0d; double x = (y / this.xy[1]) * this.xy[0]; double z = (y / this.xy[1]) * ((1.0d - this.xy[0]) - this.xy[1]); - int r = (int) (Math.abs(((1.4628067016601562d * x) - (0.18406230211257935d * y)) - (0.2743605971336365d * z)) * 255.0d); - int g = (int) (Math.abs((((-x) * 0.5217933058738708d) + (1.4472380876541138d * y)) + (0.06772270053625107d * z)) * 255.0d); - int b = (int) (Math.abs(((0.03493420034646988d * x) - (0.09689299762248993d * y)) + (1.288409948348999d * z)) * 255.0d); + int r = (int) (Math.abs( + ((1.4628067016601562d * x) - (0.18406230211257935d * y)) - (0.2743605971336365d * z)) * 255.0d); + int g = (int) (Math.abs( + (((-x) * 0.5217933058738708d) + (1.4472380876541138d * y)) + (0.06772270053625107d * z)) * 255.0d); + int b = (int) (Math.abs( + ((0.03493420034646988d * x) - (0.09689299762248993d * y)) + (1.288409948348999d * z)) * 255.0d); if (r < g) { i = r; } else { @@ -108,32 +109,34 @@ public HSBType toHSBType() { } double delta = maxValue - minValue; if (maxValue <= 0.0d) { - return new HSBType(new DecimalType(0), new PercentType(100), new PercentType((this.bri * 100) / MAX_BRI)); + return new HSBType(new DecimalType(0), new PercentType(100), + new PercentType((this.bri * 100) / MAX_BRI)); } double h; - if (((double) r) >= maxValue) { - h = ((double) (g - b)) / delta; - } else if (((double) g) >= maxValue) { - h = 2.0d + (((double) (b - r)) / delta); + if ((r) >= maxValue) { + h = (g - b) / delta; + } else if ((g) >= maxValue) { + h = 2.0d + ((b - r) / delta); } else { - h = 4.0d + (((double) (r - g)) / delta); + h = 4.0d + ((r - g) / delta); } h *= 60.0d; if (h < 0.0d) { h += 360.0d; } double hueSat = Math.floor((delta / maxValue) * 254.0d); - int percentSat = (int) ((100.0d * hueSat) / ((double) MAX_SAT)); + int percentSat = (int) ((100.0d * hueSat) / (MAX_SAT)); if (!this.on) { this.bri = 0; } - return new HSBType(new DecimalType((Math.floor(182.04d * h) * 360.0d) / ((double) MAX_HUE)), new PercentType(percentSat), new PercentType((this.bri * 100) / MAX_BRI)); - + return new HSBType(new DecimalType((Math.floor(182.04d * h) * 360.0d) / (MAX_HUE)), + new PercentType(percentSat), new PercentType((this.bri * 100) / MAX_BRI)); + } else { int bri = this.bri * 100 / MAX_BRI; int sat = this.sat * 100 / MAX_SAT; int hue = this.hue * 360 / MAX_HUE; - + if (!this.on) { bri = 0; } diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStatePlug.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStatePlug.java similarity index 84% rename from addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStatePlug.java rename to bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStatePlug.java index 1005ffd849667..b6b90b35c4ebf 100644 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStatePlug.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStatePlug.java @@ -12,6 +12,8 @@ */ package org.openhab.io.hueemulation.internal.dto; +import org.eclipse.smarthome.core.library.types.OnOffType; + /** * Hue API state object for plugs * @@ -28,6 +30,10 @@ public HueStatePlug(boolean on) { this.on = on; } + public OnOffType toOnOffType() { + return on ? OnOffType.ON : OnOffType.OFF; + } + @Override public String toString() { return "on: " + on + ", reachable: " + reachable; diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUnauthorizedConfig.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUnauthorizedConfig.java similarity index 77% rename from addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUnauthorizedConfig.java rename to bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUnauthorizedConfig.java index 499b41e39b769..49493c1c223b7 100644 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUnauthorizedConfig.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUnauthorizedConfig.java @@ -12,15 +12,19 @@ */ package org.openhab.io.hueemulation.internal.dto; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + /** * Hue API config object. Also accessible for non-authorized users. * Enpoint: /api/config * * @author David Graeff - Initial contribution */ +@NonNullByDefault public class HueUnauthorizedConfig { - public String apiversion = "1.16.0"; - public String bridgeid = "00212EFFFF022F6E"; + public String apiversion = "1.18.0"; + public String bridgeid = ""; // Example: 00212EFFFF022F6E public String datastoreversion = "60"; public String starterkitid = ""; public String modelid = "BSB002"; @@ -29,5 +33,5 @@ public class HueUnauthorizedConfig { public String swversion = "2.5.46"; public String mac = "b8:27:eb:1d:d8:0c"; public boolean factorynew = false; - public String replacesbridgeid = null; + public @Nullable String replacesbridgeid = null; } diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUserAuth.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUserAuth.java similarity index 77% rename from addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUserAuth.java rename to bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUserAuth.java index 8f0f7fc3fd460..0b356d7352799 100644 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUserAuth.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUserAuth.java @@ -15,29 +15,34 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * Hue user object. Used by {@link HueAuthorizedConfig}. * * @author David Graeff - Initial contribution */ +@NonNullByDefault public class HueUserAuth { public String name = ""; public String createDate = ""; public String lastUseDate = ""; + public String clientKey = ""; + /** * For de-serialization. */ - public HueUserAuth() { + HueUserAuth() { } /** * Create a new user * - * @param name Visible name + * @param apikey The hue "username" */ - public HueUserAuth(String name) { - this.name = name; + public HueUserAuth(String appName, String deviceName) { + this.name = appName + "#" + deviceName; this.createDate = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); } -} \ No newline at end of file +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUserAuthWithSecrets.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUserAuthWithSecrets.java new file mode 100644 index 0000000000000..b6490beeb61ce --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueUserAuthWithSecrets.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.common.registry.Identifiable; + +/** + * Hue user object. This is stored on disk for hue users, but not send via the rest api + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueUserAuthWithSecrets extends HueUserAuth implements Identifiable { + public String apiKey = ""; + + /** + * For de-serialization. + */ + HueUserAuthWithSecrets() { + } + + /** + * Create a new user with credentials + */ + public HueUserAuthWithSecrets(String appName, String deviceName, String apiKey, String clientKey) { + super(appName, deviceName); + this.apiKey = apiKey; + this.clientKey = clientKey; + } + + @Override + public String getUID() { + return apiKey; + } +} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueChangeRequest.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueChangeRequest.java similarity index 91% rename from addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueChangeRequest.java rename to bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueChangeRequest.java index a5304b04f7669..bf7f5287c289a 100644 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueChangeRequest.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueChangeRequest.java @@ -16,11 +16,12 @@ import org.eclipse.jdt.annotation.Nullable; /** - * Multiple endpoints support POST changes, for example: + * Multiple endpoints support PUT changes, for example: *
    *
  • Config: Allows to change the bridge name, dhcp, portalservices, linkbutton *
  • Light: Allows to change the name *
  • Group: Allows to change the name + *
  • Sensor: Allows to change the name *
* * @author David Graeff - Initial contribution diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueChangeSceneEntry.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueChangeSceneEntry.java new file mode 100644 index 0000000000000..0523dd0d4623a --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueChangeSceneEntry.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto.changerequest; + +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Hue API scan result object. + * Enpoint: /api/{username}/scenes/ab341ef24/lights/1/state + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueChangeSceneEntry { + public @Nullable String name; + public @Nullable String description; + + public boolean storelightstate = false; + + public @Nullable List lights; + + public @Nullable Map lightstates; +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueChangeScheduleEntry.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueChangeScheduleEntry.java new file mode 100644 index 0000000000000..897a8ec834151 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueChangeScheduleEntry.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto.changerequest; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Hue API scan result object. + * Enpoint: /api/{username}/lights/new + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueChangeScheduleEntry { + public @Nullable String name; + public @Nullable String description; + + public @Nullable String localtime; + // Either "enabled" or "disabled" + public @Nullable String status; + public @Nullable Boolean autodelete; + public @Nullable Boolean recycle; + + public @Nullable HueCommand command; +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueCommand.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueCommand.java new file mode 100644 index 0000000000000..6e1e3a67b148e --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueCommand.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto.changerequest; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Commands are http request data (method+url+body) used by schedules and rules. + *

+ * Note: Rules use shorter address variants without the "/api/username" parts, which + * makes them no valid relative urls! + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueCommand { + public String address = ""; + public String method = ""; + public String body = ""; + + public boolean isValid() { + return !address.isEmpty() && !method.isEmpty() && !body.isEmpty(); + } + + public HueCommand() { + } + + public HueCommand(String address, String method, String body) { + this.address = address; + this.method = method; + this.body = body; + } +} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueCreateUser.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueCreateUser.java similarity index 75% rename from addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueCreateUser.java rename to bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueCreateUser.java index 8105d48b71cca..7a04b433aefd9 100644 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueCreateUser.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueCreateUser.java @@ -13,7 +13,6 @@ package org.openhab.io.hueemulation.internal.dto.changerequest; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; /** * Hue API create user object @@ -23,8 +22,6 @@ */ @NonNullByDefault public class HueCreateUser { - /** The device label/name */ + /** ApplicationName#DeviceName */ public String devicetype = ""; - /** Caller suggested API key ("username"). Usually empty to generate one. Newer hue bridges always generate one. */ - public @Nullable String username; } diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateChange.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueStateChange.java similarity index 94% rename from addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateChange.java rename to bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueStateChange.java index ee979757b9660..a3018a9f21ab4 100644 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/HueStateChange.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/changerequest/HueStateChange.java @@ -10,7 +10,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.io.hueemulation.internal.dto; +package org.openhab.io.hueemulation.internal.dto.changerequest; import java.util.List; diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueResponse.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueResponse.java new file mode 100644 index 0000000000000..7a52ff3ece764 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueResponse.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto.response; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Hue API response base type + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueResponse { + public static final int UNAUTHORIZED = 1; + public static final int INVALID_JSON = 2; + public static final int NOT_AVAILABLE = 3; + public static final int METHOD_NOT_ALLOWED = 4; + public static final int ARGUMENTS_INVALID = 7; + public static final int SENSOR_NOT_CLIP_SENSOR = 8; + public static final int LINK_BUTTON_NOT_PRESSED = 101; + public static final int INTERNAL_ERROR = 901; + + public static final int RULE_ENGINE_FULL = 601; // The Rule Engine has reached its maximum capacity of 100 rules. + public static final int CONDITION_ERROR = 607; // Rule conditions contain errors or operator combination is not + // allowed + public static final int ACTION_ERROR = 608; // Rule actions contain errors or multiple actions with the same + // resource address. + public static final int TOO_MANY_ITEMS = 11; // Too many items in the list (too many conditions or too many actions) + + public final @Nullable HueErrorMessage error; + public final @Nullable HueSuccessResponse success; + + public HueResponse(HueErrorMessage error) { + this.error = error; + this.success = null; + } + + public HueResponse(HueSuccessResponse success) { + this.error = null; + this.success = success; + } + + public static class HueErrorMessage { + public final String address; + public final String description; + public final int type; + + public HueErrorMessage(int type, String address, String description) { + this.type = type; + this.address = address; + this.description = description; + } + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueResponseSuccessSimple.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueResponseSuccessSimple.java new file mode 100644 index 0000000000000..4fb2308ecd869 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueResponseSuccessSimple.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto.response; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Hue API response base type + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueResponseSuccessSimple { + public final String success; + + public HueResponseSuccessSimple(String success) { + this.success = success; + } +} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessCreateGroup.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessCreateGroup.java similarity index 84% rename from addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessCreateGroup.java rename to bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessCreateGroup.java index e7a69c3101586..884503cd9afb3 100644 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessCreateGroup.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessCreateGroup.java @@ -26,10 +26,10 @@ * * @author David Graeff - Initial contribution */ -public class HueSuccessCreateGroup implements HueSuccessResponse { - public int id; +public class HueSuccessCreateGroup extends HueSuccessResponse { + public String id; - public HueSuccessCreateGroup(int id) { + public HueSuccessCreateGroup(String id) { this.id = id; } } diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessGeneric.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessGeneric.java new file mode 100644 index 0000000000000..a46c4ff9d292e --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessGeneric.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto.response; + +import java.lang.reflect.Type; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * This object describes the right hand side of "success". + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueSuccessGeneric extends HueSuccessResponse { + public String message; + public transient String key; + + public HueSuccessGeneric(@Nullable Object message, String key) { + this.message = message != null ? String.valueOf(message) : ""; + this.key = key; + } + + public static class Serializer implements JsonSerializer { + @NonNullByDefault({}) + @Override + public JsonElement serialize(HueSuccessGeneric product, Type type, JsonSerializationContext jsc) { + JsonObject jObj = new JsonObject(); + jObj.addProperty(product.key, product.message); + return jObj; + } + } + + public boolean isValid() { + return !message.isEmpty(); + } +} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponse.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponse.java similarity index 93% rename from addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponse.java rename to bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponse.java index 9e2c22450ddb7..da6f29f2dfcae 100644 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponse.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponse.java @@ -17,5 +17,5 @@ * * @author David Graeff - Initial contribution */ -public interface HueSuccessResponse { +public class HueSuccessResponse { } diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseCreateUser.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseCreateUser.java new file mode 100644 index 0000000000000..e93675b189a27 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseCreateUser.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.dto.response; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * This object describes the right hand side of "success". + * The response looks like this: + * + *

+ * {
+ *   "success":{
+ *      "username": "-the-username-"
+ *   }
+ * }
+ * 
+ * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class HueSuccessResponseCreateUser extends HueSuccessResponse { + public String username; + // For DTLS setup of the new hue entertain DTLS/UDP protocol + // The PSK identity matches the “usernameâ€, and the PSK key matches the “clientkeyâ€. + public String clientkey; + + public HueSuccessResponseCreateUser(String username, String clientkey) { + this.username = username; + this.clientkey = clientkey; + } +} diff --git a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseStateChanged.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseStateChanged.java similarity index 96% rename from addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseStateChanged.java rename to bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseStateChanged.java index d47626adf980b..8e71d198f501b 100644 --- a/addons/io/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseStateChanged.java +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/dto/response/HueSuccessResponseStateChanged.java @@ -37,7 +37,7 @@ * * @author David Graeff - Initial contribution */ -public class HueSuccessResponseStateChanged implements HueSuccessResponse { +public class HueSuccessResponseStateChanged extends HueSuccessResponse { private transient Object value; private transient String relURI; @@ -47,7 +47,6 @@ public HueSuccessResponseStateChanged(String relURI, Object value) { } public static class Serializer implements JsonSerializer { - @Override public JsonElement serialize(HueSuccessResponseStateChanged product, Type type, JsonSerializationContext jsc) { JsonObject jObj = new JsonObject(); diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/ConfigurationAccess.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/ConfigurationAccess.java new file mode 100644 index 0000000000000..09ba6045c7959 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/ConfigurationAccess.java @@ -0,0 +1,138 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.NetworkUtils; +import org.openhab.io.hueemulation.internal.dto.HueUnauthorizedConfig; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueChangeRequest; +import org.openhab.io.hueemulation.internal.dto.response.HueResponse; +import org.openhab.io.hueemulation.internal.dto.response.HueResponse.HueErrorMessage; +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import com.google.gson.reflect.TypeToken; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +/** + * @author David Graeff - Initial contribution + */ +@Component(immediate = false, service = { + ConfigurationAccess.class }, property = "com.eclipsesource.jaxrs.publish=false") +@NonNullByDefault +@Path("") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ConfigurationAccess { + @Reference + protected @NonNullByDefault({}) ConfigStore cs; + @Reference + protected @NonNullByDefault({}) UserManagement userManagement; + @Reference + protected @NonNullByDefault({}) ConfigurationAdmin configAdmin; + + @GET + @Path("config") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Return the reduced configuration") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getReducedConfigApi() { + return Response.ok(cs.gson.toJson(cs.ds.config, new TypeToken() { + }.getType())).build(); + } + + @GET + @Path("{username}") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Return the full data store") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getAllApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username) throws IOException { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds)).build(); + } + + @GET + @Path("{username}/config") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Return the configuration") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getFullConfigApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username) throws IOException { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.config)).build(); + } + + @PUT + @Path("{username}/config") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Return the reduced configuration") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response putFullConfigApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username, String body) throws IOException { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + final HueChangeRequest changes; + changes = cs.gson.fromJson(body, HueChangeRequest.class); + String devicename = changes.devicename; + if (devicename != null) { + cs.ds.config.devicename = devicename; + } + Boolean dhcp = changes.dhcp; + if (dhcp != null) { + cs.ds.config.dhcp = dhcp; + } + Boolean linkbutton = changes.linkbutton; + if (linkbutton != null) { + cs.setLinkbutton(linkbutton, cs.getConfig().createNewUserOnEveryEndpoint, + cs.getConfig().temporarilyEmulateV1bridge); + } + return Response.ok(cs.gson.toJson(cs.ds.config)).build(); + } + + @Path("{username}/{var:.+}") + @Produces(MediaType.APPLICATION_JSON) + public Response catchAll(@Context UriInfo uri) { + HueResponse e = new HueResponse( + new HueErrorMessage(HueResponse.INVALID_JSON, uri.getPath().replace("/api", ""), "Invalid request: ")); + String str = cs.gson.toJson(Collections.singleton(e), new TypeToken>() { + }.getType()); + return Response.status(404).entity(str).build(); + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/LightsAndGroups.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/LightsAndGroups.java new file mode 100644 index 0000000000000..e66d62fb92759 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/LightsAndGroups.java @@ -0,0 +1,525 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.common.registry.RegistryChangeListener; +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.items.GenericItem; +import org.eclipse.smarthome.core.items.GroupItem; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.ItemRegistry; +import org.eclipse.smarthome.core.items.events.ItemEventFactory; +import org.eclipse.smarthome.core.library.CoreItemFactory; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.DeviceType; +import org.openhab.io.hueemulation.internal.NetworkUtils; +import org.openhab.io.hueemulation.internal.StateUtils; +import org.openhab.io.hueemulation.internal.dto.HueGroupEntry; +import org.openhab.io.hueemulation.internal.dto.HueLightEntry; +import org.openhab.io.hueemulation.internal.dto.HueNewLights; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueChangeRequest; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueStateChange; +import org.openhab.io.hueemulation.internal.dto.response.HueResponse; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.reflect.TypeToken; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +/** + * Listens to the ItemRegistry for items that fulfill one of these criteria: + *
    + *
  • Type is any of SWITCH, DIMMER, COLOR, or Group + *
  • The category is "ColorLight" for coloured lights or "Light" for switchables. + *
  • The item is tagged, according to what is set with {@link #setFilterTags(Set, Set, Set)}. + *
+ * + *

+ * A {@link HueLightEntry} instances is created for each found item. + * Those are kept in the given {@link org.openhab.io.hueemulation.internal.dto.HueDataStore}. + *

+ * + *

+ * The HUE Rest API requires a unique string based ID for every listed light. + * We are using item names here. Not all hue clients might be compatible with non + * numeric Ics.ds. A solution could be an ItemMetaData provider and to store a + * generated integer id for each item. + *

+ * + *

+ *

+ * + * @author David Graeff - Initial contribution + * @author Florian Schmidt - Removed base type restriction from Group items + */ +@Component(immediate = false, service = { LightsAndGroups.class }, property = "com.eclipsesource.jaxrs.publish=false") +@NonNullByDefault +@Path("") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class LightsAndGroups implements RegistryChangeListener { + private final Logger logger = LoggerFactory.getLogger(LightsAndGroups.class); + private static final String ITEM_TYPE_GROUP = "Group"; + private static final Set ALLOWED_ITEM_TYPES = Stream.of(CoreItemFactory.COLOR, CoreItemFactory.DIMMER, + CoreItemFactory.ROLLERSHUTTER, CoreItemFactory.SWITCH, ITEM_TYPE_GROUP).collect(Collectors.toSet()); + + @Reference + protected @NonNullByDefault({}) ConfigStore cs; + @Reference + protected @NonNullByDefault({}) UserManagement userManagement; + @Reference + protected @NonNullByDefault({}) ItemRegistry itemRegistry; + @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL) + protected volatile @NonNullByDefault({}) EventPublisher eventPublisher; + + /** + * Registers to the {@link ItemRegistry} and enumerates currently existing items. + */ + @Activate + protected void activate() { + cs.ds.resetGroupsAndLights(); + + itemRegistry.removeRegistryChangeListener(this); + itemRegistry.addRegistryChangeListener(this); + + for (Item item : itemRegistry.getItems()) { + added(item); + } + } + + /** + * Unregisters from the {@link ItemRegistry}. + */ + @Deactivate + protected void deactivate() { + itemRegistry.removeRegistryChangeListener(this); + } + + @Override + public synchronized void added(Item newElement) { + if (!(newElement instanceof GenericItem)) { + return; + } + GenericItem element = (GenericItem) newElement; + + if (!(element instanceof GroupItem) && !ALLOWED_ITEM_TYPES.contains(element.getType())) { + return; + } + + DeviceType deviceType = StateUtils.determineTargetType(cs, element); + if (deviceType == null) { + return; + } + + String hueID = cs.mapItemUIDtoHueID(element); + + if (element instanceof GroupItem) { + GroupItem g = (GroupItem) element; + HueGroupEntry group = new HueGroupEntry(g.getName(), g, deviceType); + + // Restore group type and room class from tags + for (String tag : g.getTags()) { + if (tag.startsWith("huetype_")) { + group.type = tag.split("huetype_")[1]; + } else if (tag.startsWith("hueroom_")) { + group.roomclass = tag.split("hueroom_")[1]; + } + } + + // Add group members + group.lights = new ArrayList<>(); + for (Item item : g.getMembers()) { + group.lights.add(cs.mapItemUIDtoHueID(item)); + } + + cs.ds.groups.put(hueID, group); + } else { + HueLightEntry device = new HueLightEntry(element, cs.ds.config.uuid + "-" + hueID.toString(), deviceType); + device.item = element; + cs.ds.lights.put(hueID, device); + updateGroup0(); + } + } + + /** + * The HUE API enforces a Group 0 that contains all lights. + */ + private void updateGroup0() { + cs.ds.groups.get("0").lights = cs.ds.lights.keySet().stream().map(v -> String.valueOf(v)) + .collect(Collectors.toList()); + } + + @Override + public synchronized void removed(Item element) { + String hueID = cs.mapItemUIDtoHueID(element); + logger.debug("Remove item {}", hueID); + cs.ds.lights.remove(hueID); + cs.ds.groups.remove(hueID); + updateGroup0(); + } + + /** + * The tags might have changed + */ + @SuppressWarnings({ "null", "unused" }) + @Override + public synchronized void updated(Item oldElement, Item newElement) { + if (!(newElement instanceof GenericItem)) { + return; + } + GenericItem element = (GenericItem) newElement; + + String hueID = cs.mapItemUIDtoHueID(element); + + HueGroupEntry hueGroup = cs.ds.groups.get(hueID); + if (hueGroup != null) { + DeviceType t = StateUtils.determineTargetType(cs, element); + if (t != null && element instanceof GroupItem) { + hueGroup.updateItem((GroupItem) element); + } else { + cs.ds.groups.remove(hueID); + } + } + + HueLightEntry hueDevice = cs.ds.lights.get(hueID); + if (hueDevice == null) { + // If the correct tags got added -> use the logic within added() + added(element); + return; + } + + // Check if type can still be determined (tags and category is still sufficient) + DeviceType t = StateUtils.determineTargetType(cs, element); + if (t == null) { + removed(element); + return; + } + + hueDevice.updateItem(element); + } + + @GET + @Path("{username}/lights") + @ApiOperation(value = "Return all lights") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getAllLightsApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.lights)).build(); + } + + @GET + @Path("{username}/lights/new") + @ApiOperation(value = "Return new lights since last scan. Returns an empty list for openHAB as we do not cache that information.") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getNewLights(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(new HueNewLights())).build(); + } + + @POST + @Path("{username}/lights") + @ApiOperation(value = "Starts a new scan for compatible items. This is usually not necessary, because we are observing the item registry.") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response postNewLights(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return NetworkUtils.singleSuccess(cs.gson, "Searching for new devices", "/lights"); + } + + @GET + @Path("{username}/lights/{id}") + @ApiOperation(value = "Return a light") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getLightApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "light id") String id) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.lights.get(id))).build(); + } + + @SuppressWarnings({ "null", "unused" }) + @DELETE + @Path("{username}/lights/{id}") + @ApiOperation(value = "Deletes the item that is represented by this id") + @ApiResponses(value = { @ApiResponse(code = 200, message = "The item got removed"), + @ApiResponse(code = 403, message = "Access denied") }) + public Response removeLightAPI(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "id") String id) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + HueLightEntry hueDevice = cs.ds.lights.get(id); + if (hueDevice == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Light does not exist"); + } + + if (itemRegistry.remove(id) != null) { + return NetworkUtils.singleSuccess(cs.gson, "/lights/" + id + " deleted."); + } else { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Light does not exist"); + } + } + + @SuppressWarnings({ "null", "unused" }) + @PUT + @Path("{username}/lights/{id}") + @ApiOperation(value = "Rename a light") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response renameLightApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "light id") String id, String body) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + HueLightEntry hueDevice = cs.ds.lights.get(id); + if (hueDevice == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Light not existing"); + } + + final HueChangeRequest changeRequest = cs.gson.fromJson(body, HueChangeRequest.class); + + String name = changeRequest.name; + if (name == null || name.isEmpty()) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.INVALID_JSON, "Invalid request: No name set"); + } + + hueDevice.item.setLabel(name); + itemRegistry.update(hueDevice.item); + + return NetworkUtils.singleSuccess(cs.gson, name, "/lights/" + id + "/name"); + } + + @SuppressWarnings({ "null", "unused" }) + @PUT + @Path("{username}/lights/{id}/state") + @ApiOperation(value = "Set light state") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response setLightStateApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "light id") String id, String body) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + HueLightEntry hueDevice = cs.ds.lights.get(id); + if (hueDevice == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Light not existing"); + } + + HueStateChange newState = cs.gson.fromJson(body, HueStateChange.class); + if (newState == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.INVALID_JSON, + "Invalid request: No state change data received!"); + } + + hueDevice.state = StateUtils.colorStateFromItemState(hueDevice.item.getState(), hueDevice.deviceType); + + String itemUID = hueDevice.item.getUID(); + List responses = new ArrayList<>(); + Command command = StateUtils.computeCommandByState(responses, "/lights/" + id + "/state", hueDevice.state, + newState); + + // If a command could be created, post it to the framework now + if (command != null) { + if (eventPublisher != null) { + logger.debug("sending {} to {}", command, itemUID); + eventPublisher.post(ItemEventFactory.createCommandEvent(itemUID, command, "hueemulation")); + } else { + logger.warn("No event publisher. Cannot post item '{}' command!", itemUID); + } + } + + return Response.ok(cs.gson.toJson(responses, new TypeToken>() { + }.getType())).build(); + } + + @SuppressWarnings({ "null", "unused" }) + @PUT + @Path("{username}/groups/{id}/action") + @ApiOperation(value = "Initiate group action") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response setGroupActionApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "group id") String id, String body) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + HueGroupEntry hueDevice = cs.ds.groups.get(id); + GroupItem groupItem = hueDevice.groupItem; + if (hueDevice == null || groupItem == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Group not existing"); + } + + HueStateChange state = cs.gson.fromJson(body, HueStateChange.class); + if (state == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.INVALID_JSON, + "Invalid request: No state change data received!"); + } + + // First synchronize the internal state information with the framework + hueDevice.action = StateUtils.colorStateFromItemState(groupItem.getState(), hueDevice.deviceType); + + List responses = new ArrayList<>(); + Command command = StateUtils.computeCommandByState(responses, "/groups/" + id + "/state/", hueDevice.action, + state); + + // If a command could be created, post it to the framework now + if (command != null) { + logger.debug("sending {} to {}", command, id); + if (eventPublisher != null) { + eventPublisher.post(ItemEventFactory.createCommandEvent(groupItem.getUID(), command, "hueemulation")); + } else { + logger.warn("No event publisher. Cannot post item '{}' command!", groupItem.getUID()); + } + } + + return Response.ok(cs.gson.toJson(responses, new TypeToken>() { + }.getType())).build(); + } + + @GET + @Path("{username}/groups") + @ApiOperation(value = "Return all groups") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getAllGroupsApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.groups)).build(); + } + + @GET + @Path("{username}/groups/{id}") + @ApiOperation(value = "Return a group") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getGroupApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "group id") String id) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.groups.get(id))).build(); + } + + @SuppressWarnings({ "null", "unused" }) + @POST + @Path("{username}/groups") + @ApiOperation(value = "Create a new group") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response postNewGroup(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username, String body) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + HueGroupEntry state = cs.gson.fromJson(body, HueGroupEntry.class); + if (state == null || state.name.isEmpty()) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.INVALID_JSON, + "Invalid request: No state change data received!"); + } + + String groupid = cs.ds.nextGroupID(); + GroupItem groupItem = new GroupItem(groupid); + + if (!HueGroupEntry.TypeEnum.LightGroup.name().equals(state.type)) { + groupItem.addTag("huetype_" + state.type); + } + + if (HueGroupEntry.TypeEnum.Room.name().equals(state.type) && !state.roomclass.isEmpty()) { + groupItem.addTag("hueroom_" + state.roomclass); + } + + List groupItems = new ArrayList<>(); + for (String id : state.lights) { + Item item = itemRegistry.get(id); + if (item == null) { + logger.debug("Could not create group {}. Item {} not existing!", state.name, id); + return NetworkUtils.singleError(cs.gson, uri, HueResponse.ARGUMENTS_INVALID, + "Invalid request: Item not existing"); + } + groupItem.addMember(item); + } + + itemRegistry.add(groupItem); + + return NetworkUtils.singleSuccess(cs.gson, groupid, "id"); + } + + @SuppressWarnings({ "null", "unused" }) + @DELETE + @Path("{username}/groups/{id}") + @ApiOperation(value = "Deletes the item that is represented by this id") + @ApiResponses(value = { @ApiResponse(code = 200, message = "The item got removed"), + @ApiResponse(code = 403, message = "Access denied") }) + public Response removeGroupAPI(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "id") String id) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + HueLightEntry hueDevice = cs.ds.lights.get(id); + if (hueDevice == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Group does not exist"); + } + + if (itemRegistry.remove(id) != null) { + return NetworkUtils.singleSuccess(cs.gson, "/groups/" + id + " deleted."); + } else { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Group does not exist"); + } + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/Rules.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/Rules.java new file mode 100644 index 0000000000000..56b30bc5544fc --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/Rules.java @@ -0,0 +1,373 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import java.io.IOException; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.UUID; +import java.util.stream.Collectors; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.common.registry.RegistryChangeListener; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.ItemRegistry; +import org.openhab.core.automation.Action; +import org.openhab.core.automation.Condition; +import org.openhab.core.automation.Rule; +import org.openhab.core.automation.RuleRegistry; +import org.openhab.core.automation.Trigger; +import org.openhab.core.automation.util.ModuleBuilder; +import org.openhab.core.automation.util.RuleBuilder; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.NetworkUtils; +import org.openhab.io.hueemulation.internal.RuleUtils; +import org.openhab.io.hueemulation.internal.dto.HueRuleEntry; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueCommand; +import org.openhab.io.hueemulation.internal.dto.response.HueResponse; +import org.openhab.io.hueemulation.internal.dto.response.HueSuccessGeneric; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +/** + * Handles Hue rules via the automation subsystem and the corresponding REST interface + * + * @author David Graeff - Initial contribution + */ +@Component(immediate = false, service = { Rules.class }, property = "com.eclipsesource.jaxrs.publish=false") +@NonNullByDefault +@Path("") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class Rules implements RegistryChangeListener { + public static final String RULES_TAG = "hueemulation_rule"; + + @Reference + protected @NonNullByDefault({}) ConfigStore cs; + @Reference + protected @NonNullByDefault({}) UserManagement userManagement; + @Reference + protected @NonNullByDefault({}) RuleRegistry ruleRegistry; + @Reference + protected @NonNullByDefault({}) ItemRegistry itemRegistry; + + /** + * Registers to the {@link RuleRegistry} and enumerates currently existing rules. + */ + @Activate + public void activate() { + ruleRegistry.removeRegistryChangeListener(this); + ruleRegistry.addRegistryChangeListener(this); + + for (Rule item : ruleRegistry.getAll()) { + added(item); + } + } + + @Deactivate + public void deactivate() { + ruleRegistry.removeRegistryChangeListener(this); + } + + @Override + public void added(Rule rule) { + if (!rule.getTags().contains(RULES_TAG)) { + return; + } + HueRuleEntry entry = new HueRuleEntry(rule.getName()); + String desc = rule.getDescription(); + if (desc != null) { + entry.description = desc; + } + + rule.getConditions().stream().filter(c -> c.getTypeUID().equals("hue.ruleCondition")).forEach(c -> { + HueRuleEntry.Condition condition = c.getConfiguration().as(HueRuleEntry.Condition.class); + // address with pattern "/sensors/2/state/buttonevent" + String[] parts = condition.address.split("/"); + if (parts.length < 3) { + return; + } + + entry.conditions.add(condition); + }); + + rule.getActions().stream().filter(a -> a.getTypeUID().equals("rules.HttpAction")).forEach(a -> { + HueCommand command = RuleUtils.httpActionToHueCommand(cs.ds, a, rule.getName()); + if (command == null) { + return; + } + // Remove the "/api/{user}" part + String[] parts = command.address.split("/"); + command.address = "/" + String.join("/", Arrays.copyOfRange(parts, 3, parts.length)); + entry.actions.add(command); + }); + + cs.ds.rules.put(rule.getUID(), entry); + } + + @Override + public void removed(Rule element) { + cs.ds.rules.remove(element.getUID()); + } + + @Override + public void updated(Rule oldElement, Rule element) { + removed(oldElement); + added(element); + } + + protected static Map.Entry hueConditionToAutomation(String id, HueRuleEntry.Condition condition, + ItemRegistry itemRegistry) { + // pattern: "/sensors/2/state/buttonevent" + String[] parts = condition.address.split("/"); + if (parts.length < 3) { + throw new IllegalStateException("Condition address invalid: " + condition.address); + } + + final Configuration triggerConfig = new Configuration(); + + String itemName = parts[2]; + + Item item = itemRegistry.get(itemName); + if (item == null) { + throw new IllegalStateException("Item of address does not exist: " + itemName); + } + + triggerConfig.put("itemName", itemName); + + // There might be multiple triggers for the same item. Due to the map, we are only creating one though + + Trigger trigger = ModuleBuilder.createTrigger().withId(id).withTypeUID("core.ItemStateChangeTrigger") + .withConfiguration(triggerConfig).build(); + + // Connect the outputs of the trigger with the inputs of the condition + Map inputs = new TreeMap<>(); + inputs.put("newState", id); + inputs.put("oldState", id); + + // Config for condition + final Configuration conditionConfig = new Configuration(); + conditionConfig.put("operator", condition.operator.name()); + conditionConfig.put("address", condition.address); + String value = condition.value; + if (value != null) { + conditionConfig.put("value", value); + } + + Condition conditon = ModuleBuilder.createCondition().withId(id + "-condition").withTypeUID("hue.ruleCondition") + .withConfiguration(conditionConfig).withInputs(inputs).build(); + + return new AbstractMap.SimpleEntry<>(trigger, conditon); + } + + protected static RuleBuilder createHueRuleConditions(List hueConditions, + RuleBuilder builder, List oldTriggers, List oldConditions, ItemRegistry itemRegistry) { + + // Preserve all triggers, conditions that are not part of hue rules + Map triggers = new TreeMap<>(); + triggers.putAll(oldTriggers.stream().filter(a -> !a.getTypeUID().equals("core.ItemStateChangeTrigger")) + .collect(Collectors.toMap(e -> e.getId(), e -> e))); + + Map conditions = new TreeMap<>(); + conditions.putAll(oldConditions.stream().filter(a -> !a.getTypeUID().equals("hue.ruleCondition")) + .collect(Collectors.toMap(e -> e.getId(), e -> e))); + + for (HueRuleEntry.Condition condition : hueConditions) { + String id = condition.address.replace("/", "-"); + Entry entry = hueConditionToAutomation(id, condition, itemRegistry); + triggers.put(id, entry.getKey()); + conditions.put(id, entry.getValue()); + } + + builder.withTriggers(new ArrayList<>(triggers.values())).withConditions(new ArrayList<>(conditions.values())); + return builder; + } + + protected static List createActions(String uid, List hueActions, List oldActions, + String apikey) { + // Preserve all actions that are not "rules.HttpAction" + List actions = new ArrayList<>(oldActions); + actions.removeIf(a -> a.getTypeUID().equals("rules.HttpAction")); + + for (HueCommand command : hueActions) { + command.address = "/api/" + apikey + command.address; + actions.add(RuleUtils.createHttpAction(command, command.address.replace("/", "-"))); + } + return actions; + } + + @GET + @Path("{username}/rules") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Return all rules") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getRulesApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username) throws IOException { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.rules)).build(); + } + + @GET + @Path("{username}/rules/{id}") + @ApiOperation(value = "Return a rule") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getRuleApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "rule id") String id) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.rules.get(id))).build(); + } + + @DELETE + @Path("{username}/rules/{id}") + @ApiOperation(value = "Deletes a rule") + @ApiResponses(value = { @ApiResponse(code = 200, message = "The user got removed"), + @ApiResponse(code = 403, message = "Access denied") }) + public Response removeRuleApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "Rule to remove") String id) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + Rule rule = ruleRegistry.remove(id); + if (rule == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Rule does not exist!"); + } + + return NetworkUtils.singleSuccess(cs.gson, "/rules/" + id + " deleted."); + } + + @PUT + @Path("{username}/rules/{id}") + @ApiOperation(value = "Set rule attributes") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response modifyRuleApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "rule id") String id, String body) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + final HueRuleEntry changeRequest = cs.gson.fromJson(body, HueRuleEntry.class); + + Rule rule = ruleRegistry.remove(id); + if (rule == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Rule does not exist!"); + } + + RuleBuilder builder = RuleBuilder.create(rule); + + String temp; + + temp = changeRequest.name; + if (!temp.isEmpty()) { + builder.withName(changeRequest.name); + } + + temp = changeRequest.description; + if (!temp.isEmpty()) { + builder.withDescription(temp); + } + + try { + if (!changeRequest.actions.isEmpty()) { + builder.withActions(createActions(rule.getUID(), changeRequest.actions, rule.getActions(), username)); + } + if (!changeRequest.conditions.isEmpty()) { + builder = createHueRuleConditions(changeRequest.conditions, builder, rule.getTriggers(), + rule.getConditions(), itemRegistry); + } + + ruleRegistry.add(builder.build()); + } catch (IllegalStateException e) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.ARGUMENTS_INVALID, e.getMessage()); + } + + return NetworkUtils.successList(cs.gson, Arrays.asList( // + new HueSuccessGeneric(changeRequest.name, "/rules/" + id + "/name"), // + new HueSuccessGeneric(changeRequest.description, "/rules/" + id + "/description"), // + new HueSuccessGeneric(changeRequest.actions.toString(), "/rules/" + id + "/actions"), // + new HueSuccessGeneric(changeRequest.conditions.toString(), "/rules/" + id + "/conditions") // + )); + } + + @SuppressWarnings({ "null" }) + @POST + @Path("{username}/rules") + @ApiOperation(value = "Create a new rule") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response postNewRule(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username, String body) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + HueRuleEntry newRuleData = cs.gson.fromJson(body, HueRuleEntry.class); + if (newRuleData == null || newRuleData.name.isEmpty() || newRuleData.actions.isEmpty() + || newRuleData.conditions.isEmpty()) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.INVALID_JSON, + "Invalid request: No name or actions or conditons!"); + } + + String uid = UUID.randomUUID().toString(); + RuleBuilder builder = RuleBuilder.create(uid).withName(newRuleData.name); + + String description = newRuleData.description; + if (description != null) { + builder.withDescription(description); + } + + try { + builder.withActions(createActions(uid, newRuleData.actions, Collections.emptyList(), username)); + builder = createHueRuleConditions(newRuleData.conditions, builder, Collections.emptyList(), + Collections.emptyList(), itemRegistry); + ruleRegistry.add(builder.withTags(RULES_TAG).build()); + } catch (IllegalStateException e) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.ARGUMENTS_INVALID, e.getMessage()); + } + + return NetworkUtils.singleSuccess(cs.gson, uid, "id"); + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/Scenes.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/Scenes.java new file mode 100644 index 0000000000000..64d6529efcbe6 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/Scenes.java @@ -0,0 +1,426 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.common.registry.RegistryChangeListener; +import org.eclipse.smarthome.core.items.GroupItem; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.ItemNotFoundException; +import org.eclipse.smarthome.core.items.ItemRegistry; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.openhab.core.automation.Action; +import org.openhab.core.automation.Rule; +import org.openhab.core.automation.RuleRegistry; +import org.openhab.core.automation.util.ModuleBuilder; +import org.openhab.core.automation.util.RuleBuilder; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.NetworkUtils; +import org.openhab.io.hueemulation.internal.StateUtils; +import org.openhab.io.hueemulation.internal.automation.dto.ItemCommandActionConfig; +import org.openhab.io.hueemulation.internal.dto.AbstractHueState; +import org.openhab.io.hueemulation.internal.dto.HueSceneEntry; +import org.openhab.io.hueemulation.internal.dto.HueSceneWithLightstates; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueChangeSceneEntry; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueStateChange; +import org.openhab.io.hueemulation.internal.dto.response.HueResponse; +import org.openhab.io.hueemulation.internal.dto.response.HueSuccessGeneric; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +/** + * Handles Hue scenes via the automation subsystem and the corresponding REST interface + * + * @author David Graeff - Initial contribution + */ +@Component(immediate = false, service = { Scenes.class }, property = "com.eclipsesource.jaxrs.publish=false") +@NonNullByDefault +@Path("") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class Scenes implements RegistryChangeListener { + private final Logger logger = LoggerFactory.getLogger(Scenes.class); + + @Reference + protected @NonNullByDefault({}) ConfigStore cs; + @Reference + protected @NonNullByDefault({}) UserManagement userManagement; + @Reference + protected @NonNullByDefault({}) RuleRegistry ruleRegistry; + @Reference + protected @NonNullByDefault({}) ItemRegistry itemRegistry; + + /** + * Registers to the {@link RuleRegistry} and enumerates currently existing rules. + */ + @Activate + public void activate() { + ruleRegistry.removeRegistryChangeListener(this); + ruleRegistry.addRegistryChangeListener(this); + + for (Rule item : ruleRegistry.getAll()) { + added(item); + } + } + + @Deactivate + public void deactivate() { + ruleRegistry.removeRegistryChangeListener(this); + } + + @Override + public void added(Rule scene) { + if (!scene.getTags().contains("scene")) { + return; + } + HueSceneEntry entry = new HueSceneEntry(scene.getName()); + String desc = scene.getDescription(); + if (desc != null) { + entry.description = desc; + } + + List items = new ArrayList<>(); + + for (Action a : scene.getActions()) { + if (!a.getTypeUID().equals("core.ItemCommandAction")) { + continue; + } + ItemCommandActionConfig config = a.getConfiguration().as(ItemCommandActionConfig.class); + Item item; + try { + item = itemRegistry.getItem(config.itemName); + } catch (ItemNotFoundException e) { + logger.warn("Rule {} is referring to a non existing item {}", scene.getName(), config.itemName); + continue; + } + if (scene.getActions().size() == 1 && item instanceof GroupItem) { + entry.type = HueSceneEntry.TypeEnum.GroupScene; + entry.group = cs.mapItemUIDtoHueID(item); + } else { + items.add(cs.mapItemUIDtoHueID(item)); + } + } + + if (items.size() > 0) { + entry.lights = items; + } + + cs.ds.scenes.put(scene.getUID(), entry); + } + + @Override + public void removed(Rule element) { + cs.ds.scenes.remove(element.getUID()); + } + + @Override + public void updated(Rule oldElement, Rule element) { + removed(oldElement); + added(element); + } + + @GET + @Path("{username}/scenes") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Return all scenes") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getScenesApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username) throws IOException { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.scenes)).build(); + } + + @SuppressWarnings({ "unused", "null" }) + @GET + @Path("{username}/scenes/{id}") + @ApiOperation(value = "Return a scene") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getSceneApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "scene id") String id) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + HueSceneEntry sceneEntry = cs.ds.scenes.get(id); + if (sceneEntry == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Scene does not exist!"); + } + HueSceneWithLightstates s = new HueSceneWithLightstates(sceneEntry); + for (String itemID : s.lights) { + Item item; + try { + item = itemRegistry.getItem(itemID); + } catch (ItemNotFoundException e) { + logger.warn("Scene {} is referring to a non existing item {}", sceneEntry.name, itemID); + continue; + } + AbstractHueState state = StateUtils.colorStateFromItemState(item.getState(), null); + s.lightstates.put(cs.mapItemUIDtoHueID(item), state); + } + + return Response.ok(cs.gson.toJson(s)).build(); + } + + @DELETE + @Path("{username}/scenes/{id}") + @ApiOperation(value = "Deletes a scene") + @ApiResponses(value = { @ApiResponse(code = 200, message = "The user got removed"), + @ApiResponse(code = 403, message = "Access denied") }) + public Response removeSceneApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "Scene to remove") String id) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + Rule rule = ruleRegistry.remove(id); + if (rule == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Scene does not exist!"); + } + + return NetworkUtils.singleSuccess(cs.gson, "/scenes/" + id + " deleted."); + } + + protected static Action actionFromState(String itemID, State state) { + final Configuration actionConfig = new Configuration(); + actionConfig.put("itemName", itemID); + actionConfig.put("command", StateUtils.commandByItemState(state).toFullString()); + return ModuleBuilder.createAction().withId(itemID).withTypeUID("core.ItemCommandAction") + .withConfiguration(actionConfig).build(); + } + + protected static Action actionFromState(String itemID, Command command) { + final Configuration actionConfig = new Configuration(); + actionConfig.put("itemName", itemID); + actionConfig.put("command", command.toFullString()); + return ModuleBuilder.createAction().withId(itemID).withTypeUID("core.ItemCommandAction") + .withConfiguration(actionConfig).build(); + } + + /** + * Either assigns a new name, description, lights to a scene or directly assign + * a new light state for an entry to a scene + */ + @PUT + @Path("{username}/scenes/{id}") + @ApiOperation(value = "Set scene attributes") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response modifySceneApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "scene id") String id, String body) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + final HueChangeSceneEntry changeRequest = cs.gson.fromJson(body, HueChangeSceneEntry.class); + + Rule rule = ruleRegistry.remove(id); + if (rule == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Scene does not exist!"); + } + + RuleBuilder builder = RuleBuilder.create(rule); + + String temp = changeRequest.name; + if (temp != null) { + builder.withName(temp); + } + temp = changeRequest.description; + if (temp != null) { + builder.withDescription(temp); + } + + List lights = changeRequest.lights; + if (changeRequest.storelightstate && lights != null) { + @SuppressWarnings("null") + @NonNullByDefault({}) + List actions = lights.stream().map(itemID -> itemRegistry.get(itemID)).filter(Objects::nonNull) + .map(item -> actionFromState(item.getUID(), item.getState())).collect(Collectors.toList()); + builder.withActions(actions); + } + Map lightStates = changeRequest.lightstates; + if (changeRequest.storelightstate && lightStates != null) { + List actions = new ArrayList<>(rule.getActions()); + for (Map.Entry entry : lightStates.entrySet()) { + // Remove existing action + actions.removeIf(action -> action.getId().equals(entry.getKey())); + // Assign new action + Command command = StateUtils.computeCommandByChangeRequest(entry.getValue()); + if (command == null) { + logger.warn("Failed to compute command for {}", body); + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Cannot compute command!"); + } + actions.add(actionFromState(entry.getKey(), command)); + } + builder.withActions(actions); + } + + try { + + ruleRegistry.add(builder.build()); + } catch (IllegalStateException e) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.ARGUMENTS_INVALID, e.getMessage()); + } + + return NetworkUtils.successList(cs.gson, Arrays.asList( // + new HueSuccessGeneric(changeRequest.name, "/scenes/" + id + "/name"), // + new HueSuccessGeneric(changeRequest.description, "/scenes/" + id + "/description"), // + new HueSuccessGeneric(changeRequest.lights != null ? String.join(",", changeRequest.lights) : null, + "/scenes/" + id + "/lights") // + )); + } + + @SuppressWarnings({ "null" }) + @POST + @Path("{username}/scenes") + @ApiOperation(value = "Create a new scene") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response postNewScene(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username, String body) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + HueSceneEntry newScheduleData = cs.gson.fromJson(body, HueSceneEntry.class); + if (newScheduleData == null || newScheduleData.name.isEmpty()) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.INVALID_JSON, + "Invalid request: No name or localtime!"); + } + + String uid = UUID.randomUUID().toString(); + RuleBuilder builder = RuleBuilder.create(uid).withName(newScheduleData.name).withTags("scene"); + + if (!newScheduleData.description.isEmpty()) { + builder.withDescription(newScheduleData.description); + } + + List lights = newScheduleData.lights; + if (lights != null) { + List actions = new ArrayList<>(); + for (String itemID : lights) { + Item item = itemRegistry.get(itemID); + if (item == null) { + continue; + } + actions.add(actionFromState(cs.mapItemUIDtoHueID(item), item.getState())); + } + builder.withActions(actions); + } + String groupid = newScheduleData.group; + if (groupid != null) { + Item groupItem = itemRegistry.get(groupid); + if (groupItem == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.ARGUMENTS_INVALID, "Group does not exist!"); + } + List actions = Collections + .singletonList(actionFromState(cs.mapItemUIDtoHueID(groupItem), groupItem.getState())); + builder.withActions(actions); + } + + try { + ruleRegistry.add(builder.build()); + } catch (IllegalStateException e) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.ARGUMENTS_INVALID, e.getMessage()); + } + + return NetworkUtils.singleSuccess(cs.gson, uid, "id"); + } + + @PUT + @Path("{username}/scenes/{id}/lightstates/{lightid}") + @ApiOperation(value = "Set scene attributes") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response modifySceneLightStateApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "scene id") String id, + @PathParam("lightid") @ApiParam(value = "light id") String lightid, String body) { + + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + final HueStateChange changeRequest = cs.gson.fromJson(body, HueStateChange.class); + + Rule rule = ruleRegistry.remove(id); + if (rule == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Scene does not exist!"); + } + + RuleBuilder builder = RuleBuilder.create(rule); + + List actions = new ArrayList<>(rule.getActions()); + // Remove existing action + actions.removeIf(action -> action.getId().equals(lightid)); + // Assign new action + Command command = StateUtils.computeCommandByChangeRequest(changeRequest); + if (command == null) { + logger.warn("Failed to compute command for {}", body); + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Cannot compute command!"); + } + + actions.add(actionFromState(lightid, command)); + + builder.withActions(actions); + + try { + ruleRegistry.add(builder.build()); + } catch (IllegalStateException e) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.ARGUMENTS_INVALID, e.getMessage()); + } + + return NetworkUtils.successList(cs.gson, Arrays.asList( // + new HueSuccessGeneric(changeRequest.on, "/scenes/" + id + "/lightstates/" + lightid + "/on"), // + new HueSuccessGeneric(changeRequest.hue, "/scenes/" + id + "/lightstates/" + lightid + "/hue"), // + new HueSuccessGeneric(changeRequest.sat, "/scenes/" + id + "/lightstates/" + lightid + "/sat"), // + new HueSuccessGeneric(changeRequest.bri, "/scenes/" + id + "/lightstates/" + lightid + "/bri"), // + new HueSuccessGeneric(changeRequest.transitiontime, + "/scenes/" + id + "/lightstates/" + lightid + "/transitiontime"))); + } + +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/Schedules.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/Schedules.java new file mode 100644 index 0000000000000..29e749e31ec9a --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/Schedules.java @@ -0,0 +1,344 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.common.registry.RegistryChangeListener; +import org.openhab.core.automation.Action; +import org.openhab.core.automation.Rule; +import org.openhab.core.automation.RuleManager; +import org.openhab.core.automation.RuleRegistry; +import org.openhab.core.automation.Trigger; +import org.openhab.core.automation.Visibility; +import org.openhab.core.automation.util.ModuleBuilder; +import org.openhab.core.automation.util.RuleBuilder; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.NetworkUtils; +import org.openhab.io.hueemulation.internal.RuleUtils; +import org.openhab.io.hueemulation.internal.dto.HueDataStore; +import org.openhab.io.hueemulation.internal.dto.HueScheduleEntry; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueChangeScheduleEntry; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueCommand; +import org.openhab.io.hueemulation.internal.dto.response.HueResponse; +import org.openhab.io.hueemulation.internal.dto.response.HueSuccessGeneric; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +/** + * Enables the schedule part of the Hue REST API. Uses automation rules with GenericCronTrigger, TimerTrigger and + * AbsoluteDateTimeTrigger depending on the schedule time pattern. + *

+ * If the scheduled task should remove itself after completion, a RemoveRuleAction is used in the rule. + *

+ * The actual command execution uses HttpAction. + * + * @author David Graeff - Initial contribution + */ +@Component(immediate = false, service = { Schedules.class }, property = "com.eclipsesource.jaxrs.publish=false") +@NonNullByDefault +@Path("") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class Schedules implements RegistryChangeListener { + public static final String SCHEDULE_TAG = "hueemulation_schedule"; + private final Logger logger = LoggerFactory.getLogger(Schedules.class); + + @Reference + protected @NonNullByDefault({}) ConfigStore cs; + @Reference + protected @NonNullByDefault({}) UserManagement userManagement; + + @Reference + protected @NonNullByDefault({}) RuleManager ruleManager; + @Reference + protected @NonNullByDefault({}) RuleRegistry ruleRegistry; + + /** + * Registers to the {@link RuleRegistry} and enumerates currently existing rules. + */ + @Activate + public void activate() { + ruleRegistry.removeRegistryChangeListener(this); + ruleRegistry.addRegistryChangeListener(this); + + for (Rule item : ruleRegistry.getAll()) { + added(item); + } + } + + @Deactivate + public void deactivate() { + ruleRegistry.removeRegistryChangeListener(this); + } + + /** + * Called by the registry when a rule got added (and when a rule got modified). + *

+ * Converts the rule into a {@link HueScheduleEntry} object and add that to the hue datastore. + */ + @Override + public void added(Rule rule) { + if (!rule.getTags().contains(SCHEDULE_TAG)) { + return; + } + HueScheduleEntry entry = new HueScheduleEntry(); + entry.name = rule.getName(); + entry.description = rule.getDescription(); + entry.autodelete = rule.getActions().stream().anyMatch(p -> p.getId().equals("autodelete")); + entry.status = ruleManager.isEnabled(rule.getUID()) ? "enabled" : "disabled"; + + String timeStringFromTrigger = RuleUtils.timeStringFromTrigger(rule.getTriggers()); + if (timeStringFromTrigger == null) { + logger.warn("Schedule from rule '{}' invalid!", rule.getName()); + return; + } + + entry.localtime = timeStringFromTrigger; + + for (Action a : rule.getActions()) { + if (!a.getTypeUID().equals("rules.HttpAction")) { + continue; + } + HueCommand command = RuleUtils.httpActionToHueCommand(cs.ds, a, rule.getName()); + if (command == null) { + continue; + } + entry.command = command; + } + + cs.ds.schedules.put(rule.getUID(), entry); + } + + @Override + public void removed(Rule element) { + cs.ds.schedules.remove(element.getUID()); + } + + @Override + public void updated(Rule oldElement, Rule element) { + removed(oldElement); + added(element); + } + + /** + * Creates a new rule that executes a http rule action, triggered by the scheduled time + * + * @param uid A rule unique id. + * @param builder A rule builder that will be used for creating the rule. It must have been created with the given + * uid. + * @param oldActions Old actions. Useful if `data` is only partially set and old actions should be preserved + * @param data The configuration for the http action and trigger time is in here + * @return A new rule with the given uid + * @throws IllegalStateException If a required parameter is not set or if a light / group that is referred to is not + * existing + */ + protected static Rule createRule(String uid, RuleBuilder builder, List oldActions, + List oldTriggers, HueChangeScheduleEntry data, HueDataStore ds) throws IllegalStateException { + HueCommand command = data.command; + Boolean autodelete = data.autodelete; + + String temp; + + temp = data.name; + if (temp != null) { + builder.withName(temp); + } else if (oldActions.isEmpty()) { // This is a new rule without a name yet + throw new IllegalStateException("Name not set!"); + } + + temp = data.description; + if (temp != null) { + builder.withDescription(temp); + } + + temp = data.localtime; + if (temp != null) { + builder.withTriggers(RuleUtils.createTriggerForTimeString(temp)); + } else if (oldTriggers.isEmpty()) { // This is a new rule without triggers yet + throw new IllegalStateException("localtime not set!"); + } + + List actions = new ArrayList(oldActions); + + if (command != null) { + RuleUtils.validateHueHttpAddress(ds, command.address); + actions.removeIf(a -> a.getId().equals("command")); // Remove old command action if any and add new one + actions.add(RuleUtils.createHttpAction(command, "command")); + } else if (oldActions.isEmpty()) { // This is a new rule without an action yet + throw new IllegalStateException("No command set!"); + } + + if (autodelete != null) { + // Remove action to remove rule after execution + actions = actions.stream().filter(e -> !e.getId().equals("autodelete")) + .collect(Collectors.toCollection(() -> new ArrayList<>())); + if (autodelete) { // Add action to remove this rule again after execution + final Configuration actionConfig = new Configuration(); + actionConfig.put("removeuid", uid); + actions.add(ModuleBuilder.createAction().withId("autodelete").withTypeUID("rules.RemoveRuleAction") + .withConfiguration(actionConfig).build()); + } + } + + builder.withActions(actions); + + return builder.withVisibility(Visibility.VISIBLE).withTags(SCHEDULE_TAG).build(); + } + + @GET + @Path("{username}/schedules") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation(value = "Return all schedules") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getSchedulesApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username) throws IOException { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.schedules)).build(); + } + + @GET + @Path("{username}/schedules/{id}") + @ApiOperation(value = "Return a schedule") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getScheduleApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "schedule id") String id) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.schedules.get(id))).build(); + } + + @DELETE + @Path("{username}/schedules/{id}") + @ApiOperation(value = "Deletes a schedule") + @ApiResponses(value = { @ApiResponse(code = 200, message = "The user got removed"), + @ApiResponse(code = 403, message = "Access denied") }) + public Response removeScheduleApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "Schedule to remove") String id) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + Rule rule = ruleRegistry.remove(id); + if (rule == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Schedule does not exist!"); + } + + return NetworkUtils.singleSuccess(cs.gson, "/schedules/" + id + " deleted."); + } + + @PUT + @Path("{username}/schedules/{id}") + @ApiOperation(value = "Set schedule attributes") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response modifyScheduleApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "schedule id") String id, String body) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + final HueChangeScheduleEntry changeRequest = cs.gson.fromJson(body, HueChangeScheduleEntry.class); + + Rule rule = ruleRegistry.remove(id); + if (rule == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Schedule does not exist!"); + } + + RuleBuilder builder = RuleBuilder.create(rule); + + try { + ruleRegistry.add( + createRule(rule.getUID(), builder, rule.getActions(), rule.getTriggers(), changeRequest, cs.ds)); + } catch (IllegalStateException e) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.ARGUMENTS_INVALID, e.getMessage()); + } + + return NetworkUtils.successList(cs.gson, Arrays.asList( // + new HueSuccessGeneric(changeRequest.name, "/schedules/" + id + "/name"), // + new HueSuccessGeneric(changeRequest.description, "/schedules/" + id + "/description"), // + new HueSuccessGeneric(changeRequest.localtime, "/schedules/" + id + "/localtime"), // + new HueSuccessGeneric(changeRequest.status, "/schedules/" + id + "/status"), // + new HueSuccessGeneric(changeRequest.autodelete, "/schedules/1/autodelete"), // + new HueSuccessGeneric(changeRequest.command, "/schedules/1/command") // + )); + } + + @SuppressWarnings({ "null" }) + @POST + @Path("{username}/schedules") + @ApiOperation(value = "Create a new schedule") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response postNewSchedule(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username, String body) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + HueScheduleEntry newScheduleData = cs.gson.fromJson(body, HueScheduleEntry.class); + if (newScheduleData == null || newScheduleData.name.isEmpty() || newScheduleData.localtime.isEmpty()) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.INVALID_JSON, + "Invalid request: No name or localtime!"); + } + + String uid = UUID.randomUUID().toString(); + RuleBuilder builder = RuleBuilder.create(uid); + + Rule rule; + try { + rule = createRule(uid, builder, Collections.emptyList(), Collections.emptyList(), newScheduleData, cs.ds); + } catch (IllegalStateException e) { // No stacktrace required, we just need the exception message + return NetworkUtils.singleError(cs.gson, uri, HueResponse.ARGUMENTS_INVALID, e.getMessage()); + } + + ruleRegistry.add(rule); + + return NetworkUtils.singleSuccess(cs.gson, uid, "id"); + } + +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/Sensors.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/Sensors.java new file mode 100644 index 0000000000000..7725b5e34cf21 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/Sensors.java @@ -0,0 +1,283 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.common.registry.RegistryChangeListener; +import org.eclipse.smarthome.core.items.GenericItem; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.ItemRegistry; +import org.eclipse.smarthome.core.library.CoreItemFactory; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.NetworkUtils; +import org.openhab.io.hueemulation.internal.dto.HueNewLights; +import org.openhab.io.hueemulation.internal.dto.HueSensorEntry; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueChangeRequest; +import org.openhab.io.hueemulation.internal.dto.response.HueResponse; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +/** + * Listens to the ItemRegistry and add all DecimalType, OnOffType, ContactType, DimmerType items + * as sensors. + * + * @author David Graeff - Initial contribution + */ +@Component(immediate = false, service = { Sensors.class }, property = "com.eclipsesource.jaxrs.publish=false") +@NonNullByDefault +@Path("") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class Sensors implements RegistryChangeListener { + private final Logger logger = LoggerFactory.getLogger(Sensors.class); + private static final Set ALLOWED_ITEM_TYPES = Stream.of(CoreItemFactory.COLOR, CoreItemFactory.DIMMER, + CoreItemFactory.ROLLERSHUTTER, CoreItemFactory.SWITCH, CoreItemFactory.CONTACT, CoreItemFactory.NUMBER) + .collect(Collectors.toSet()); + + @Reference + protected @NonNullByDefault({}) ConfigStore cs; + @Reference + protected @NonNullByDefault({}) UserManagement userManagement; + @Reference + protected @NonNullByDefault({}) ItemRegistry itemRegistry; + + /** + * Registers to the {@link ItemRegistry} and enumerates currently existing items. + * Call {@link #close(ItemRegistry)} when you are done with this object. + * + * Only call this after you have set the filter tags with {@link #setFilterTags(Set, Set, Set)}. + */ + @Activate + protected void activate() { + cs.ds.resetSensors(); + + itemRegistry.removeRegistryChangeListener(this); + itemRegistry.addRegistryChangeListener(this); + + for (Item item : itemRegistry.getItems()) { + added(item); + } + logger.debug("Added as sensor: {}", + cs.ds.sensors.values().stream().map(l -> l.name).collect(Collectors.joining(", "))); + } + + /** + * Unregisters from the {@link ItemRegistry}. + */ + @Deactivate + protected void deactivate() { + itemRegistry.removeRegistryChangeListener(this); + } + + @Override + public synchronized void added(Item newElement) { + if (!(newElement instanceof GenericItem)) { + return; + } + GenericItem element = (GenericItem) newElement; + + if (!ALLOWED_ITEM_TYPES.contains(element.getType())) { + return; + } + + String hueID = cs.mapItemUIDtoHueID(element); + + HueSensorEntry sensor = new HueSensorEntry(element); + cs.ds.sensors.put(hueID, sensor); + + } + + @Override + public synchronized void removed(Item element) { + String hueID = cs.mapItemUIDtoHueID(element); + logger.debug("Remove item {}", hueID); + cs.ds.sensors.remove(hueID); + } + + @Override + public synchronized void updated(Item oldElement, Item newElement) { + if (!(newElement instanceof GenericItem)) { + return; + } + GenericItem element = (GenericItem) newElement; + + String hueID = cs.mapItemUIDtoHueID(element); + + HueSensorEntry sensor = new HueSensorEntry(element); + cs.ds.sensors.put(hueID, sensor); + } + + @GET + @Path("{username}/sensors") + @ApiOperation(value = "Return all sensors") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getAllSensorsApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.sensors)).build(); + } + + @GET + @Path("{username}/sensors/new") + @ApiOperation(value = "Return new sensors since last scan. Returns an empty list for openHAB as we do not cache that information.") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getNewSensors(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(new HueNewLights())).build(); + } + + @POST + @Path("{username}/sensors") + @ApiOperation(value = "Starts a new scan for compatible items. This is usually not necessary, because we are observing the item registry.") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response postNewLights(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return NetworkUtils.singleSuccess(cs.gson, "Searching for new sensors", "/sensors"); + } + + @GET + @Path("{username}/sensors/{id}") + @ApiOperation(value = "Return a sensor") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getSensorApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "sensor id") String id) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.sensors.get(id))).build(); + } + + @SuppressWarnings({ "null", "unused" }) + @GET + @Path("{username}/sensors/{id}/config") + @ApiOperation(value = "Return a sensor config. Always empty") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getSensorConfigApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "sensor id") String id) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + HueSensorEntry sensor = cs.ds.sensors.get(id); + if (sensor == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Sensor does not exist"); + } + + return Response.ok(cs.gson.toJson(sensor.config)).build(); + } + + @SuppressWarnings({ "null", "unused" }) + @DELETE + @Path("{username}/sensors/{id}") + @ApiOperation(value = "Deletes the sensor that is represented by this id") + @ApiResponses(value = { @ApiResponse(code = 200, message = "The item got removed"), + @ApiResponse(code = 403, message = "Access denied") }) + public Response removeSensorAPI(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "id") String id) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + HueSensorEntry sensor = cs.ds.sensors.get(id); + if (sensor == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Sensor does not exist"); + } + + if (itemRegistry.remove(id) != null) { + return NetworkUtils.singleSuccess(cs.gson, "/sensors/" + id + " deleted."); + } else { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Sensor does not exist"); + } + } + + @SuppressWarnings({ "null", "unused" }) + @PUT + @Path("{username}/sensors/{id}") + @ApiOperation(value = "Rename a sensor") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response renameLightApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "light id") String id, String body) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + HueSensorEntry sensor = cs.ds.sensors.get(id); + if (sensor == null) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Sensor not existing"); + } + + final HueChangeRequest changeRequest = cs.gson.fromJson(body, HueChangeRequest.class); + + String name = changeRequest.name; + if (name == null || name.isEmpty()) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.INVALID_JSON, "Invalid request: No name set"); + } + + sensor.item.setLabel(name); + itemRegistry.update(sensor.item); + + return NetworkUtils.singleSuccess(cs.gson, name, "/sensors/" + id + "/name"); + } + + @PUT + @Path("{username}/sensors/{id}/state") + @ApiOperation(value = "Set sensor state") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response setSensorStateApi(@Context UriInfo uri, // + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "sensor id") String id, String body) { + if (!userManagement.authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + + return NetworkUtils.singleError(cs.gson, uri, HueResponse.SENSOR_NOT_CLIP_SENSOR, + "Invalid request: Not a clip sensor"); + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/StatusResource.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/StatusResource.java new file mode 100644 index 0000000000000..a8157d0ab918d --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/StatusResource.java @@ -0,0 +1,262 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import java.net.URI; +import java.util.stream.Collectors; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.jupnp.UpnpService; +import org.jupnp.controlpoint.ControlPoint; +import org.jupnp.model.meta.Device; +import org.jupnp.model.meta.DeviceDetails; +import org.jupnp.model.meta.LocalDevice; +import org.jupnp.model.meta.RemoteDevice; +import org.jupnp.registry.Registry; +import org.jupnp.registry.RegistryListener; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.upnp.UpnpServer; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is used by the status REST API for troubleshoot purposes. + *

+ * The UPNP announcement is tested, the /description.xml reachability is checked, + * and some statistics are gathered. + * + * @author David Graeff - Initial contribution + */ +@Component(immediate = false, service = { StatusResource.class }, property = "com.eclipsesource.jaxrs.publish=false") +@Path("") +@NonNullByDefault +public class StatusResource implements RegistryListener { + @Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY) + protected @NonNullByDefault({}) UpnpServer discovery; + @Reference + protected @NonNullByDefault({}) ConfigStore cs; + @Reference + protected @NonNullByDefault({}) UpnpService upnpService; + + private enum upnpStatus { + service_not_registered, + service_registered_but_no_UPnP_traffic_yet, + upnp_announcement_thread_not_running, + any_device_but_not_this_one_found, + success + } + + private upnpStatus selfTestUpnpFound = upnpStatus.service_not_registered; + + private final Logger logger = LoggerFactory.getLogger(StatusResource.class); + + /** + * This will register to the {@link UpnpService} registry to get notified of new UPNP devices and check all existing + * devices if they match our UPNP announcement. + */ + public void startUpnpSelfTest() { + Registry registry = upnpService.getRegistry(); + if (registry == null) { + logger.warn("upnp service registry is null!"); + return; + } + + selfTestUpnpFound = upnpStatus.service_registered_but_no_UPnP_traffic_yet; + + for (RemoteDevice device : registry.getRemoteDevices()) { + remoteDeviceAdded(registry, device); + } + for (LocalDevice device : registry.getLocalDevices()) { + localDeviceAdded(registry, device); + } + + registry.addListener(this); + + ControlPoint controlPoint = upnpService.getControlPoint(); + if (controlPoint != null) { + controlPoint.search(); + } + } + + @GET + @Path("status/link") + @Produces("text/plain") + public Response link(@Context UriInfo uri) { + cs.setLinkbutton(!cs.ds.config.linkbutton, true, uri.getQueryParameters().containsKey("v1")); + URI newuri = uri.getBaseUri().resolve("/api/status"); + return Response.seeOther(newuri).build(); + } + + private static String toYesNo(boolean b) { + return b ? "yes" : "no"; + } + + private static String TD(String s) { + return "" + s + ""; + } + + private static String TR(String s) { + return "" + s + ""; + } + + @GET + @Path("status") + @Produces("text/html") + public String getStatus() { + if (discovery == null) { // Optional service wiring + return "UPnP Server service not started!"; + } + + int httpPort = discovery.getDefaultport(); + + String format = "

Self test

" + // + "

To access any links you need be in pairing mode!

" + // + "

Pairing mode: %s (%s) Enable | Enable with bridge V1 emulation

" + + // + "%d published lights (see %s/api/testuser/lights)
" + // + "%d published sensors (see %s/api/testuser/sensors)
" + // + "

UPnP discovery test

" + // + "

%s

" + // + "%s
serial noname
" + // + "

Reachability test

" + // + "%s
URLResponds?Ours?
" + + // + "

Users

    %s
"; + + final String users = cs.ds.config.whitelist.entrySet().stream().map(user -> "
  • " + user.getKey() + " " + + user.getValue().name + " " + user.getValue().lastUseDate + "") + .collect(Collectors.joining("\n")); + + final String url = "http://" + cs.ds.config.ipaddress + ":" + String.valueOf(httpPort); + + final String reachable = discovery.selfTests().stream() + .map(entry -> TR(TD(entry.address) + TD(toYesNo(entry.reachable)) + TD(toYesNo(entry.isOurs)))) + .collect(Collectors.joining("\n")); + + Registry registry = upnpService.getRegistry(); + final String upnps; + if (registry != null) { + upnps = registry.getRemoteDevices().stream().map(device -> getDetails(device)) + .map(details -> TR(TD(details.getSerialNumber()) + TD(details.getFriendlyName()))) + .collect(Collectors.joining("\n")); + } else { + upnps = TR(TD("service not available") + TD("")); + } + + if (!discovery.upnpAnnouncementThreadRunning()) { + selfTestUpnpFound = upnpStatus.upnp_announcement_thread_not_running; + } + + return String.format(format, cs.ds.config.linkbutton ? "On" : "Off", + cs.getConfig().temporarilyEmulateV1bridge ? "V1" : "V2", url, url, // + cs.ds.lights.size(), url, url, cs.ds.sensors.size(), url, url, // + selfTestUpnpFound.name().replace('_', ' '), // + upnps, reachable, users); + } + + @NonNullByDefault({}) + @Override + public void remoteDeviceDiscoveryStarted(Registry registry, RemoteDevice device) { + } + + @NonNullByDefault({}) + @Override + public void remoteDeviceDiscoveryFailed(Registry registry, RemoteDevice device, Exception ex) { + } + + @NonNullByDefault({}) + @Override + public void remoteDeviceAdded(Registry registry, RemoteDevice device) { + if (selfTestUpnpFound == upnpStatus.success) { + return; + } + checkForDevice(getDetails(device)); + } + + private static DeviceDetails getDetails(@Nullable Device device) { + if (device != null) { + DeviceDetails details = device.getDetails(); + if (details != null) { + return details; + } + } + return new DeviceDetails(null, "", null, null, "", null, null, null, null, null); + } + + private void checkForDevice(DeviceDetails details) { + selfTestUpnpFound = upnpStatus.any_device_but_not_this_one_found; + try { + if (cs.ds.config.bridgeid.equals(details.getSerialNumber())) { + selfTestUpnpFound = upnpStatus.success; + } + } catch (Exception e) { // We really don't want the service to fail on any exception + logger.warn("upnp service: adding services failed: {}", details.getFriendlyName(), e); + } + } + + @NonNullByDefault({}) + @Override + public void remoteDeviceUpdated(Registry registry, RemoteDevice device) { + } + + @NonNullByDefault({}) + @Override + public void remoteDeviceRemoved(Registry registry, RemoteDevice device) { + if (selfTestUpnpFound != upnpStatus.success || discovery.upnpAnnouncementThreadRunning()) { + return; + } + DeviceDetails details = getDetails(device); + String serialNo = details.getSerialNumber(); + if (cs.ds.config.bridgeid.equals(serialNo)) { + selfTestUpnpFound = upnpStatus.any_device_but_not_this_one_found; + } + } + + @NonNullByDefault({}) + @Override + public void localDeviceAdded(Registry registry, LocalDevice device) { + if (selfTestUpnpFound == upnpStatus.success || discovery.upnpAnnouncementThreadRunning()) { + return; + } + checkForDevice(getDetails(device)); + } + + @NonNullByDefault({}) + @Override + public void localDeviceRemoved(Registry registry, LocalDevice device) { + + } + + @NonNullByDefault({}) + @Override + public void beforeShutdown(Registry registry) { + } + + @Override + public void afterShutdown() { + selfTestUpnpFound = upnpStatus.service_not_registered; + } + +} \ No newline at end of file diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/UserManagement.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/UserManagement.java new file mode 100644 index 0000000000000..c224c50d2de45 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/rest/UserManagement.java @@ -0,0 +1,223 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.common.registry.DefaultAbstractManagedProvider; +import org.eclipse.smarthome.core.storage.StorageService; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.NetworkUtils; +import org.openhab.io.hueemulation.internal.dto.HueUserAuth; +import org.openhab.io.hueemulation.internal.dto.HueUserAuthWithSecrets; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueCreateUser; +import org.openhab.io.hueemulation.internal.dto.response.HueResponse; +import org.openhab.io.hueemulation.internal.dto.response.HueSuccessResponseCreateUser; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.reflect.TypeToken; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; + +/** + * Manages users of this emulated HUE bridge. Stores users in the frameworks storage backend. + *

    + * This is an OSGi component. Usage: + * + *

    + * @Reference
    + * UserManagement userManagment;
    + * 
    + * + * @author David Graeff - Initial contribution + */ +@Component(immediate = false, service = { UserManagement.class }, property = "com.eclipsesource.jaxrs.publish=false") +@NonNullByDefault +@Path("") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class UserManagement extends DefaultAbstractManagedProvider { + private final Logger logger = LoggerFactory.getLogger(UserManagement.class); + + protected final ConfigStore cs; + + @Activate + public UserManagement(final @Reference StorageService storageService, final @Reference ConfigStore cs) { + super(storageService); + this.cs = cs; + + for (HueUserAuthWithSecrets userAuth : getAll()) { + cs.ds.config.whitelist.put(userAuth.getUID(), userAuth); + } + } + + /** + * Checks if the username exists in the whitelist + */ + @SuppressWarnings("null") + public boolean authorizeUser(String userName) { + HueUserAuth userAuth = cs.ds.config.whitelist.get(userName); + + if (cs.ds.config.linkbutton && cs.ds.config.createNewUserOnEveryEndpoint) { + addUser(userName, userName, "On-the-go-user"); + userAuth = cs.ds.config.whitelist.get(userName); + } + + if (userAuth != null) { + userAuth.lastUseDate = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); + update((HueUserAuthWithSecrets) userAuth); + } + + return userAuth != null; + } + + /** + * Adds a user to the whitelist and persist the user file + * + * @param apiKey The hue "username" which is actually an API key + * @param clientKey The UDP/DTLS client key + * @param The user visible name + */ + private void addUser(String apiKey, String clientKey, String label) { + if (cs.ds.config.whitelist.containsKey(apiKey)) { + return; + } + logger.debug("APIKey {} added", apiKey); + String l[] = label.split("#"); + HueUserAuthWithSecrets hueUserAuth = new HueUserAuthWithSecrets(l[0], l.length == 2 ? l[1] : "openhab", apiKey, + clientKey); + cs.ds.config.whitelist.put(apiKey, hueUserAuth); + add(hueUserAuth); + } + + @SuppressWarnings("null") + private synchronized void removeUser(String apiKey) { + HueUserAuth userAuth = cs.ds.config.whitelist.remove(apiKey); + if (userAuth != null) { + logger.debug("APIKey {} removed", apiKey); + } + remove(apiKey); + } + + @Override + protected String getStorageName() { + return "hueEmulationUsers"; + } + + @Override + protected String keyToString(String key) { + return key; + } + + @GET + public Response illegalGetUserAccessApi(@Context UriInfo uri) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.METHOD_NOT_ALLOWED, "Not Authorized"); + } + + @POST + @ApiOperation(value = "Create an API Key") + @ApiResponses(value = { @ApiResponse(code = 200, message = "API Key created"), + @ApiResponse(code = 403, message = "Link button not pressed") }) + public Response createNewUser(@Context UriInfo uri, String body) { + if (!cs.ds.config.linkbutton) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.LINK_BUTTON_NOT_PRESSED, + "link button not pressed"); + } + + final HueCreateUser userRequest; + userRequest = cs.gson.fromJson(body, HueCreateUser.class); + if (userRequest.devicetype.isEmpty()) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.INVALID_JSON, + "Invalid request: No devicetype set"); + } + + String apiKey = UUID.randomUUID().toString(); + String clientKey = UUID.randomUUID().toString(); + addUser(apiKey, clientKey, userRequest.devicetype); + HueSuccessResponseCreateUser h = new HueSuccessResponseCreateUser(apiKey, clientKey); + String result = cs.gson.toJson(Collections.singleton(new HueResponse(h)), new TypeToken>() { + }.getType()); + + return Response.ok(result).build(); + } + + @GET + @Path("{username}/config/whitelist/{userid}") + @ApiOperation(value = "Return a user") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getUserApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("userid") @ApiParam(value = "User ID") String userid) { + if (!authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.config.whitelist.get(userid))).build(); + } + + @GET + @Path("{username}/config/whitelist") + @ApiOperation(value = "Return all users") + @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") }) + public Response getAllUsersApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username) { + if (!authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + return Response.ok(cs.gson.toJson(cs.ds.config.whitelist)).build(); + } + + @DELETE + @Path("{username}/config/whitelist/{id}") + @ApiOperation(value = "Deletes a user") + @ApiResponses(value = { @ApiResponse(code = 200, message = "The user got removed"), + @ApiResponse(code = 403, message = "Access denied") }) + public Response removeUserApi(@Context UriInfo uri, + @PathParam("username") @ApiParam(value = "username") String username, + @PathParam("id") @ApiParam(value = "User to remove") String id) { + if (!authorizeUser(username)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); + } + if (!username.equals(id)) { + return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, + "You can only remove yourself not someone else!"); + } + + removeUser(username); + + return NetworkUtils.singleSuccess(cs.gson, "/config/whitelist/" + username + " deleted."); + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/HueEmulationConfigWithRuntime.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/HueEmulationConfigWithRuntime.java new file mode 100644 index 0000000000000..98c127f65aa91 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/HueEmulationConfigWithRuntime.java @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.upnp; + +import java.io.IOException; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.channels.Selector; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.io.hueemulation.internal.HueEmulationConfig; + +/** + * The upnp server runtime configuration. Based on a {@link HueEmulationConfig} and determined ip address and port. + * This extends {@link Thread}, because a runtime configuration is always valid for exactly one thread to be started + * once. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +class HueEmulationConfigWithRuntime extends Thread implements Runnable { + final @NonNullByDefault({}) HueEmulationConfig config; + final InetAddress address; + final String addressString; + final InetAddress multicastAddress; + int port; + + final CompletableFuture<@Nullable HueEmulationConfigWithRuntime> future = new CompletableFuture<>(); + final Consumer r; + + // IO + public @Nullable Selector asyncIOselector; + private boolean hasAlreadyBeenStarted = false; + + HueEmulationConfigWithRuntime(Consumer r, HueEmulationConfig config, + String addrString, InetAddress MULTI_ADDR_IPV4, InetAddress MULTI_ADDR_IPV6) throws UnknownHostException { + super("HueEmulation UPNP Server"); + this.r = r; + this.config = config; + + address = InetAddress.getByName(addrString); + if (address instanceof Inet6Address) { + addressString = "[" + address.getHostAddress().split("%")[0] + "]"; + multicastAddress = MULTI_ADDR_IPV4; + } else { + addressString = address.getHostAddress(); + multicastAddress = MULTI_ADDR_IPV6; + } + + port = config.discoveryHttpPort == 0 ? Integer.getInteger("org.osgi.service.http.port", 8080) + : config.discoveryHttpPort; + } + + HueEmulationConfigWithRuntime(Consumer r, @Nullable HueEmulationConfig config, + InetAddress MULTI_ADDR_IPV4, InetAddress MULTI_ADDR_IPV6) throws UnknownHostException { + super("HueEmulation UPNP Server"); + this.r = r; + this.config = config; + + address = InetAddress.getByName("localhost"); + if (address instanceof Inet6Address) { + addressString = "[" + address.getHostAddress().split("%")[0] + "]"; + multicastAddress = MULTI_ADDR_IPV4; + } else { + addressString = address.getHostAddress(); + multicastAddress = MULTI_ADDR_IPV6; + } + port = 8080; + } + + String getMulticastAddress() { + if (multicastAddress instanceof Inet6Address) { + return "[" + multicastAddress.getHostAddress().split("%")[0] + "]"; + } else { + return multicastAddress.getHostAddress(); + } + } + + public synchronized CompletableFuture<@Nullable HueEmulationConfigWithRuntime> startNow( + @Nullable HueEmulationConfigWithRuntime ignored) { + if (hasAlreadyBeenStarted) { + throw new IllegalStateException("Cannot restart thread"); + } + hasAlreadyBeenStarted = true; + super.start(); + return future; + } + + @Override + public void run() { + r.accept(this); + } + + public void dispose() { + Selector selector = asyncIOselector; + if (selector != null) { + try { + selector.close(); + } catch (IOException ignored) { + } + + try { + join(); + } catch (InterruptedException ignore) { + Thread.currentThread().interrupt(); + } + asyncIOselector = null; + } + } +} \ No newline at end of file diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/SelfTestReport.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/SelfTestReport.java new file mode 100644 index 0000000000000..b0cb9feab2a36 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/SelfTestReport.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.upnp; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * A self test report includes the tested address, a success flag and + * if the service at the given address is actually ours. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class SelfTestReport { + final public String address; + final public boolean reachable; + final public boolean isOurs; + + SelfTestReport(String address, boolean testReport, boolean isOurs) { + this.address = address; + this.reachable = testReport; + this.isOurs = isOurs; + } + + /** + * A failed address is not reachable and not ours + */ + public static SelfTestReport failed(String address) { + return new SelfTestReport(address, false, false); + } +} \ No newline at end of file diff --git a/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/UpnpServer.java b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/UpnpServer.java new file mode 100644 index 0000000000000..0c277a0940a6a --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/java/org/openhab/io/hueemulation/internal/upnp/UpnpServer.java @@ -0,0 +1,527 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.upnp; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.SocketAddress; +import java.net.StandardSocketOptions; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedSelectorException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.ForkJoinPool; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.core.Response; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventAdmin; +import org.osgi.service.event.EventConstants; +import org.osgi.service.event.EventHandler; +import org.osgi.service.http.HttpService; +import org.osgi.service.http.NamespaceException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Advertises a Hue compatible bridge via UPNP and provides the announced /description.xml http endpoint. + * + * @author Dan Cunningham - Initial contribution + * @author David Graeff - Rewritten + */ +@SuppressWarnings("serial") +@NonNullByDefault +@Component(immediate = false, // Don't start the upnp server on its own. Must be pulled in by HueEmulationService. + configurationPolicy = ConfigurationPolicy.IGNORE, property = { + EventConstants.EVENT_TOPIC + "=" + ConfigStore.EVENT_ADDRESS_CHANGED, + "com.eclipsesource.jaxrs.publish=false" }, // + service = { UpnpServer.class, EventHandler.class }) +public class UpnpServer extends HttpServlet implements Consumer, EventHandler { + /** + * Used by async IO. This is our context object class. + */ + static class ClientRecord { + public @Nullable SocketAddress clientAddress; + public ByteBuffer buffer = ByteBuffer.allocate(1000); + } + + public static final String DISCOVERY_FILE = "/description.xml"; + + // jUPNP shares port 1900, but since this is multicast, we can also bind to it + public static final int UPNP_PORT = 1900; + /** + * Send a keep alive every 2 minutes + */ + private static final int CACHE_MSECS = 120 * 1000; + + private final Logger logger = LoggerFactory.getLogger(UpnpServer.class); + + public final InetAddress MULTI_ADDR_IPV4; + public final InetAddress MULTI_ADDR_IPV6; + private String[] stVersions = { "", "", "" }; + private String notifyMsg = ""; + + //// objects, set within activate() + protected @NonNullByDefault({}) String xmlDoc; + protected @NonNullByDefault({}) String xmlDocWithAddress; + private @NonNullByDefault({}) String baseurl; + + //// services + @Reference + protected @NonNullByDefault({}) ConfigStore cs; + @Reference + protected @NonNullByDefault({}) HttpService httpService; + + public boolean overwriteReadyToFalse = false; + + private HueEmulationConfigWithRuntime config; + protected CompletableFuture<@Nullable HueEmulationConfigWithRuntime> configChangeFuture = CompletableFuture + .completedFuture(config); + + private List selfTests = new ArrayList<>(); + private final Executor executor; + + /** + * Creates a server instance. + * UPnP IPv4/v6 multicast addresses are determined. + */ + public UpnpServer() { + this(ForkJoinPool.commonPool()); + } + + public UpnpServer(Executor executor) { + try { + MULTI_ADDR_IPV4 = InetAddress.getByName("239.255.255.250"); + MULTI_ADDR_IPV6 = InetAddress.getByName("ff02::c"); + config = new HueEmulationConfigWithRuntime(this, null, MULTI_ADDR_IPV4, MULTI_ADDR_IPV6); + } catch (UnknownHostException e) { + throw new IllegalStateException(e); + } + + this.executor = executor; + } + + /** + * this object is also a servlet for providing /description.xml, the UPnP discovery result + */ + @NonNullByDefault({}) + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + if (xmlDocWithAddress == null || xmlDocWithAddress.isEmpty()) { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } + try (PrintWriter out = resp.getWriter()) { + resp.setContentType("application/xml"); + out.write(xmlDocWithAddress); + } + } + + /** + * Server to send UDP packets onto the network when requested by a Hue API compatible device. + * + * @param relativePath The URI path where the discovery xml document can be retrieved + * @param config The hue datastore. Contains the bridgeid and uuid. + * @param address IP to advertise for UPNP + * @throws IOException + * @throws NamespaceException + * @throws ServletException + */ + @Activate + protected void activate() { + InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("discovery.xml"); + if (resourceAsStream == null) { + logger.warn("Could not start Hue Emulation service: discovery.xml not found"); + return; + } + try (InputStreamReader r = new InputStreamReader(resourceAsStream, StandardCharsets.UTF_8); + BufferedReader br = new BufferedReader(r)) { + xmlDoc = br.lines().collect(Collectors.joining("\n")); + } catch (IOException e) { + logger.warn("Could not start Hue Emulation UPNP server: {}", e.getMessage(), e); + return; + } + + try { + httpService.unregister(DISCOVERY_FILE); + } catch (IllegalArgumentException ignore) { + } + + try { + httpService.registerServlet(DISCOVERY_FILE, this, null, null); + } catch (ServletException | NamespaceException e) { + logger.warn("Could not start Hue Emulation UPNP server: {}", e.getMessage(), e); + } + + if (cs.isReady() && !overwriteReadyToFalse) { + handleEvent(null); + } + } + + private void useAddressPort(HueEmulationConfigWithRuntime r) { + final String urlBase = "http://" + r.addressString + ":" + String.valueOf(r.port); + this.baseurl = urlBase + DISCOVERY_FILE; + + final String[] stVersions = { "upnp:rootdevice", "urn:schemas-upnp-org:device:basic:1", + "uuid:" + config.config.uuid }; + for (int i = 0; i < stVersions.length; ++i) { + this.stVersions[i] = String.format( + "HTTP/1.1 200 OK\r\n" + "HOST: %s:%d\r\n" + "EXT:\r\n" + "CACHE-CONTROL: max-age=%d\r\n" + + "LOCATION: %s\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/%s\r\n" + + "hue-bridgeid: %s\r\n" + "ST: %s\r\n" + "USN: uuid:%s\r\n\r\n", + r.getMulticastAddress(), UPNP_PORT, CACHE_MSECS / 1000, baseurl, // host:port, + // cache,location + cs.ds.config.apiversion, cs.ds.config.bridgeid, // version, bridgeid + stVersions[i], config.config.uuid); + } + + this.notifyMsg = String.format( + "NOTIFY * HTTP/1.1\r\n" + "HOST: %s:%d\r\n" + "CACHE-CONTROL: max-age=%d\r\n" + "LOCATION: %s\r\n" + + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/%s\r\nNTS: ssdp:alive\r\nNT: upnp:rootdevice\r\n" + + "USN: uuid:%s::upnp:rootdevice\r\n" + "hue-bridgeid: %s\r\n\r\n", + r.getMulticastAddress(), UPNP_PORT, CACHE_MSECS / 1000, baseurl, // host:port, cache,location + cs.ds.config.apiversion, config.config.uuid, cs.ds.config.bridgeid);// version, uuid, bridgeid + + xmlDocWithAddress = String.format(xmlDoc, urlBase, r.addressString, cs.ds.config.bridgeid, cs.ds.config.uuid, + cs.ds.config.devicename); + + } + + protected @Nullable HueEmulationConfigWithRuntime performAddressTest( + @Nullable HueEmulationConfigWithRuntime config) { + if (config == null) { + return null; // Config hasn't changed + } + + selfTests.clear(); + + ClientConfig configuration = new ClientConfig(); + configuration = configuration.property(ClientProperties.CONNECT_TIMEOUT, 1000); + configuration = configuration.property(ClientProperties.READ_TIMEOUT, 1000); + Client client = ClientBuilder.newClient(configuration); + Response response; + String url = ""; + try { + boolean selfTestOnPort80; + try { + response = client.target("http://" + cs.ds.config.ipaddress + ":80" + DISCOVERY_FILE).request().get(); + selfTestOnPort80 = response.getStatus() == 200 + && response.readEntity(String.class).contains(cs.ds.config.bridgeid); + } catch (ProcessingException ignored) { + selfTestOnPort80 = false; + } + + // Prefer port 80 if possible and if not overwritten by discoveryHttpPort + config.port = config.config.discoveryHttpPort == 0 && selfTestOnPort80 ? 80 : config.port; + + // Test on all assigned interface addresses on org.osgi.service.http.port as well as port 80 + // Other services might run on port 80, so we search for our bridge ID in the returned document. + for (InetAddress address : cs.getDiscoveryIps()) { + String ip = address.getHostAddress(); + if (address instanceof Inet6Address) { + ip = "[" + ip.split("%")[0] + "]"; + } + try { + url = "http://" + ip + ":" + String.valueOf(config.port) + DISCOVERY_FILE; + response = client.target(url).request().get(); + boolean isOurs = response.readEntity(String.class).contains(cs.ds.config.bridgeid); + selfTests.add(new SelfTestReport(url, response.getStatus() == 200, isOurs)); + } catch (ProcessingException e) { + logger.debug("Self test fail on {}: {}", url, e.getMessage()); + selfTests.add(SelfTestReport.failed(url)); + } + try { + url = "http://" + ip + DISCOVERY_FILE; // Port 80 + response = client.target(url).request().get(); + boolean isOurs = response.readEntity(String.class).contains(cs.ds.config.bridgeid); + selfTests.add(new SelfTestReport(url, response.getStatus() == 200, isOurs)); + } catch (ProcessingException e) { + logger.debug("Self test fail on {}: {}", url, e.getMessage()); + selfTests.add(SelfTestReport.failed(url)); + } + } + } finally { + client.close(); + } + return config; + } + + /** + * Create and return new runtime configuration based on {@link ConfigStore}s current configuration. + * Return null if the configuration has not changed compared to {@link #config}. + * + * @throws IllegalStateException If the {@link ConfigStore}s IP is invalid this exception is thrown. + */ + protected @Nullable HueEmulationConfigWithRuntime createConfiguration( + @Nullable HueEmulationConfigWithRuntime ignoredParameter) throws IllegalStateException { + HueEmulationConfigWithRuntime r; + try { + r = new HueEmulationConfigWithRuntime(this, cs.getConfig(), cs.ds.config.ipaddress, MULTI_ADDR_IPV4, + MULTI_ADDR_IPV6); + } catch (UnknownHostException e) { + logger.warn("The picked default IP address is not valid: ", e.getMessage()); + throw new IllegalStateException(e); + } + return r; + } + + /** + * Apply the given runtime configuration by stopping the current udp thread, shutting down the socket and restarting + * the thread. + */ + protected @Nullable HueEmulationConfigWithRuntime applyConfiguration( + @Nullable HueEmulationConfigWithRuntime newRuntimeConfig) { + if (newRuntimeConfig == null) { + return null;// Config hasn't changed + } + config.dispose(); + this.config = newRuntimeConfig; + useAddressPort(config); + return config; + } + + /** + * We have a hard dependency on the {@link ConfigStore} and that it has initialized the Hue DataStore config + * completely. That initialization happens asynchronously and therefore we cannot rely on OSGi activate/modified + * state changes. Instead the {@link EventAdmin} is used and we listen for the + * {@link ConfigStore#EVENT_ADDRESS_CHANGED} event that is fired as soon as the config is ready. + *

    + * To be really sure that we are called here, this is also issued by the main service after it has received the + * configuration ready event and depending on service start order we are also called by our own activate() method + * when the configuration is already ready at that time. + *

    + * Therefore this method is "synchronized" and chains a completeable future for each call to re-evaluate the config + * after the former future has finished. + */ + @Override + public synchronized void handleEvent(@Nullable Event event) { + CompletableFuture<@Nullable HueEmulationConfigWithRuntime> root; + // There is either already a running future, then chain a new one + if (!configChangeFuture.isDone()) { + root = configChangeFuture; + } else { // Or there is none -> create a new one + root = CompletableFuture.completedFuture(null); + } + configChangeFuture = root.thenApply(this::createConfiguration) + .thenApplyAsync(this::performAddressTest, executor).thenApply(this::applyConfiguration) + .thenCompose(config::startNow) + .whenComplete((HueEmulationConfigWithRuntime config, @Nullable Throwable e) -> { + if (e != null) { + logger.warn("Upnp server: Address test failed", e); + } + }); + } + + /** + * Stops the upnp server from running + * + * @throws InterruptedException + */ + @Deactivate + public void deactivate() { + config.dispose(); + try { + httpService.unregister(DISCOVERY_FILE); + } catch (IllegalArgumentException ignore) { + } + } + + private void handleRead(SelectionKey key, Set addresses) throws IOException { + logger.trace("upnp thread handle received message"); + DatagramChannel channel = (DatagramChannel) key.channel(); + ClientRecord clntRec = (ClientRecord) key.attachment(); + clntRec.buffer.clear(); // Prepare buffer for receiving + clntRec.clientAddress = channel.receive(clntRec.buffer); + InetSocketAddress recAddress = (InetSocketAddress) clntRec.clientAddress; + if (recAddress == null) { // Did we receive something? + return; + } + String data = new String(clntRec.buffer.array(), StandardCharsets.UTF_8); + if (!data.startsWith("M-SEARCH")) { + return; + } + + try (DatagramSocket sendSocket = new DatagramSocket()) { + sendUPNPDatagrams(sendSocket, recAddress.getAddress(), recAddress.getPort()); + } + } + + private void sendUPNPDatagrams(DatagramSocket sendSocket, InetAddress address, int port) { + logger.trace("upnp thread send announcement"); + for (String msg : stVersions) { + DatagramPacket response = new DatagramPacket(msg.getBytes(), msg.length(), address, port); + try { + logger.trace("Sending to {}:{}", address.getHostAddress(), port); + sendSocket.send(response); + } catch (IOException e) { + logger.warn("Could not send UPNP response: {}", e.getMessage()); + } + } + } + + private void sendUPNPNotify(DatagramSocket sendSocket, InetAddress address, int port) { + DatagramPacket response = new DatagramPacket(notifyMsg.getBytes(), notifyMsg.length(), address, port); + try { + logger.trace("Sending to {}:{}", address.getHostAddress(), port); + sendSocket.send(response); + } catch (IOException e) { + logger.warn("Could not send UPNP response: {}", e.getMessage()); + } + } + + @Override + public void accept(HueEmulationConfigWithRuntime threadContext) { + logger.info("Hue Emulation UPNP server started on {}:{}", threadContext.addressString, threadContext.port); + boolean hasIPv4 = false; + boolean hasIPv6 = false; + + try (DatagramChannel channel = DatagramChannel.open(); Selector selector = Selector.open()) { + + threadContext.asyncIOselector = selector; + + channel.setOption(StandardSocketOptions.SO_REUSEADDR, true) + .setOption(StandardSocketOptions.IP_MULTICAST_LOOP, true).bind(new InetSocketAddress(UPNP_PORT)); + for (InetAddress address : cs.getDiscoveryIps()) { + NetworkInterface networkInterface = NetworkInterface.getByInetAddress(address); + if (networkInterface == null) { + continue; + } + if (address instanceof Inet4Address) { + channel.join(MULTI_ADDR_IPV4, networkInterface); + hasIPv4 = true; + } else { + channel.join(MULTI_ADDR_IPV6, networkInterface); + hasIPv6 = true; + } + } + if (!hasIPv4 && !hasIPv6) { + logger.warn("Could not join upnp multicast network!"); + threadContext.future + .completeExceptionally(new IllegalStateException("Could not join upnp multicast network!")); + return; + } + + channel.configureBlocking(false); + + channel.register(selector, SelectionKey.OP_READ, new ClientRecord()); + + if (hasIPv4) { + try (DatagramSocket sendSocket = new DatagramSocket(new InetSocketAddress(config.address, 0))) { + sendUPNPDatagrams(sendSocket, MULTI_ADDR_IPV4, UPNP_PORT); + } + } + if (hasIPv6) { + try (DatagramSocket sendSocket = new DatagramSocket()) { + sendUPNPDatagrams(sendSocket, MULTI_ADDR_IPV6, UPNP_PORT); + } + } + + threadContext.future.complete(threadContext); + Instant time = Instant.now(); + + while (selector.isOpen()) { // Run forever, receiving and echoing datagrams + // Wait for task or until timeout expires + selector.select(CACHE_MSECS); + Iterator keyIter = selector.selectedKeys().iterator(); + while (keyIter.hasNext()) { + SelectionKey key = keyIter.next(); + if (key.isReadable()) { + handleRead(key, cs.getDiscoveryIps()); + } + keyIter.remove(); + } + + if (time.plusMillis(CACHE_MSECS - 200).isBefore(Instant.now())) { + logger.trace("upnp thread send periodic announcement"); + time = Instant.now(); + if (hasIPv4) { + try (DatagramSocket sendSocket = new DatagramSocket(new InetSocketAddress(config.address, 0))) { + sendUPNPNotify(sendSocket, MULTI_ADDR_IPV4, UPNP_PORT); + } + } + if (hasIPv6) { + try (DatagramSocket sendSocket = new DatagramSocket()) { + sendUPNPNotify(sendSocket, MULTI_ADDR_IPV6, UPNP_PORT); + } + } + } + } + } catch (ClosedSelectorException ignored) { + } catch (IOException e) { + logger.warn("Socket error with UPNP server", e); + threadContext.future.completeExceptionally(e); + } finally { + threadContext.asyncIOselector = null; + } + } + + /** + * The upnp server performs some self-tests + * + * @return + */ + public List selfTests() { + return selfTests; + } + + public String getBaseURL() { + return baseurl; + } + + public int getDefaultport() { + return config.port; + } + + public boolean upnpAnnouncementThreadRunning() { + return config.asyncIOselector != null; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/AbsoluteDateTimeTrigger.json b/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/AbsoluteDateTimeTrigger.json new file mode 100644 index 0000000000000..08c2e685092d5 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/AbsoluteDateTimeTrigger.json @@ -0,0 +1,36 @@ +{ + "triggers":[ + { + "uid":"timer.AbsoluteDateTimeTrigger", + "label":"Absolute date/time Trigger", + "description":"This triggers a rule based on a fixed date/time", + "visibility":"VISIBLE", + "configDescriptions":[ + { + "name":"date", + "type":"TEXT", + "context":"date", + "label":"Date", + "description":"A date with the pattern yyyy-mm-dd", + "required":false + }, + { + "name":"time", + "type":"TEXT", + "context":"time", + "label":"Time", + "description":"A time with the pattern hh:mm:ss", + "required":false + }, + { + "name":"randomizeTime", + "type":"TEXT", + "context":"time", + "label":"Randomized Time bound", + "description":"An upper time bound with the pattern hh:mm:ss. If this is given, the trigger triggers on a random time between Time and Randomized Time", + "required":false + } + ] + } + ] +} \ No newline at end of file diff --git a/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/HttpAction.json b/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/HttpAction.json new file mode 100644 index 0000000000000..f02355bd6249e --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/HttpAction.json @@ -0,0 +1,94 @@ +{ + "triggers": [ + { + "uid": "rules.HttpAction", + "label": "Remove rule", + "description": "Removes a rule permanently", + "visibility": "VISIBLE", + "configDescriptions": [ + { + "name": "method", + "type": "TEXT", + "label": "Http method", + "description": "The http method to be used", + "required": true, + "default": "GET", + "limitToOptions": true, + "options": [ + { + "label": "GET", + "value": "GET" + }, + { + "label": "POST", + "value": "POST" + }, + { + "label": "PUT", + "value": "PUT" + }, + { + "label": "HEAD", + "value": "HEAD" + }, + { + "label": "DELETE", + "value": "DELETE" + } + ] + }, + { + "name": "url", + "type": "TEXT", + "context": "url", + "label": "URL", + "description": "The url that the http request should be done on. Can be a relative one, starting with '/'. For example '/api/foo/bar'", + "required": true + }, + { + "name": "body", + "type": "TEXT", + "context": "body", + "label": "Data", + "description": "For post and put request you can send data with the request", + "required": false + }, + { + "name": "mimetype", + "type": "TEXT", + "context": "body", + "label": "Mimetype", + "description": "For post and put request you can send data with the request. Set the mimetype of that date here.", + "required": false, + "limitToOptions": false, + "options": [ + { + "label": "Text", + "value": "text/plain" + }, + { + "label": "Json", + "value": "application/json" + }, + { + "label": "XML", + "value": "application/xml" + }, + { + "label": "Binary", + "value": "application/octet-stream" + } + ] + }, + { + "name": "timeout", + "type": "INTEGER", + "label": "Timeout in sec", + "description": "The timeout of this request in seconds.", + "required": false, + "default": "5" + } + ] + } + ] +} \ No newline at end of file diff --git a/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/HueRuleCondition.json b/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/HueRuleCondition.json new file mode 100644 index 0000000000000..1853f16937e5e --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/HueRuleCondition.json @@ -0,0 +1,72 @@ +{ + "triggers": [ + { + "uid": "hue.ruleCondition", + "label": "A condition especially for Hue rules", + "description": "Depending on the operator and item state this condition will be satisfied or not", + "visibility": "HIDDEN", + "configDescriptions": [ + { + "name": "operator", + "type": "TEXT", + "label": "Operator", + "description": "The operator that compares either the hue bridge time or a light/sensor state with a given static value", + "required": true, + "limitToOptions": true, + "options": [ + { + "label": "Equal", + "value": "eq" + }, + { + "label": "Greater than", + "value": "gt" + }, + { + "label": "Lower than", + "value": "lt" + }, + { + "label": "On item state change", + "value": "dx" + }, + { + "label": "On item state change, delayed", + "value": "ddx" + }, + { + "label": "Item has been updated but did not change", + "value": "stable" + }, + { + "label": "Item has been updated and has changed", + "value": "not_stable" + }, + { + "label": "The current time is in range of the given time", + "value": "in" + }, + { + "label": "The current time is NOT in range of the given time", + "value": "not_in" + } + ] + }, + { + "name": "address", + "type": "TEXT", + "label": "Hue link address", + "description": "A hue local link address like /sensors/2/state/buttonevent or /config/localtime", + "required": true + }, + { + "name": "value", + "type": "TEXT", + "label": "An optional compare value", + "description": "Only valid for eq,lg,lt operators and a number or boolean item type or in,not_in. For the later it must be in the format of W127/T12:12:12/T14:00:00 describing the valid weekdays (binary encoded), the start time and stop time.", + "required": false + } + ] + } + ] +} \ No newline at end of file diff --git a/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/RemoveRuleAction.json b/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/RemoveRuleAction.json new file mode 100644 index 0000000000000..4e956f1eb0b7b --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/RemoveRuleAction.json @@ -0,0 +1,20 @@ +{ + "triggers":[ + { + "uid":"rules.RemoveRuleAction", + "label":"Remove rule", + "description":"Removes a rule permanently", + "visibility":"VISIBLE", + "configDescriptions":[ + { + "name":"removeuid", + "type":"TEXT", + "context":"rule", + "label":"Rule", + "description":"The rule that should be removed", + "required":true + } + ] + } + ] +} \ No newline at end of file diff --git a/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/TimerTrigger.json b/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/TimerTrigger.json new file mode 100644 index 0000000000000..7772d5a002f23 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/automation/moduletypes/TimerTrigger.json @@ -0,0 +1,36 @@ +{ + "triggers":[ + { + "uid":"timer.TimerTrigger", + "label":"Timer Trigger", + "description":"This triggers a rule based on a timer", + "visibility":"VISIBLE", + "configDescriptions":[ + { + "name":"time", + "type":"TEXT", + "context":"time", + "label":"Duration", + "description":"A duration before this timer expires with the pattern hh:mm:ss. The shortest duration is therefore 1 second, the longest is 99 hours.", + "required":true + }, + { + "name":"randomizeTime", + "type":"TEXT", + "context":"time", + "label":"Duration Upper Bound", + "description":"An optional upper bound duration before this timer expires with the pattern hh:mm:ss. A random duration between Duration and Duration Upper Bound will be chosen.", + "required":false + }, + { + "name":"repeat", + "type":"INTEGER", + "label":"Repeat", + "description":"You can make this timer a recurring timer by setting a value above 1. The default is 1. If you set a value below 0 like -1 this timer will be repeated indefinitely", + "default":"1", + "required":false + } + ] + } + ] +} \ No newline at end of file diff --git a/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/config/config.xml b/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/config/config.xml new file mode 100644 index 0000000000000..8474de87964fe --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/main/resources/ESH-INF/config/config.xml @@ -0,0 +1,69 @@ + + + + + + Pairing must be enabled to connect a new device. Pairing is automatically disabled after the configured pairing time (usually 60 seconds). + false + + + + Pairing is automatically disabled after the given time in seconds. + 60 + true + + + + Set this option to create new users on the fly during the next pairing mode period. This helps with Amazon Echo device discovery. This option is automatically switched off after the timeout. + false + + + + Some Amazon Echos only support V1 bridges (round hardware bridge). This option is only active during discovery and automatically switched off after the timeout. + false + + + + There is no obvious reason to not emulate the newer bridge all the time, but here is the option if you want the old (round Hue bridge) to be emulated. + false + + + + The HUE emulation can either publish Switch items if this is set to an empty string or filter items by tags. Use commas to separate multiple entries. + Switchable + + + + The HUE emulation can either publish all Color items if this is set to an empty string or filter items by tags. Use commas to separate multiple entries. + ColorLighting + + + + The HUE emulation can either publish all Dimmer items if this is set to an empty string or filter items by tags. Use commas to separate multiple entries. + Lighting + + + + All items that are tagged with the given tags are ignore by the Hue Emulation Service. Use commas to separate multiple entries. + internal + + + + If your host has multiple IP addresses you may specify the IP you would like to advertise in the UPNP discovery process. You may safely leave this empty on most systems. + + + + Some Hue applications require a different port (80) then what openHAB runs on by default (8080). This option will only advertise a different port then what we are listening on. Useful if you have an iptables rule redirect traffic from this port to the openHAB port. + true + + + + Each Hue bridge has a universal unique id (UUID) assigned. This is random generated if no value has been assigned. Note on Amazon Alexa Echo devices: It might help to change the UUID after you have changed item ids. The Echos will recognize this service as a new bridge. + true + + + diff --git a/addons/io/org.openhab.io.hueemulation/src/main/resources/discovery.xml b/bundles/org.openhab.io.hueemulation/src/main/resources/discovery.xml similarity index 88% rename from addons/io/org.openhab.io.hueemulation/src/main/resources/discovery.xml rename to bundles/org.openhab.io.hueemulation/src/main/resources/discovery.xml index e0ff4a09fbd40..d562eae1356c3 100644 --- a/addons/io/org.openhab.io.hueemulation/src/main/resources/discovery.xml +++ b/bundles/org.openhab.io.hueemulation/src/main/resources/discovery.xml @@ -4,18 +4,19 @@ 1 0 - http://%s:%d/ + %s/ urn:schemas-upnp-org:device:Basic:1 openHAB Hue Emulation (%s) Royal Philips Electronics http://www.openhab.org - Philips hue compatible Personal Wireless Lighting + Philips hue Personal Wireless Lighting Philips hue bridge 2015 BSB002 http://www.meethue.com %s uuid:%s + %s index.html diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/automation/RuleConditionHandlerTests.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/automation/RuleConditionHandlerTests.java new file mode 100644 index 0000000000000..d972fbfb128ca --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/automation/RuleConditionHandlerTests.java @@ -0,0 +1,216 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.automation; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.Map; +import java.util.Random; +import java.util.TreeMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.items.GroupItem; +import org.eclipse.smarthome.core.library.items.ContactItem; +import org.eclipse.smarthome.core.library.items.NumberItem; +import org.eclipse.smarthome.core.library.items.SwitchItem; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.OpenClosedType; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.openhab.core.automation.Condition; +import org.openhab.core.automation.util.ConditionBuilder; +import org.openhab.io.hueemulation.internal.DeviceType; +import org.openhab.io.hueemulation.internal.RuleUtils; +import org.openhab.io.hueemulation.internal.dto.HueDataStore; +import org.openhab.io.hueemulation.internal.dto.HueGroupEntry; +import org.openhab.io.hueemulation.internal.dto.HueLightEntry; +import org.openhab.io.hueemulation.internal.dto.HueSensorEntry; + +/** + * Test the {@link HueRuleConditionHandler}. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class RuleConditionHandlerTests { + protected @NonNullByDefault({}) HueDataStore ds; + + private static class HueRuleConditionHandlerEx extends HueRuleConditionHandler { + LocalDateTime dt = LocalDateTime.of(2019, 04, 12, 12, 12, 12); + + public HueRuleConditionHandlerEx(Condition module, HueDataStore ds) { + super(module, ds); + + } + + @Override + protected LocalDateTime getNow() { + return dt; + } + } + + @Before + public void setUp() throws IOException { + ds = new HueDataStore(); + + ds.lights.put("1", new HueLightEntry(new SwitchItem("switch"), "switch", DeviceType.SwitchType)); + + ds.sensors.put("2", new HueSensorEntry(new ContactItem("contact"))); + + ds.groups.put("10", + new HueGroupEntry("name", new GroupItem("white", new NumberItem("number")), DeviceType.SwitchType)); + } + + @After + public void tearDown() { + RuleUtils.random = new Random(); + } + + @Test(expected = IllegalStateException.class) + public void itemNotExisting() { + Configuration configuration = new Configuration(); + configuration.put("address", "/groups/9/action"); + configuration.put("operator", "dx"); + configuration.put("value", ""); + Condition c = ConditionBuilder.create().withId("a").withTypeUID(HueRuleConditionHandler.MODULE_TYPE_ID) + .withConfiguration(configuration).build(); + new HueRuleConditionHandler(c, ds); + } + + @Test + public void itemAccept() { + Condition c; + Configuration configuration = new Configuration(); + configuration.put("operator", "dx"); + configuration.put("value", ""); + + configuration.put("address", "/groups/10/action"); + c = ConditionBuilder.create().withId("a").withTypeUID(HueRuleConditionHandler.MODULE_TYPE_ID) + .withConfiguration(configuration).build(); + new HueRuleConditionHandler(c, ds); + + configuration.put("address", "/lights/1/state"); + c = ConditionBuilder.create().withId("a").withTypeUID(HueRuleConditionHandler.MODULE_TYPE_ID) + .withConfiguration(configuration).build(); + new HueRuleConditionHandler(c, ds); + + configuration.put("address", "/sensors/2/state"); + c = ConditionBuilder.create().withId("a").withTypeUID(HueRuleConditionHandler.MODULE_TYPE_ID) + .withConfiguration(configuration).build(); + new HueRuleConditionHandler(c, ds); + } + + @Test + public void timeRangeAccept() { + Condition c; + Configuration configuration = new Configuration(); + configuration.put("address", "/config/localtime"); + configuration.put("operator", "in"); + configuration.put("value", "T12:12:10/T12:12:50"); + c = ConditionBuilder.create().withId("a").withTypeUID(HueRuleConditionHandler.MODULE_TYPE_ID) + .withConfiguration(configuration).build(); + HueRuleConditionHandlerEx subject = new HueRuleConditionHandlerEx(c, ds); + assertThat(subject.isSatisfied(Collections.emptyMap()), is(true)); + + configuration.put("address", "/config/localtime"); + configuration.put("operator", "not_in"); + configuration.put("value", "W8/T12:12:10/T12:12:50"); + c = ConditionBuilder.create().withId("a").withTypeUID(HueRuleConditionHandler.MODULE_TYPE_ID) + .withConfiguration(configuration).build(); + subject = new HueRuleConditionHandlerEx(c, ds); + assertThat(subject.isSatisfied(Collections.emptyMap()), is(false)); + } + + @Test + public void equalOperator() { + Map context = new TreeMap<>(); + + HueRuleConditionHandler subject; + Condition c; + Configuration configuration = new Configuration(); + configuration.put("operator", "eq"); + + context.put("newState", OnOffType.ON); + context.put("oldState", OnOffType.OFF); + configuration.put("value", "true"); + configuration.put("address", "/lights/1/state"); + c = ConditionBuilder.create().withId("a").withTypeUID(HueRuleConditionHandler.MODULE_TYPE_ID) + .withConfiguration(configuration).build(); + subject = new HueRuleConditionHandler(c, ds); + assertThat(subject.isSatisfied(context), is(true)); + + context.put("newState", OpenClosedType.OPEN); + context.put("oldState", OpenClosedType.CLOSED); + configuration.put("value", "true"); + configuration.put("address", "/sensors/2/state"); + c = ConditionBuilder.create().withId("a").withTypeUID(HueRuleConditionHandler.MODULE_TYPE_ID) + .withConfiguration(configuration).build(); + subject = new HueRuleConditionHandler(c, ds); + assertThat(subject.isSatisfied(context), is(true)); + + context.put("newState", new DecimalType(12)); + context.put("oldState", new DecimalType(0)); + configuration.put("value", "12"); + configuration.put("address", "/groups/10/action"); + c = ConditionBuilder.create().withId("a").withTypeUID(HueRuleConditionHandler.MODULE_TYPE_ID) + .withConfiguration(configuration).build(); + subject = new HueRuleConditionHandler(c, ds); + assertThat(subject.isSatisfied(context), is(true)); + } + + @Test + public void gtOperator() { + Map context = new TreeMap<>(); + + HueRuleConditionHandler subject; + Condition c; + Configuration configuration = new Configuration(); + configuration.put("operator", "gt"); + + context.put("newState", new DecimalType(12)); + context.put("oldState", new DecimalType(0)); + configuration.put("value", "10"); + configuration.put("address", "/groups/10/action"); + c = ConditionBuilder.create().withId("a").withTypeUID(HueRuleConditionHandler.MODULE_TYPE_ID) + .withConfiguration(configuration).build(); + subject = new HueRuleConditionHandler(c, ds); + assertThat(subject.isSatisfied(context), is(true)); + } + + @Test + public void ltOperator() { + Map context = new TreeMap<>(); + + HueRuleConditionHandler subject; + Condition c; + Configuration configuration = new Configuration(); + configuration.put("operator", "lt"); + + context.put("newState", new DecimalType(12)); + context.put("oldState", new DecimalType(0)); + configuration.put("value", "15"); + configuration.put("address", "/groups/10/action"); + c = ConditionBuilder.create().withId("a").withTypeUID(HueRuleConditionHandler.MODULE_TYPE_ID) + .withConfiguration(configuration).build(); + subject = new HueRuleConditionHandler(c, ds); + assertThat(subject.isSatisfied(context), is(true)); + } + +} diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/CommonSetup.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/CommonSetup.java new file mode 100644 index 0000000000000..993d47b2a0966 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/CommonSetup.java @@ -0,0 +1,157 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.net.URI; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.concurrent.ScheduledExecutorService; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; + +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.items.MetadataRegistry; +import org.eclipse.smarthome.core.net.NetworkAddressService; +import org.eclipse.smarthome.core.storage.Storage; +import org.eclipse.smarthome.core.storage.StorageService; +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; +import org.glassfish.jersey.logging.LoggingFeature; +import org.glassfish.jersey.logging.LoggingFeature.Verbosity; +import org.glassfish.jersey.server.ResourceConfig; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.rest.mocks.ConfigStoreWithoutMetadata; +import org.openhab.io.hueemulation.internal.rest.mocks.DummyMetadataRegistry; +import org.openhab.io.hueemulation.internal.rest.mocks.DummyUsersStorage; +import org.osgi.service.cm.ConfigurationAdmin; + +/** + * We have no OSGi framework in the background. This class resolves + * dependencies between the different classes and mocks common services like the configAdmin. + *

    + * The {@link UserManagement} rest components is always + * setup and started in this common test setup, because all other rest components require + * user authentication. + * + * @author David Graeff - Initial contribution + */ +public class CommonSetup { + + public UserManagement userManagement; + + @Mock + public EventPublisher eventPublisher; + + public ConfigStore cs; + + @Mock + ConfigurationAdmin configAdmin; + + @Mock + ScheduledExecutorService scheduler; + + @Mock + org.osgi.service.cm.Configuration configAdminConfig; + + @Mock + NetworkAddressService networkAddressService; + + MetadataRegistry metadataRegistry = new DummyMetadataRegistry(); + + StorageService storageService = new StorageService() { + @Override + public Storage getStorage(String name, ClassLoader classLoader) { + return getStorage(name); + } + + @SuppressWarnings("unchecked") + @Override + public Storage getStorage(String name) { + if (name.equals("hueEmulationUsers")) { + return (Storage) new DummyUsersStorage(); + } + return null; + } + }; + + public Client client; + public HttpServer server; + public String basePath; + + public CommonSetup(boolean withMetadata) throws IOException { + MockitoAnnotations.initMocks(this); + when(configAdmin.getConfiguration(anyString())).thenReturn(configAdminConfig); + when(configAdmin.getConfiguration(anyString(), any())).thenReturn(configAdminConfig); + Dictionary mockProperties = new Hashtable<>(); + when(configAdminConfig.getProperties()).thenReturn(mockProperties); + when(networkAddressService.getPrimaryIpv4HostAddress()).thenReturn("127.0.0.1"); + + // If anything is scheduled, immediately run it instead + when(scheduler.schedule(any(Runnable.class), anyLong(), any())).thenAnswer(answer -> { + ((Runnable) answer.getArgument(0)).run(); + return null; + }); + + if (withMetadata) { + cs = new ConfigStore(networkAddressService, configAdmin, metadataRegistry, scheduler); + } else { + cs = new ConfigStoreWithoutMetadata(networkAddressService, configAdmin, scheduler); + } + cs.activate(Collections.singletonMap("uuid", "demouuid")); + cs.switchFilter = Collections.singleton("Switchable"); + cs.whiteFilter = Collections.singleton("Switchable"); + cs.colorFilter = Collections.singleton("ColorLighting"); + + userManagement = Mockito.spy(new UserManagement(storageService, cs)); + + basePath = "http://localhost:8080/api"; + } + + /** + * Start the http server to serve all registered jax-rs resources. Also setup a client for testing, see + * {@link #client}. + * + * @param rc A resource config. Add objects and object instance resources to your needs. Example: + * "new ResourceConfig().registerInstances(configurationAccess)" + */ + public void start(ResourceConfig resourceConfig) { + ResourceConfig rc = resourceConfig.registerInstances(userManagement).register(new LoggingFeature( + Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME), Level.OFF, Verbosity.HEADERS_ONLY, 10)); + + Logger log2 = Logger.getLogger("org.glassfish"); + log2.setLevel(Level.OFF); + + server = GrizzlyHttpServerFactory.createHttpServer(URI.create(basePath), rc); + client = ClientBuilder.newClient(); + } + + public void dispose() { + if (client != null) { + client.close(); + } + if (server != null) { + server.shutdownNow(); + } + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/ItemUIDtoHueIDMappingTests.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/ItemUIDtoHueIDMappingTests.java new file mode 100644 index 0000000000000..aa63db8506339 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/ItemUIDtoHueIDMappingTests.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; + +import java.io.IOException; +import java.util.Collections; +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.items.ItemRegistry; +import org.eclipse.smarthome.core.items.Metadata; +import org.eclipse.smarthome.core.items.MetadataKey; +import org.eclipse.smarthome.core.library.items.SwitchItem; +import org.glassfish.jersey.server.ResourceConfig; +import org.hamcrest.CoreMatchers; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.dto.HueLightEntry; +import org.openhab.io.hueemulation.internal.dto.HueStatePlug; +import org.openhab.io.hueemulation.internal.rest.mocks.DummyItemRegistry; + +/** + * Tests for the metadata provided hue ID mapping + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class ItemUIDtoHueIDMappingTests { + protected @NonNullByDefault({}) CommonSetup commonSetup; + protected @NonNullByDefault({}) ItemRegistry itemRegistry; + + LightsAndGroups lightsAndGroups = new LightsAndGroups(); + + @Before + public void setUp() throws IOException { + commonSetup = new CommonSetup(true); + commonSetup.start(new ResourceConfig()); + + itemRegistry = new DummyItemRegistry(); + + lightsAndGroups.cs = commonSetup.cs; + lightsAndGroups.eventPublisher = commonSetup.eventPublisher; + lightsAndGroups.userManagement = commonSetup.userManagement; + lightsAndGroups.itemRegistry = itemRegistry; + lightsAndGroups.activate(); + } + + @After + public void tearDown() { + commonSetup.dispose(); + } + + @Test + public void determineHighestHueID() { + ConfigStore cs = new ConfigStore(commonSetup.networkAddressService, commonSetup.configAdmin, + commonSetup.metadataRegistry, mock(ScheduledExecutorService.class)); + + // Pretend there is a metadata entry for the imaginary item "demo1" with hueid 10 + commonSetup.metadataRegistry.add(new Metadata(new MetadataKey(ConfigStore.METAKEY, "demo1"), "10", null)); + cs.activate(Collections.singletonMap("uuid", "demouuid")); + + assertThat(cs.getHighestAssignedHueID(), CoreMatchers.is(10)); + } + + @Test + public void mapItemWithoutHueID() { + ConfigStore cs = commonSetup.cs; + assertThat(cs.getHighestAssignedHueID(), CoreMatchers.is(1)); + + SwitchItem item = new SwitchItem("switch1"); + item.setCategory("Light"); + itemRegistry.add(item); + + String hueID = cs.mapItemUIDtoHueID(item); + assertThat(hueID, CoreMatchers.is("2")); + + HueLightEntry device = cs.ds.lights.get(hueID); + assertThat(device.item, is(item)); + assertThat(device.state, is(instanceOf(HueStatePlug.class))); + + assertThat(cs.getHighestAssignedHueID(), CoreMatchers.is(2)); + } + + @Test + public void mapItemWithHueID() { + ConfigStore cs = commonSetup.cs; + assertThat(cs.getHighestAssignedHueID(), CoreMatchers.is(1)); + + SwitchItem item = new SwitchItem("switch1"); + item.setCategory("Light"); + commonSetup.metadataRegistry.add(new Metadata(new MetadataKey(ConfigStore.METAKEY, "switch1"), "10", null)); + itemRegistry.add(item); + + String hueID = cs.mapItemUIDtoHueID(item); + assertThat(hueID, CoreMatchers.is("10")); + + HueLightEntry device = cs.ds.lights.get(hueID); + assertThat(device.item, is(item)); + assertThat(device.state, is(instanceOf(HueStatePlug.class))); + + assertThat(cs.getHighestAssignedHueID(), CoreMatchers.is(1)); + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/LightsAndGroupsTests.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/LightsAndGroupsTests.java new file mode 100644 index 0000000000000..cf99574e590a9 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/LightsAndGroupsTests.java @@ -0,0 +1,344 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.verify; + +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Response; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.events.Event; +import org.eclipse.smarthome.core.items.GroupItem; +import org.eclipse.smarthome.core.items.ItemRegistry; +import org.eclipse.smarthome.core.items.events.ItemCommandEvent; +import org.eclipse.smarthome.core.library.items.ColorItem; +import org.eclipse.smarthome.core.library.items.SwitchItem; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.DeviceType; +import org.openhab.io.hueemulation.internal.dto.HueGroupEntry; +import org.openhab.io.hueemulation.internal.dto.HueLightEntry; +import org.openhab.io.hueemulation.internal.dto.HueStateColorBulb; +import org.openhab.io.hueemulation.internal.dto.HueStatePlug; +import org.openhab.io.hueemulation.internal.rest.mocks.DummyItemRegistry; + +/** + * Tests for {@link LightsAndGroups}. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class LightsAndGroupsTests { + protected @NonNullByDefault({}) CommonSetup commonSetup; + protected @NonNullByDefault({}) ItemRegistry itemRegistry; + protected @NonNullByDefault({}) ConfigStore cs; + + LightsAndGroups subject = new LightsAndGroups(); + + @Before + public void setUp() throws IOException { + commonSetup = new CommonSetup(false); + itemRegistry = new DummyItemRegistry(); + + this.cs = commonSetup.cs; + + subject.cs = cs; + subject.eventPublisher = commonSetup.eventPublisher; + subject.userManagement = commonSetup.userManagement; + subject.itemRegistry = itemRegistry; + subject.activate(); + + // Add simulated lights + cs.ds.lights.put("1", new HueLightEntry(new SwitchItem("switch"), "switch", DeviceType.SwitchType)); + cs.ds.lights.put("2", new HueLightEntry(new ColorItem("color"), "color", DeviceType.ColorType)); + cs.ds.lights.put("3", new HueLightEntry(new ColorItem("white"), "white", DeviceType.WhiteTemperatureType)); + + // Add group item + cs.ds.groups.put("10", + new HueGroupEntry("name", new GroupItem("white", new SwitchItem("switch")), DeviceType.SwitchType)); + + commonSetup.start(new ResourceConfig().registerInstances(subject)); + } + + @After + public void tearDown() { + commonSetup.dispose(); + } + + @Test + public void addSwitchableByCategory() throws IOException { + SwitchItem item = new SwitchItem("switch1"); + item.setCategory("Light"); + itemRegistry.add(item); + HueLightEntry device = cs.ds.lights.get(cs.mapItemUIDtoHueID(item)); + assertThat(device.item, is(item)); + assertThat(device.state, is(instanceOf(HueStatePlug.class))); + + } + + @Test + public void addSwitchableByTag() throws IOException { + SwitchItem item = new SwitchItem("switch1"); + item.addTag("Switchable"); + itemRegistry.add(item); + HueLightEntry device = cs.ds.lights.get(cs.mapItemUIDtoHueID(item)); + assertThat(device.item, is(item)); + assertThat(device.state, is(instanceOf(HueStatePlug.class))); + } + + @Test + public void ignoreByTag() throws IOException { + SwitchItem item = new SwitchItem("switch1"); + item.addTags("Switchable", "internal"); // The ignore tag will win + itemRegistry.add(item); + HueLightEntry device = cs.ds.lights.get(cs.mapItemUIDtoHueID(item)); + assertThat(device, is(nullValue())); + } + + @Test + public void addGroupSwitchableByTag() throws IOException { + GroupItem item = new GroupItem("group1", new SwitchItem("switch1")); + item.addTag("Switchable"); + itemRegistry.add(item); + HueGroupEntry device = cs.ds.groups.get(cs.mapItemUIDtoHueID(item)); + assertThat(device.groupItem, is(item)); + assertThat(device.action, is(instanceOf(HueStatePlug.class))); + } + + @Test + public void addGroupWithoutTypeByTag() throws IOException { + GroupItem item = new GroupItem("group1", null); + item.addTag("Switchable"); + + itemRegistry.add(item); + + HueGroupEntry device = cs.ds.groups.get(cs.mapItemUIDtoHueID(item)); + assertThat(device.groupItem, is(item)); + assertThat(device.action, is(instanceOf(HueStatePlug.class))); + assertThat(cs.ds.groups.get(cs.mapItemUIDtoHueID(item)).groupItem, is(item)); + } + + @Test + public void removeGroupWithoutTypeAndTag() throws IOException { + String groupName = "group1"; + GroupItem item = new GroupItem(groupName, null); + item.addTag("Switchable"); + itemRegistry.add(item); + + String hueID = cs.mapItemUIDtoHueID(item); + assertThat(cs.ds.groups.get(hueID), notNullValue()); + + subject.updated(item, new GroupItem(groupName, null)); + + assertThat(cs.ds.groups.get(hueID), nullValue()); + } + + @Test + public void updateSwitchable() throws IOException { + SwitchItem item = new SwitchItem("switch1"); + item.setLabel("labelOld"); + item.addTag("Switchable"); + itemRegistry.add(item); + String hueID = cs.mapItemUIDtoHueID(item); + HueLightEntry device = cs.ds.lights.get(hueID); + assertThat(device.item, is(item)); + assertThat(device.state, is(instanceOf(HueStatePlug.class))); + assertThat(device.name, is("labelOld")); + + SwitchItem newitem = new SwitchItem("switch1"); + newitem.setLabel("labelNew"); + newitem.addTag("Switchable"); + subject.updated(item, newitem); + device = cs.ds.lights.get(hueID); + assertThat(device.item, is(newitem)); + assertThat(device.state, is(instanceOf(HueStatePlug.class))); + assertThat(device.name, is("labelNew")); + + // Update with an item that has no tags anymore -> should be removed + SwitchItem newitemWithoutTag = new SwitchItem("switch1"); + newitemWithoutTag.setLabel("labelNew2"); + subject.updated(newitem, newitemWithoutTag); + + device = cs.ds.lights.get(hueID); + assertThat(device, nullValue()); + } + + @Test + public void changeSwitchState() throws IOException { + + assertThat(((HueStatePlug) cs.ds.lights.get("1").state).on, is(false)); + + String body = "{'on':true}"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/lights/1/state").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("success")); + assertThat(((HueStatePlug) cs.ds.lights.get("1").state).on, is(true)); + verify(commonSetup.eventPublisher).post(argThat((Event t) -> { + assertThat(t.getPayload(), is("{\"type\":\"OnOff\",\"value\":\"ON\"}")); + return true; + })); + } + + @Test + public void changeGroupItemSwitchState() throws IOException { + + assertThat(((HueStatePlug) cs.ds.groups.get("10").action).on, is(false)); + + String body = "{'on':true}"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/groups/10/action").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("success")); + assertThat(((HueStatePlug) cs.ds.groups.get("10").action).on, is(true)); + verify(commonSetup.eventPublisher).post(argThat((Event t) -> { + assertThat(t.getPayload(), is("{\"type\":\"OnOff\",\"value\":\"ON\"}")); + return true; + })); + } + + @Test + public void changeOnValue() throws IOException { + + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(false)); + + String body = "{'on':true}"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/lights/2/state").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + String entity = response.readEntity(String.class); + assertThat(entity, is("[{\"success\":{\"/lights/2/state/on\":true}}]")); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(true)); + } + + @Test + public void changeOnAndBriValues() throws IOException { + + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(false)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).bri, is(0)); + + String body = "{'on':true,'bri':200}"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/lights/2/state").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("success")); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(true)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).bri, is(200)); + } + + @Test + public void changeHueSatValues() throws IOException { + HueLightEntry hueDevice = cs.ds.lights.get("2"); + hueDevice.item.setState(OnOffType.ON); + hueDevice.state.as(HueStateColorBulb.class).on = true; + + String body = "{'hue':1000,'sat':50}"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/lights/2/state").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("success")); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(true)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).hue, is(1000)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).sat, is(50)); + + verify(commonSetup.eventPublisher).post(argThat(ce -> assertHueValue((ItemCommandEvent) ce, 1000))); + } + + /** + * Amazon echos are setting ct only, if commanded to turn a light white. + */ + @Test + public void changeCtValue() throws IOException { + HueLightEntry hueDevice = cs.ds.lights.get("2"); + hueDevice.item.setState(OnOffType.ON); + hueDevice.state.as(HueStateColorBulb.class).on = true; + + String body = "{'ct':500}"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/lights/2/state").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + body = response.readEntity(String.class); + assertThat(body, containsString("success")); + assertThat(body, containsString("ct")); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(true)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).ct, is(500)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).sat, is(0)); + + // Saturation is expected to be 0 -> white light + verify(commonSetup.eventPublisher).post(argThat(ce -> assertSatValue((ItemCommandEvent) ce, 0))); + } + + @Test + public void switchOnWithXY() throws IOException { + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(false)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).bri, is(0)); + + String body = "{'on':true,'bri':200,'xy':[0.5119,0.4147]}"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/lights/2/state").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("success")); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(true)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).bri, is(200)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).xy[0], is(0.5119)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).xy[1], is(0.4147)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).colormode, is(HueStateColorBulb.ColorMode.xy)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).toHSBType().getHue().intValue(), + is((int) 27.47722590981918)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).toHSBType().getSaturation().intValue(), is(88)); + assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).toHSBType().getBrightness().intValue(), is(78)); + } + + @Test + public void allLightsAndSingleLight() + throws InterruptedException, ExecutionException, TimeoutException, IOException { + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/lights").request().get(); + assertEquals(200, response.getStatus()); + + String body = response.readEntity(String.class); + + assertThat(body, containsString("switch")); + assertThat(body, containsString("color")); + assertThat(body, containsString("white")); + + // Single light access test + response = commonSetup.client.target(commonSetup.basePath + "/testuser/lights/2").request().get(); + assertEquals(200, response.getStatus()); + body = response.readEntity(String.class); + assertThat(body, containsString("color")); + } + + private boolean assertHueValue(ItemCommandEvent ce, int hueValue) { + assertThat(((HSBType) ce.getItemCommand()).getHue().intValue(), is(hueValue * 360 / HueStateColorBulb.MAX_HUE)); + return true; + } + + private boolean assertSatValue(ItemCommandEvent ce, int satValue) { + assertThat(((HSBType) ce.getItemCommand()).getSaturation().intValue(), + is(satValue * 100 / HueStateColorBulb.MAX_SAT)); + return true; + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/RulesTests.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/RulesTests.java new file mode 100644 index 0000000000000..828ef7deffb6e --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/RulesTests.java @@ -0,0 +1,280 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Response; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.items.GenericItem; +import org.eclipse.smarthome.core.items.ItemRegistry; +import org.eclipse.smarthome.core.library.items.ColorItem; +import org.eclipse.smarthome.core.library.items.SwitchItem; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.types.State; +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.openhab.core.automation.Condition; +import org.openhab.core.automation.Rule; +import org.openhab.core.automation.RuleRegistry; +import org.openhab.core.automation.Trigger; +import org.openhab.core.automation.util.RuleBuilder; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.RuleUtils; +import org.openhab.io.hueemulation.internal.dto.HueRuleEntry; +import org.openhab.io.hueemulation.internal.dto.HueRuleEntry.Operator; +import org.openhab.io.hueemulation.internal.dto.HueSceneEntry; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueCommand; +import org.openhab.io.hueemulation.internal.rest.mocks.DummyItemRegistry; +import org.openhab.io.hueemulation.internal.rest.mocks.DummyRuleRegistry; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +/** + * Tests for various rules API endpoints. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class RulesTests { + protected @NonNullByDefault({}) CommonSetup commonSetup; + protected @NonNullByDefault({}) ConfigStore cs; + protected @NonNullByDefault({}) ItemRegistry itemRegistry; + protected @NonNullByDefault({}) RuleRegistry ruleRegistry; + + Rules subject = new Rules(); + LightsAndGroups lightsAndGroups = new LightsAndGroups(); + + private void addItemToReg(GenericItem item, State state, String tag, String label) { + item.setState(state); + item.setLabel(label); + item.addTag(tag); + itemRegistry.add(item); + } + + @Before + public void setUp() throws IOException { + commonSetup = new CommonSetup(false); + this.cs = commonSetup.cs; + + itemRegistry = new DummyItemRegistry(); + ruleRegistry = new DummyRuleRegistry(); + + subject.cs = commonSetup.cs; + subject.userManagement = commonSetup.userManagement; + subject.ruleRegistry = ruleRegistry; + subject.itemRegistry = itemRegistry; + subject.activate(); + + // We need the LightsAndGroups class to convert registry entries into HueDatastore + // light entries + lightsAndGroups.cs = cs; + lightsAndGroups.eventPublisher = commonSetup.eventPublisher; + lightsAndGroups.userManagement = commonSetup.userManagement; + lightsAndGroups.itemRegistry = itemRegistry; + lightsAndGroups.activate(); + + addItemToReg(new SwitchItem("switch1"), OnOffType.ON, "Switchable", "name1"); + addItemToReg(new SwitchItem("switch2"), OnOffType.ON, "Switchable", "name2"); + addItemToReg(new ColorItem("color1"), HSBType.BLUE, "ColorLighting", ""); + + commonSetup.start(new ResourceConfig().registerInstances(subject)); + } + + @After + public void tearDown() { + RuleUtils.random = new Random(); + commonSetup.dispose(); + } + + @Test + public void addUpdateRemoveScheduleToRegistry() { + assertThat(cs.ds.lights.get("switch1"), is(notNullValue())); + + HueCommand command = new HueCommand("/api/testuser/lights/switch1/state", "PUT", "{'on':true}"); + HueRuleEntry.Condition condition = new HueRuleEntry.Condition("/lights/switch1/state/on", Operator.dx, null); + + Entry triggerCond = Rules.hueConditionToAutomation(command.address.replace("/", "-"), + condition, itemRegistry); + + Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) // + .withActions(RuleUtils.createHttpAction(command, "command")) // + .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build(); + + ruleRegistry.add(rule); + + // Check hue entry + HueRuleEntry entry = cs.ds.rules.get("demo1"); + assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on")); + assertThat(entry.conditions.get(0).operator, is(Operator.dx)); + assertThat(entry.actions.get(0).address, is("/lights/switch1/state")); + assertThat(entry.actions.get(0).method, is("PUT")); + assertThat(entry.actions.get(0).body, is("{'on':true}")); + + // Update + command = new HueCommand("/api/testuser/lights/switch2/state", "PUT", "{'on':false}"); + rule = RuleBuilder.create("demo1").withName("name2").withTags(Rules.RULES_TAG) // + .withActions(RuleUtils.createHttpAction(command, "command")) // + .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build(); + ruleRegistry.update(rule); + + entry = cs.ds.rules.get("demo1"); + assertThat(entry.actions.get(0).address, is("/lights/switch2/state")); + assertThat(entry.actions.get(0).method, is("PUT")); + assertThat(entry.actions.get(0).body, is("{'on':false}")); + assertThat(entry.name, is("name2")); + + // Remove + + ruleRegistry.remove("demo1"); + entry = cs.ds.rules.get("demo1"); + assertThat(entry, nullValue()); + } + + @SuppressWarnings("null") + @Test + public void addGetRemoveRuleViaRest() { + // 1. Create + String body = "{\"name\":\"test name\",\"description\":\"\",\"owner\":\"\",\"conditions\":[{\"address\":\"/lights/switch1/state/on\",\"operator\":\"dx\"}],\"actions\":[{\"address\":\"/lights/switch1/state\",\"method\":\"PUT\",\"body\":\"{\\u0027on\\u0027:true}\"}]}"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules").request() + .post(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("success")); + + // 1.1 Check for entry + Entry idAndEntry = cs.ds.rules.entrySet().stream().findAny().get(); + HueRuleEntry entry = idAndEntry.getValue(); + assertThat(entry.name, is("test name")); + assertThat(entry.actions.get(0).address, is("/lights/switch1/state")); + assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on")); + + // 1.2 Check for rule + Rule rule = ruleRegistry.get(idAndEntry.getKey()); + assertThat(rule.getName(), is("test name")); + assertThat(rule.getActions().get(0).getId(), is("-api-testuser-lights-switch1-state")); + assertThat(rule.getActions().get(0).getTypeUID(), is("rules.HttpAction")); + + // 2. Get + response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/" + idAndEntry.getKey()).request() + .get(); + assertEquals(200, response.getStatus()); + HueSceneEntry fromJson = new Gson().fromJson(response.readEntity(String.class), HueSceneEntry.class); + assertThat(fromJson.name, is(idAndEntry.getValue().name)); + + // 3. Remove + response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/" + idAndEntry.getKey()).request() + .delete(); + assertEquals(200, response.getStatus()); + assertTrue(cs.ds.rules.isEmpty()); + } + + @Test + public void updateRuleViaRest() { + HueCommand command = new HueCommand("/api/testuser/lights/switch1/state", "PUT", "{'on':true}"); + HueRuleEntry.Condition condition = new HueRuleEntry.Condition("/lights/switch1/state/on", Operator.dx, null); + + Entry triggerCond = Rules.hueConditionToAutomation(command.address.replace("/", "-"), + condition, itemRegistry); + + Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) // + .withActions(RuleUtils.createHttpAction(command, "command")) // + .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build(); + + ruleRegistry.add(rule); + + // Modify (just the name) + String body = "{ 'name':'A new name'}"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/demo1").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("name")); + + Entry idAndEntry = cs.ds.rules.entrySet().stream().findAny().get(); + HueRuleEntry entry = idAndEntry.getValue(); + assertThat(entry.name, is("A new name")); + assertThat(entry.actions.get(0).address, is("/lights/switch1/state")); + assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on")); + + // Reset + rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) // + .withActions(RuleUtils.createHttpAction(command, "command")) // + .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build(); + + ruleRegistry.update(rule); // Reset rule + + idAndEntry = cs.ds.rules.entrySet().stream().findAny().get(); + + // Modify (Change condition) + body = "{\"conditions\":[{\"address\":\"/lights/switch1/state/on\",\"operator\":\"ddx\"}]}"; + response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/demo1").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("conditions")); + + idAndEntry = cs.ds.rules.entrySet().stream().findAny().get(); + entry = idAndEntry.getValue(); + assertThat(entry.name, is("test name")); // should not have changed + assertThat(entry.conditions.get(0).operator, is(Operator.ddx)); + + // Modify (Change action) + body = "{\"actions\":[{\"address\":\"/lights/switch2/state\",\"method\":\"PUT\",\"body\":\"{\\u0027on\\u0027:false}\"}]}"; + response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules/demo1").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("actions")); + + idAndEntry = cs.ds.rules.entrySet().stream().findAny().get(); + entry = idAndEntry.getValue(); + assertThat(entry.name, is("test name")); // should not have changed + assertThat(entry.actions.get(0).address, is("/lights/switch2/state")); + } + + @Test + public void getAll() { + HueCommand command = new HueCommand("/api/testuser/lights/switch1/state", "PUT", "{'on':true}"); + HueRuleEntry.Condition condition = new HueRuleEntry.Condition("/lights/switch1/state/on", Operator.dx, null); + + Entry triggerCond = Rules.hueConditionToAutomation(command.address.replace("/", "-"), + condition, itemRegistry); + + Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Rules.RULES_TAG) // + .withActions(RuleUtils.createHttpAction(command, "command")) // + .withTriggers(triggerCond.getKey()).withConditions(triggerCond.getValue()).build(); + + ruleRegistry.add(rule); + + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/rules").request().get(); + Type type = new TypeToken>() { + }.getType(); + String body = response.readEntity(String.class); + Map fromJson = new Gson().fromJson(body, type); + HueRuleEntry entry = fromJson.get("demo1"); + assertThat(entry.name, is("test name")); + assertThat(entry.actions.get(0).address, is("/lights/switch1/state")); + assertThat(entry.conditions.get(0).address, is("/lights/switch1/state/on")); + } + +} diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/SceneTests.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/SceneTests.java new file mode 100644 index 0000000000000..cefd8478e71be --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/SceneTests.java @@ -0,0 +1,231 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.Map.Entry; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Response; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.items.GenericItem; +import org.eclipse.smarthome.core.items.GroupItem; +import org.eclipse.smarthome.core.items.ItemRegistry; +import org.eclipse.smarthome.core.library.items.ColorItem; +import org.eclipse.smarthome.core.library.items.DimmerItem; +import org.eclipse.smarthome.core.library.items.RollershutterItem; +import org.eclipse.smarthome.core.library.items.SwitchItem; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.types.Command; +import org.eclipse.smarthome.core.types.State; +import org.glassfish.jersey.server.ResourceConfig; +import org.hamcrest.CoreMatchers; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.openhab.core.automation.Rule; +import org.openhab.core.automation.RuleRegistry; +import org.openhab.core.automation.util.RuleBuilder; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.dto.HueSceneEntry; +import org.openhab.io.hueemulation.internal.rest.mocks.DummyItemRegistry; +import org.openhab.io.hueemulation.internal.rest.mocks.DummyRuleRegistry; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +/** + * Tests for various scene API endpoints. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class SceneTests { + protected @NonNullByDefault({}) CommonSetup commonSetup; + protected @NonNullByDefault({}) ConfigStore cs; + protected @NonNullByDefault({}) ItemRegistry itemRegistry; + protected @NonNullByDefault({}) RuleRegistry ruleRegistry; + + Scenes subject = new Scenes(); + + private void addItemToReg(GenericItem item, State state, String tag) { + item.setState(state); + item.addTag(tag); + itemRegistry.add(item); + } + + @Before + public void setUp() throws IOException { + commonSetup = new CommonSetup(false); + this.cs = commonSetup.cs; + + itemRegistry = new DummyItemRegistry(); + ruleRegistry = new DummyRuleRegistry(); + + subject.cs = commonSetup.cs; + subject.userManagement = commonSetup.userManagement; + subject.itemRegistry = itemRegistry; + subject.ruleRegistry = ruleRegistry; + subject.activate(); + + // Add simulated lights + addItemToReg(new SwitchItem("switch1"), OnOffType.ON, "Switchable"); + addItemToReg(new ColorItem("color1"), HSBType.BLUE, "ColorLighting"); + addItemToReg(new DimmerItem("white1"), new PercentType(12), "Lighting"); + addItemToReg(new RollershutterItem("roller1"), new PercentType(12), "Lighting"); + addItemToReg(new DimmerItem("white1"), new PercentType(12), "Lighting"); + addItemToReg(new GroupItem("group1"), OnOffType.ON, "Switchable"); + + commonSetup.start(new ResourceConfig().registerInstances(subject)); + } + + @After + public void tearDown() { + commonSetup.dispose(); + } + + @SuppressWarnings("null") + @Test + public void addUpdateRemoveSceneToRegistry() { + Rule rule = RuleBuilder.create("demo1").withTags("scene") // + .withActions(Scenes.actionFromState("switch1", (Command) OnOffType.ON)).build(); + + ruleRegistry.add(rule); + + HueSceneEntry sceneEntry = cs.ds.scenes.get("demo1"); + assertThat(sceneEntry.lights.get(0), CoreMatchers.is("switch1")); + + // Update + rule = RuleBuilder.create("demo1").withTags("scene") // + .withActions(Scenes.actionFromState("white1", (Command) OnOffType.ON)).build(); + ruleRegistry.update(rule); + + sceneEntry = cs.ds.scenes.get("demo1"); + assertThat(sceneEntry.lights.get(0), CoreMatchers.is("white1")); + + // Remove + + ruleRegistry.remove("demo1"); + sceneEntry = cs.ds.scenes.get("demo1"); + assertThat(sceneEntry, CoreMatchers.nullValue()); + } + + @SuppressWarnings("null") + @Test + public void addGetRemoveSceneViaRest() { + // 1. Create + String body = "{ 'name':'Cozy dinner', 'recycle':false, 'lights':['switch1','white1'], 'type':'LightScene'}"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/scenes").request() + .post(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("success")); + + // 1.1 Check for scene entry + Entry entry = cs.ds.scenes.entrySet().stream().findAny().get(); + assertThat(entry.getValue().name, is("Cozy dinner")); + assertThat(entry.getValue().lights.get(0), is("switch1")); + assertThat(entry.getValue().lights.get(1), is("white1")); + + // 1.2 Check for rule + Rule rule = ruleRegistry.get(entry.getKey()); + assertThat(rule.getName(), is("Cozy dinner")); + assertThat(rule.getActions().get(0).getId(), is("switch1")); + assertThat(rule.getActions().get(1).getId(), is("white1")); + + // 2. Get + response = commonSetup.client.target(commonSetup.basePath + "/testuser/scenes/" + entry.getKey()).request() + .get(); + assertEquals(200, response.getStatus()); + HueSceneEntry fromJson = new Gson().fromJson(response.readEntity(String.class), HueSceneEntry.class); + assertThat(fromJson.name, is(entry.getValue().name)); + + // 3. Remove + response = commonSetup.client.target(commonSetup.basePath + "/testuser/scenes/" + entry.getKey()).request() + .delete(); + assertEquals(200, response.getStatus()); + assertTrue(cs.ds.scenes.isEmpty()); + } + + @SuppressWarnings("null") + @Test + public void updateSceneViaRest() { + Rule rule = RuleBuilder.create("demo1").withTags("scene").withName("Some name") // + .withActions(Scenes.actionFromState("switch1", (Command) OnOffType.ON)).build(); + + ruleRegistry.add(rule); + + // 3. Modify (just the name) + String body = "{ 'name':'A new name'}"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/scenes/demo1").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("name")); + + Entry sceneEntry = cs.ds.scenes.entrySet().stream().findAny().get(); + assertThat(sceneEntry.getValue().name, is("A new name")); + assertThat(sceneEntry.getValue().lights.get(0), is("switch1")); // nothing else should have changed + + // 3. Modify (just the lights) + rule = RuleBuilder.create("demo1").withTags("scene").withName("Some name") // + .withActions(Scenes.actionFromState("switch1", (Command) OnOffType.ON)).build(); + + ruleRegistry.update(rule); // Reset rule + + sceneEntry = cs.ds.scenes.entrySet().stream().findAny().get(); + String uid = sceneEntry.getKey(); + + // Without store lights + body = "{ 'lights':['white1']}"; + response = commonSetup.client.target(commonSetup.basePath + "/testuser/scenes/demo1").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("lights")); + + sceneEntry = cs.ds.scenes.entrySet().stream().findAny().get(); + assertThat(sceneEntry.getValue().name, is("Some name")); // should not have changed + assertThat(sceneEntry.getKey(), is(uid)); + assertThat(sceneEntry.getValue().lights.get(0), is("switch1")); // storelightstate not set, lights not changed + + // With store lights + body = "{ 'lights':['white1'], 'storelightstate':true }"; + response = commonSetup.client.target(commonSetup.basePath + "/testuser/scenes/demo1").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("lights")); + + sceneEntry = cs.ds.scenes.entrySet().stream().findAny().get(); + assertThat(sceneEntry.getValue().lights.get(0), is("white1")); + } + + @Test + public void getAll() { + Rule rule = RuleBuilder.create("demo1").withTags("scene") // + .withActions(Scenes.actionFromState("switch1", (Command) OnOffType.ON)).build(); + + ruleRegistry.add(rule); + + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/scenes").request().get(); + Type type = new TypeToken>() { + }.getType(); + Map fromJson = new Gson().fromJson(response.readEntity(String.class), type); + assertTrue(fromJson.containsKey("demo1")); + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/ScheduleTests.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/ScheduleTests.java new file mode 100644 index 0000000000000..d31f44960ec3f --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/ScheduleTests.java @@ -0,0 +1,426 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Response; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.config.core.Configuration; +import org.eclipse.smarthome.core.library.items.ColorItem; +import org.eclipse.smarthome.core.library.items.SwitchItem; +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.openhab.core.automation.Rule; +import org.openhab.core.automation.RuleManager; +import org.openhab.core.automation.RuleRegistry; +import org.openhab.core.automation.Trigger; +import org.openhab.core.automation.util.RuleBuilder; +import org.openhab.core.automation.util.TriggerBuilder; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.DeviceType; +import org.openhab.io.hueemulation.internal.RuleUtils; +import org.openhab.io.hueemulation.internal.dto.HueLightEntry; +import org.openhab.io.hueemulation.internal.dto.HueSceneEntry; +import org.openhab.io.hueemulation.internal.dto.HueScheduleEntry; +import org.openhab.io.hueemulation.internal.dto.changerequest.HueCommand; +import org.openhab.io.hueemulation.internal.rest.mocks.DummyRuleRegistry; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +/** + * Tests for various schedule API endpoints. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class ScheduleTests { + protected @NonNullByDefault({}) CommonSetup commonSetup; + protected @NonNullByDefault({}) ConfigStore cs; + protected @NonNullByDefault({}) RuleRegistry ruleRegistry; + + Schedules subject = new Schedules(); + + @Before + public void setUp() throws IOException { + commonSetup = new CommonSetup(false); + this.cs = commonSetup.cs; + + ruleRegistry = new DummyRuleRegistry(); + + subject.cs = commonSetup.cs; + subject.userManagement = commonSetup.userManagement; + subject.ruleManager = mock(RuleManager.class); + when(subject.ruleManager.isEnabled(anyString())).thenReturn(true); + subject.ruleRegistry = ruleRegistry; + subject.activate(); + + // Add simulated lights + cs.ds.lights.put("1", new HueLightEntry(new SwitchItem("switch"), "switch", DeviceType.SwitchType)); + cs.ds.lights.put("2", new HueLightEntry(new ColorItem("color"), "color", DeviceType.ColorType)); + cs.ds.lights.put("3", new HueLightEntry(new ColorItem("white"), "white", DeviceType.WhiteTemperatureType)); + + commonSetup.start(new ResourceConfig().registerInstances(subject)); + + // Mock random -> always return int=10 or the highest possible int if bounded + Random random = mock(Random.class); + doReturn(10).when(random).nextInt(); + doAnswer(a -> { + Integer bound = a.getArgument(0); + return bound - 1; + }).when(random).nextInt(anyInt()); + RuleUtils.random = random; + } + + @After + public void tearDown() { + RuleUtils.random = new Random(); + commonSetup.dispose(); + } + + @SuppressWarnings("null") + @Test + public void addUpdateRemoveScheduleToRegistry() { + HueCommand command = new HueCommand("/api/testuser/lights/1/state", "PUT", "{'on':true}"); + String localtime = "2020-02-01T12:12:00"; + + Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Schedules.SCHEDULE_TAG) // + .withActions(RuleUtils.createHttpAction(command, "command")) // + .withTriggers(RuleUtils.createTriggerForTimeString(localtime)).build(); + + ruleRegistry.add(rule); + + // Check hue entry + HueScheduleEntry sceneEntry = cs.ds.schedules.get("demo1"); + assertThat(sceneEntry.command.address, is("/api/testuser/lights/1/state")); + assertThat(sceneEntry.command.method, is("PUT")); + assertThat(sceneEntry.command.body, is("{'on':true}")); + assertThat(sceneEntry.localtime, is(localtime)); + + // Update + localtime = "2021-03-01T17:12:00"; + rule = RuleBuilder.create("demo1").withName("test name").withTags(Schedules.SCHEDULE_TAG) // + .withActions(RuleUtils.createHttpAction(command, "command")) // + .withTriggers(RuleUtils.createTriggerForTimeString(localtime)).build(); + ruleRegistry.update(rule); + + sceneEntry = cs.ds.schedules.get("demo1"); + assertThat(sceneEntry.command.address, is("/api/testuser/lights/1/state")); + assertThat(sceneEntry.localtime, is(localtime)); + + // Remove + + ruleRegistry.remove("demo1"); + sceneEntry = cs.ds.schedules.get("demo1"); + assertThat(sceneEntry, nullValue()); + } + + @SuppressWarnings("null") + @Test + public void addGetRemoveScheduleViaRest() { + // 1. Create + String body = "{ 'name':'Wake up', 'description':'My wake up alarm', 'localtime':'2015-06-30T14:24:40'," + // + "'command':{'address':'/api/testuser/lights/1/state','method':'PUT','body':'{\"on\":true}'} }"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/schedules").request() + .post(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("success")); + + // 1.1 Check for entry + Entry entry = cs.ds.schedules.entrySet().stream().findAny().get(); + assertThat(entry.getValue().name, is("Wake up")); + assertThat(entry.getValue().command.address, is("/api/testuser/lights/1/state")); + assertThat(entry.getValue().command.method, is("PUT")); + assertThat(entry.getValue().command.body, is("{\"on\":true}")); + assertThat(entry.getValue().localtime, is("2015-06-30T14:24:40")); + + // 1.2 Check for rule + Rule rule = ruleRegistry.get(entry.getKey()); + assertThat(rule.getName(), is("Wake up")); + assertThat(rule.getActions().get(0).getId(), is("command")); + assertThat(rule.getActions().get(0).getTypeUID(), is("rules.HttpAction")); + + // 2. Get + response = commonSetup.client.target(commonSetup.basePath + "/testuser/schedules/" + entry.getKey()).request() + .get(); + assertEquals(200, response.getStatus()); + HueSceneEntry fromJson = new Gson().fromJson(response.readEntity(String.class), HueSceneEntry.class); + assertThat(fromJson.name, is(entry.getValue().name)); + + // 3. Remove + response = commonSetup.client.target(commonSetup.basePath + "/testuser/schedules/" + entry.getKey()).request() + .delete(); + assertEquals(200, response.getStatus()); + assertTrue(cs.ds.schedules.isEmpty()); + } + + @SuppressWarnings("null") + @Test + public void updateScheduleViaRest() { + HueCommand command = new HueCommand("/api/testuser/lights/1/state", "PUT", "{'on':true}"); + String localtime = "2020-02-01T12:12:00"; + + Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Schedules.SCHEDULE_TAG) // + .withActions(RuleUtils.createHttpAction(command, "command")) // + .withTriggers(RuleUtils.createTriggerForTimeString(localtime)).build(); + + ruleRegistry.add(rule); + + // Modify (just the name) + String body = "{ 'name':'A new name'}"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/schedules/demo1").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("name")); + + Entry entry = cs.ds.schedules.entrySet().stream().findAny().get(); + assertThat(entry.getValue().name, is("A new name")); + assertThat(entry.getValue().command.address, is("/api/testuser/lights/1/state")); // nothing else should have + // changed + assertThat(entry.getValue().localtime, is(localtime)); + + // Reset + rule = RuleBuilder.create("demo1").withName("test name").withTags(Schedules.SCHEDULE_TAG) // + .withActions(RuleUtils.createHttpAction(command, "command")) // + .withTriggers(RuleUtils.createTriggerForTimeString(localtime)).build(); + + ruleRegistry.update(rule); // Reset rule + + entry = cs.ds.schedules.entrySet().stream().findAny().get(); + String uid = entry.getKey(); + + // Modify (Change time) + body = "{ 'localtime':'2015-06-30T14:24:40'}"; + response = commonSetup.client.target(commonSetup.basePath + "/testuser/schedules/demo1").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("localtime")); + + entry = cs.ds.schedules.entrySet().stream().findAny().get(); + assertThat(entry.getValue().name, is("test name")); // should not have changed + assertThat(entry.getKey(), is(uid)); + assertThat(entry.getValue().localtime, is("2015-06-30T14:24:40")); + + // Modify (Change command) + body = "{ 'command':{'address':'/api/testuser/lights/2/state','method':'PUT','body':'{\"on\":true}'} }"; + response = commonSetup.client.target(commonSetup.basePath + "/testuser/schedules/demo1").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + assertThat(response.readEntity(String.class), containsString("command")); + + entry = cs.ds.schedules.entrySet().stream().findAny().get(); + assertThat(entry.getValue().name, is("test name")); // should not have changed + assertThat(entry.getKey(), is(uid)); + assertThat(entry.getValue().command.address, is("/api/testuser/lights/2/state")); + } + + @Test + public void getAll() { + HueCommand command = new HueCommand("/api/testuser/lights/1/state", "POST", "{'on':true}"); + String localtime = "2020-02-01T12:12:00"; + + Rule rule = RuleBuilder.create("demo1").withName("test name").withTags(Schedules.SCHEDULE_TAG) // + .withActions(RuleUtils.createHttpAction(command, "command")) // + .withTriggers(RuleUtils.createTriggerForTimeString(localtime)).build(); + + ruleRegistry.add(rule); + + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/schedules").request().get(); + Type type = new TypeToken>() { + }.getType(); + Map fromJson = new Gson().fromJson(response.readEntity(String.class), type); + assertTrue(fromJson.containsKey("demo1")); + } + + @Test + public void timeStringToTrigger() { + + String timeString; + Trigger trigger; + Configuration configuration; + + // absolute time + timeString = "2020-02-01T12:12:00"; + trigger = RuleUtils.createTriggerForTimeString(timeString); + configuration = trigger.getConfiguration(); + + assertThat(trigger.getTypeUID(), is("timer.AbsoluteDateTimeTrigger")); + assertThat(configuration.get("date"), is("2020-02-01")); + assertThat(configuration.get("time"), is("12:12:00")); + + // absolute randomized time + timeString = "2020-02-01T12:12:00A14:12:34"; + trigger = RuleUtils.createTriggerForTimeString(timeString); + configuration = trigger.getConfiguration(); + + assertThat(trigger.getTypeUID(), is("timer.AbsoluteDateTimeTrigger")); + assertThat(configuration.get("date"), is("2020-02-01")); + assertThat(configuration.get("time"), is("12:12:00")); + assertThat(configuration.get("randomizeTime"), is("14:12:34")); + + // Recurring times,Monday = 64, Tuesday = 32, Wednesday = 16, Thursday = 8, Friday = 4, Saturday = 2, Sunday= 1 + // Cron expression: min hour day month weekdays + timeString = "W3/T12:15:17"; + trigger = RuleUtils.createTriggerForTimeString(timeString); + configuration = trigger.getConfiguration(); + + assertThat(trigger.getTypeUID(), is("timer.GenericCronTrigger")); + assertThat(configuration.get("cronExpression"), is("15 12 * * 6,7")); + + // Recurring randomized times + timeString = "W127/T12:15:17A14:12:34"; + trigger = RuleUtils.createTriggerForTimeString(timeString); + configuration = trigger.getConfiguration(); + + assertThat(trigger.getTypeUID(), is("timer.GenericCronTrigger")); + assertThat(configuration.get("cronExpression"), is("15 14 * * 1,2,3,4,5,6,7")); + + // Timer, expiring after given time + timeString = "PT12:12:00"; + trigger = RuleUtils.createTriggerForTimeString(timeString); + configuration = trigger.getConfiguration(); + + assertThat(trigger.getTypeUID(), is("timer.TimerTrigger")); + assertThat(configuration.get("time"), is("12:12:00")); + + // Timer with random element + timeString = "PT12:12:00A14:12:34"; + trigger = RuleUtils.createTriggerForTimeString(timeString); + configuration = trigger.getConfiguration(); + + assertThat(trigger.getTypeUID(), is("timer.TimerTrigger")); + assertThat(configuration.get("time"), is("12:12:00")); + assertThat(configuration.get("randomizeTime"), is("14:12:34")); + + // Timers, Recurring timer + timeString = "R/PT12:12:00"; + trigger = RuleUtils.createTriggerForTimeString(timeString); + configuration = trigger.getConfiguration(); + + assertThat(trigger.getTypeUID(), is("timer.TimerTrigger")); + assertThat(configuration.get("time"), is("12:12:00")); + assertThat(configuration.get("repeat"), is("-1")); + + // Recurring timer with random element + timeString = "R12/PT12:12:00A14:12:34"; + trigger = RuleUtils.createTriggerForTimeString(timeString); + configuration = trigger.getConfiguration(); + + assertThat(trigger.getTypeUID(), is("timer.TimerTrigger")); + assertThat(configuration.get("time"), is("12:12:00")); + assertThat(configuration.get("randomizeTime"), is("14:12:34")); + assertThat(configuration.get("repeat"), is("12")); + } + + @Test + public void triggerToTimestring() { + String timeString; + Trigger trigger; + Configuration configuration; + + // absolute time + configuration = new Configuration(); + configuration.put("date", "2020-02-01"); + configuration.put("time", "12:12:00"); + trigger = TriggerBuilder.create().withId("absolutetrigger").withTypeUID("timer.AbsoluteDateTimeTrigger") + .withConfiguration(configuration).build(); + timeString = RuleUtils.timeStringFromTrigger(Collections.singletonList(trigger)); + + assertThat(timeString, is("2020-02-01T12:12:00")); + + // absolute randomized time + configuration = new Configuration(); + configuration.put("date", "2020-02-01"); + configuration.put("time", "12:12:00"); + configuration.put("randomizeTime", "14:12:34"); + trigger = TriggerBuilder.create().withId("absolutetrigger").withTypeUID("timer.AbsoluteDateTimeTrigger") + .withConfiguration(configuration).build(); + timeString = RuleUtils.timeStringFromTrigger(Collections.singletonList(trigger)); + + assertThat(timeString, is("2020-02-01T12:12:00A14:12:34")); + + // Recurring times,Monday = 64, Tuesday = 32, Wednesday = 16, Thursday = 8, Friday = 4, Saturday = 2, Sunday= 1 + // Cron expression: min hour day month weekdays + configuration = new Configuration(); + configuration.put("cronExpression", "15 12 * * 6,7"); + trigger = TriggerBuilder.create().withId("crontrigger").withTypeUID("timer.GenericCronTrigger") + .withConfiguration(configuration).build(); + timeString = RuleUtils.timeStringFromTrigger(Collections.singletonList(trigger)); + + assertThat(timeString, is("W3/T12:15:00")); + + // Recurring randomized times (not possible, the cron rule has no way to store that info) + configuration = new Configuration(); + configuration.put("cronExpression", "15 14 * * 1,2,3,4,5,6,7"); + trigger = TriggerBuilder.create().withId("crontrigger").withTypeUID("timer.GenericCronTrigger") + .withConfiguration(configuration).build(); + timeString = RuleUtils.timeStringFromTrigger(Collections.singletonList(trigger)); + + assertThat(timeString, is("W127/T14:15:00")); + + // Timer, expiring after given time + configuration = new Configuration(); + configuration.put("time", "12:12:00"); + trigger = TriggerBuilder.create().withId("timertrigger").withTypeUID("timer.TimerTrigger") + .withConfiguration(configuration).build(); + timeString = RuleUtils.timeStringFromTrigger(Collections.singletonList(trigger)); + + assertThat(timeString, is("PT12:12:00")); + + // Timer with random element + configuration = new Configuration(); + configuration.put("time", "12:12:00"); + configuration.put("randomizeTime", "14:12:34"); + trigger = TriggerBuilder.create().withId("timertrigger").withTypeUID("timer.TimerTrigger") + .withConfiguration(configuration).build(); + timeString = RuleUtils.timeStringFromTrigger(Collections.singletonList(trigger)); + + assertThat(timeString, is("PT12:12:00A14:12:34")); + + // Timers, Recurring timer + configuration = new Configuration(); + configuration.put("time", "12:12:00"); + configuration.put("repeat", -1); + trigger = TriggerBuilder.create().withId("timertrigger").withTypeUID("timer.TimerTrigger") + .withConfiguration(configuration).build(); + timeString = RuleUtils.timeStringFromTrigger(Collections.singletonList(trigger)); + + assertThat(timeString, is("R/PT12:12:00")); + + // Recurring timer with random element + configuration = new Configuration(); + configuration.put("time", "12:12:00"); + configuration.put("randomizeTime", "14:12:34"); + configuration.put("repeat", 12); + trigger = TriggerBuilder.create().withId("timertrigger").withTypeUID("timer.TimerTrigger") + .withConfiguration(configuration).build(); + timeString = RuleUtils.timeStringFromTrigger(Collections.singletonList(trigger)); + + assertThat(timeString, is("R12/PT12:12:00A14:12:34")); + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/SensorTests.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/SensorTests.java new file mode 100644 index 0000000000000..4418ecaf0838b --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/SensorTests.java @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Response; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.items.GenericItem; +import org.eclipse.smarthome.core.items.ItemRegistry; +import org.eclipse.smarthome.core.library.items.ColorItem; +import org.eclipse.smarthome.core.library.items.ContactItem; +import org.eclipse.smarthome.core.library.items.DimmerItem; +import org.eclipse.smarthome.core.library.items.NumberItem; +import org.eclipse.smarthome.core.library.items.RollershutterItem; +import org.eclipse.smarthome.core.library.items.SwitchItem; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.HSBType; +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.OpenClosedType; +import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.types.State; +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.rest.mocks.DummyItemRegistry; + +/** + * Tests for {@link Sensors}. + * + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class SensorTests { + protected @NonNullByDefault({}) CommonSetup commonSetup; + protected @NonNullByDefault({}) ItemRegistry itemRegistry; + protected @NonNullByDefault({}) ConfigStore cs; + + Sensors subject = new Sensors(); + + private void addItemToReg(GenericItem item, State state, String label) { + item.setState(state); + item.setLabel(label); + itemRegistry.add(item); + } + + @Before + public void setUp() throws IOException { + commonSetup = new CommonSetup(false); + itemRegistry = new DummyItemRegistry(); + + this.cs = commonSetup.cs; + + subject.cs = cs; + subject.userManagement = commonSetup.userManagement; + subject.itemRegistry = itemRegistry; + subject.activate(); + + // Add simulated sensor items + addItemToReg(new SwitchItem("switch1"), OnOffType.ON, "name1"); + addItemToReg(new ContactItem("contact1"), OpenClosedType.OPEN, ""); + addItemToReg(new ColorItem("color1"), HSBType.BLUE, ""); + addItemToReg(new DimmerItem("white1"), new PercentType(12), ""); + addItemToReg(new RollershutterItem("roller1"), new PercentType(12), ""); + addItemToReg(new NumberItem("number1"), new DecimalType(12), ""); + + commonSetup.start(new ResourceConfig().registerInstances(subject)); + } + + @After + public void tearDown() { + commonSetup.dispose(); + } + + @Test + public void renameSensor() throws IOException { + + assertThat(cs.ds.sensors.get("switch1").name, is("name1")); + + String body = "{'name':'name2'}"; + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/sensors/switch1").request() + .put(Entity.json(body)); + assertEquals(200, response.getStatus()); + body = response.readEntity(String.class); + assertThat(body, containsString("success")); + assertThat(body, containsString("name")); + assertThat(cs.ds.sensors.get("switch1").name, is("name2")); + } + + @Test + public void allAndSingleSensor() throws InterruptedException, ExecutionException, TimeoutException, IOException { + Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/sensors").request().get(); + assertEquals(200, response.getStatus()); + + String body = response.readEntity(String.class); + + assertThat(body, containsString("switch1")); + assertThat(body, containsString("color1")); + assertThat(body, containsString("white1")); + + // Single light access test + response = commonSetup.client.target(commonSetup.basePath + "/testuser/sensors/switch1").request().get(); + assertEquals(200, response.getStatus()); + body = response.readEntity(String.class); + assertThat(body, containsString("CLIPGenericFlag")); + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/UsersAndConfigTests.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/UsersAndConfigTests.java new file mode 100644 index 0000000000000..12727fb1045a0 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/UsersAndConfigTests.java @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.Dictionary; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Response; + +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.openhab.io.hueemulation.internal.HueEmulationConfig; +import org.openhab.io.hueemulation.internal.dto.HueUnauthorizedConfig; +import org.openhab.io.hueemulation.internal.dto.response.HueResponse; +import org.openhab.io.hueemulation.internal.dto.response.HueSuccessResponseCreateUser; +import org.openhab.io.hueemulation.internal.rest.mocks.ConfigStoreWithoutMetadata; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +/** + * Tests for various user management API endpoints. + * + * @author David Graeff - Initial contribution + */ +public class UsersAndConfigTests { + + ConfigurationAccess configurationAccess = new ConfigurationAccess(); + + CommonSetup commonSetup; + + @Before + public void setUp() throws IOException { + commonSetup = new CommonSetup(false); + + configurationAccess.cs = commonSetup.cs; + configurationAccess.userManagement = commonSetup.userManagement; + configurationAccess.configAdmin = commonSetup.configAdmin; + + commonSetup.start(new ResourceConfig().registerInstances(configurationAccess)); + } + + @After + public void tearDown() { + commonSetup.dispose(); + } + + @Test + public void invalidUser() throws IOException { + assertFalse(commonSetup.userManagement.authorizeUser("blub")); + } + + @Test + public void validUser() throws IOException { + assertTrue(commonSetup.userManagement.authorizeUser("testuser")); + } + + @Test + public void configStoreRestartOnNoUUID() throws IOException { + ConfigStore configStore = new ConfigStoreWithoutMetadata(commonSetup.networkAddressService, + commonSetup.configAdmin, commonSetup.scheduler); + + // No uuid known yet + assertThat(configStore.ds.config.uuid, is("")); + configStore.activate(Collections.emptyMap()); + + assertThat(configStore.getConfig().uuid, not(is(""))); + // The config admin service was requested for the service config + Mockito.verify(commonSetup.configAdminConfig).getProperties(); + Dictionary p = commonSetup.configAdminConfig.getProperties(); + // And the service config was updated + assertThat(p.get(HueEmulationConfig.CONFIG_UUID), is(configStore.getConfig().uuid)); + } + + @Test + public void addUser() throws IOException { + // GET should fail + assertEquals(405, commonSetup.client.target(commonSetup.basePath).request().get().getStatus()); + + String body = "{'username':'testuser','devicetype':'app#device'}"; + + Response response; + HueResponse[] r; + + // Post should create a user, except: if linkbutton not enabled + response = commonSetup.client.target(commonSetup.basePath).request().post(Entity.json(body)); + assertThat(response.getStatus(), is(200)); + r = commonSetup.cs.gson.fromJson(response.readEntity(String.class), HueResponse[].class); + assertNotNull(r[0].error); + + // Post should create a user + commonSetup.cs.ds.config.linkbutton = true; + response = commonSetup.client.target(commonSetup.basePath).request().post(Entity.json(body)); + assertThat(response.getStatus(), is(200)); + + JsonElement e = new JsonParser().parse(response.readEntity(String.class)).getAsJsonArray().get(0); + e = e.getAsJsonObject().get("success"); + HueSuccessResponseCreateUser rc = commonSetup.cs.gson.fromJson(e, HueSuccessResponseCreateUser.class); + assertNotNull(rc); + assertThat(commonSetup.cs.ds.config.whitelist.get(rc.username).name, is("app#device")); + } + + @Test + public void UnauthorizedAccessTest() + throws InterruptedException, ExecutionException, TimeoutException, IOException { + + // Unauthorized config + Response response; + response = commonSetup.client.target(commonSetup.basePath + "/config").request().get(); + assertThat(response.getStatus(), is(200)); + HueUnauthorizedConfig config = new Gson().fromJson(response.readEntity(String.class), + HueUnauthorizedConfig.class); + assertThat(config.bridgeid, is(commonSetup.cs.ds.config.bridgeid)); + assertThat(config.name, is(commonSetup.cs.ds.config.name)); + + // Invalid user name + response = commonSetup.client.target(commonSetup.basePath + "/invalid/config").request().get(); + assertThat(response.getStatus(), is(403)); + assertThat(response.readEntity(String.class), containsString("error")); + } + +} diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/ConfigStoreWithoutMetadata.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/ConfigStoreWithoutMetadata.java new file mode 100644 index 0000000000000..1e7f4328286bd --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/ConfigStoreWithoutMetadata.java @@ -0,0 +1,36 @@ +package org.openhab.io.hueemulation.internal.rest.mocks; + +import java.util.concurrent.ScheduledExecutorService; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.net.NetworkAddressService; +import org.openhab.io.hueemulation.internal.ConfigStore; +import org.osgi.service.cm.ConfigurationAdmin; + +/** + * Usually the metadata registry is used to map item UIDs to integer hue IDs. + * For tests we do not need this extra complexity and just map the item UID to the hue ID. + * + * @author David Graeff - Initial contribution + */ +public class ConfigStoreWithoutMetadata extends ConfigStore { + + public ConfigStoreWithoutMetadata(NetworkAddressService networkAddressService, ConfigurationAdmin configAdmin, + ScheduledExecutorService scheduler) { + super(networkAddressService, configAdmin, null, scheduler); + } + + @Override + protected void determineHighestAssignedHueID() { + } + + @Override + public @NonNull String mapItemUIDtoHueID(@Nullable Item item) { + if (item == null) { + throw new IllegalArgumentException(); + } + return item.getUID(); + } +} \ No newline at end of file diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/DummyItemRegistry.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/DummyItemRegistry.java new file mode 100644 index 0000000000000..aa7c366b49648 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/DummyItemRegistry.java @@ -0,0 +1,156 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest.mocks; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.common.registry.RegistryChangeListener; +import org.eclipse.smarthome.core.items.Item; +import org.eclipse.smarthome.core.items.ItemNotFoundException; +import org.eclipse.smarthome.core.items.ItemNotUniqueException; +import org.eclipse.smarthome.core.items.ItemRegistry; +import org.eclipse.smarthome.core.items.RegistryHook; + +/** + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class DummyItemRegistry implements ItemRegistry { + Map items = new TreeMap<>(); + List> listeners = new ArrayList<>(); + + @Override + public void addRegistryChangeListener(RegistryChangeListener listener) { + listeners.add(listener); + } + + @Override + public Collection getAll() { + return items.values(); + } + + @NonNullByDefault({}) + @Override + public Stream stream() { + return items.values().stream(); + } + + @Override + public @Nullable Item get(@Nullable String key) { + return items.get(key); + } + + @Override + public void removeRegistryChangeListener(RegistryChangeListener listener) { + listeners.remove(listener); + } + + @Override + public Item add(Item element) { + Item put = items.put(element.getUID(), element); + for (RegistryChangeListener l : listeners) { + l.added(element); + } + return put; + } + + @Override + public @Nullable Item update(Item element) { + Item put = items.put(element.getUID(), element); + for (RegistryChangeListener l : listeners) { + l.updated(put, element); + } + return put; + } + + @Override + public @Nullable Item remove(String key) { + Item put = items.remove(key); + for (RegistryChangeListener l : listeners) { + l.removed(put); + } + return put; + } + + @Override + public Item getItem(@Nullable String name) throws ItemNotFoundException { + return items.get(name); + } + + @Override + public Item getItemByPattern(String name) throws ItemNotFoundException, ItemNotUniqueException { + return items.get(name); + } + + @Override + public Collection getItems() { + return items.values(); + } + + @Override + public Collection getItemsOfType(String type) { + return items.values(); + } + + @Override + public Collection getItems(String pattern) { + return items.values(); + } + + @NonNullByDefault({}) + @Override + public Collection getItemsByTag(String... tags) { + return items.values(); + } + + @NonNullByDefault({}) + @Override + public Collection getItemsByTagAndType(String type, String... tags) { + return items.values(); + } + + @NonNullByDefault({}) + @SuppressWarnings("unchecked") + @Override + public Collection getItemsByTag(Class typeFilter, String... tags) { + return (Collection) items.values(); + } + + @Override + public @Nullable Item remove(String itemName, boolean recursive) { + Item put = items.remove(itemName); + for (RegistryChangeListener l : listeners) { + l.removed(put); + } + return put; + } + + @NonNullByDefault({}) + @Override + public void addRegistryHook(RegistryHook hook) { + + } + + @NonNullByDefault({}) + @Override + public void removeRegistryHook(RegistryHook hook) { + + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/DummyMetadataRegistry.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/DummyMetadataRegistry.java new file mode 100644 index 0000000000000..ea582e256adc5 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/DummyMetadataRegistry.java @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest.mocks; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.common.registry.RegistryChangeListener; +import org.eclipse.smarthome.core.items.Metadata; +import org.eclipse.smarthome.core.items.MetadataKey; +import org.eclipse.smarthome.core.items.MetadataRegistry; + +/** + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class DummyMetadataRegistry implements MetadataRegistry { + Map items = new HashMap<>(); + List> listeners = new ArrayList<>(); + + @Override + public void addRegistryChangeListener(RegistryChangeListener listener) { + listeners.add(listener); + } + + @Override + public Collection getAll() { + return items.values(); + } + + @Override + public Stream stream() { + return items.values().stream(); + } + + @Override + public @Nullable Metadata get(@Nullable MetadataKey key) { + return items.get(key); + } + + @Override + public void removeRegistryChangeListener(RegistryChangeListener listener) { + listeners.remove(listener); + } + + @Override + public Metadata add(Metadata element) { + Metadata put = items.put(element.getUID(), element); + for (RegistryChangeListener l : listeners) { + l.added(element); + } + return put; + } + + @Override + public @Nullable Metadata update(Metadata element) { + Metadata put = items.put(element.getUID(), element); + for (RegistryChangeListener l : listeners) { + l.updated(put, element); + } + return put; + } + + @Override + public @Nullable Metadata remove(MetadataKey key) { + Metadata put = items.remove(key); + for (RegistryChangeListener l : listeners) { + l.removed(put); + } + return put; + } + + @Override + public boolean isInternalNamespace(String namespace) { + return false; + } + + @Override + public void removeItemMetadata(String name) { + + } + +} diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/DummyRuleRegistry.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/DummyRuleRegistry.java new file mode 100644 index 0000000000000..6e346e9701814 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/DummyRuleRegistry.java @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest.mocks; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.common.registry.RegistryChangeListener; +import org.openhab.core.automation.Rule; +import org.openhab.core.automation.RuleRegistry; + +/** + * @author David Graeff - Initial contribution + */ +@NonNullByDefault +public class DummyRuleRegistry implements RuleRegistry { + Map items = new HashMap<>(); + List> listeners = new ArrayList<>(); + + @Override + public void addRegistryChangeListener(RegistryChangeListener listener) { + listeners.add(listener); + } + + @Override + public Collection getAll() { + return items.values(); + } + + @NonNullByDefault({}) + @Override + public Stream stream() { + return items.values().stream(); + } + + @Override + public @Nullable Rule get(@Nullable String key) { + return items.get(key); + } + + @Override + public void removeRegistryChangeListener(RegistryChangeListener listener) { + listeners.remove(listener); + } + + @Override + public Rule add(Rule element) { + Rule put = items.put(element.getUID(), element); + for (RegistryChangeListener l : listeners) { + l.added(element); + } + return put; + } + + @Override + public @Nullable Rule update(Rule element) { + Rule put = items.put(element.getUID(), element); + for (RegistryChangeListener l : listeners) { + l.updated(put, element); + } + return put; + } + + @Override + public @Nullable Rule remove(String key) { + Rule put = items.remove(key); + for (RegistryChangeListener l : listeners) { + l.removed(put); + } + return put; + } + + @NonNullByDefault({}) + @Override + public Collection getByTag(String tag) { + return Collections.emptyList(); + } + + @NonNullByDefault({}) + @Override + public Collection getByTags(String... tags) { + return Collections.emptyList(); + } +} diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/DummyUsersStorage.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/DummyUsersStorage.java new file mode 100644 index 0000000000000..3c7d95f01b841 --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/rest/mocks/DummyUsersStorage.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.rest.mocks; + +import java.util.Collection; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.storage.Storage; +import org.openhab.io.hueemulation.internal.dto.HueUserAuthWithSecrets; + +/** + * @author David Graeff - Initial contribution + */ +public class DummyUsersStorage implements Storage { + Map users = new TreeMap<>(); + + public DummyUsersStorage() { + users.put("testuser", new HueUserAuthWithSecrets("appname", "devicename", "testuser", "clientkey")); + } + + @Override + public @Nullable HueUserAuthWithSecrets put(String key, @Nullable HueUserAuthWithSecrets value) { + return users.put(key, value); + } + + @Override + public @Nullable HueUserAuthWithSecrets remove(String key) { + return users.remove(key); + } + + @Override + public boolean containsKey(String key) { + return users.containsKey(key); + } + + @Override + public @Nullable HueUserAuthWithSecrets get(String key) { + return users.get(key); + } + + @Override + public Collection<@NonNull String> getKeys() { + return users.keySet(); + } + + @Override + public Collection<@Nullable HueUserAuthWithSecrets> getValues() { + return users.values(); + } + +} diff --git a/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/upnp/UpnpTests.java b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/upnp/UpnpTests.java new file mode 100644 index 0000000000000..563f6f650ecca --- /dev/null +++ b/bundles/org.openhab.io.hueemulation/src/test/java/org/openhab/io/hueemulation/internal/upnp/UpnpTests.java @@ -0,0 +1,149 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal.upnp; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import javax.ws.rs.core.Response; + +import org.glassfish.grizzly.osgi.httpservice.HttpServiceImpl; +import org.glassfish.grizzly.osgi.httpservice.OSGiMainHandler; +import org.glassfish.grizzly.osgi.httpservice.util.Logger; +import org.glassfish.jersey.server.ResourceConfig; +import org.hamcrest.CoreMatchers; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; +import org.openhab.io.hueemulation.internal.rest.CommonSetup; +import org.openhab.io.hueemulation.internal.rest.LightsAndGroups; +import org.osgi.framework.Bundle; +import org.osgi.util.tracker.ServiceTracker; + +/** + * Tests the upnp server part if the description.xml is available and if the udp thread comes online + * + * @author David Graeff - Initial contribution + */ +public class UpnpTests { + protected static CommonSetup commonSetup = null; + protected UpnpServer subject; + protected static OSGiMainHandler mainHttpHandler; + private static HttpServiceImpl httpServiceImpl; + + LightsAndGroups lightsAndGroups = new LightsAndGroups(); + + @BeforeClass + public static void setupHttpParts() throws IOException { + commonSetup = new CommonSetup(true); + commonSetup.start(new ResourceConfig()); + + Logger logger = new org.glassfish.grizzly.osgi.httpservice.util.Logger(mock(ServiceTracker.class)); + + mainHttpHandler = new OSGiMainHandler(logger, mock(Bundle.class)); + commonSetup.server.getServerConfiguration().addHttpHandler(mainHttpHandler, "/"); + + httpServiceImpl = new HttpServiceImpl(mock(Bundle.class), logger); + } + + @Before + public void setup() { + Executor executor = mock(Executor.class); + Mockito.doAnswer(a -> { + ((Runnable) a.getArgument(0)).run(); + return null; + }).when(executor).execute(ArgumentMatchers.any()); + subject = new UpnpServer(executor); + subject.httpService = httpServiceImpl; + subject.cs = commonSetup.cs; + subject.overwriteReadyToFalse = true; + subject.activate(); // don't execute handleEvent() + subject.overwriteReadyToFalse = false; + } + + @After + public void tearDown() { + subject.deactivate(); + } + + @AfterClass + public static void tearDownHttp() { + mainHttpHandler.unregisterAll(); + commonSetup.dispose(); + } + + @Test + public void descriptionWithoutAddress() { + Response response = commonSetup.client.target("http://localhost:8080/description.xml").request().get(); + assertEquals(404, response.getStatus()); + } + + @Test + public void descriptionWithAddress() + throws InterruptedException, ExecutionException, TimeoutException, IOException { + HueEmulationConfigWithRuntime r = subject.createConfiguration(null); + r = subject.performAddressTest(r); + subject.applyConfiguration(r); + Response response = commonSetup.client.target("http://localhost:8080/description.xml").request().get(); + assertEquals(200, response.getStatus()); + String body = response.readEntity(String.class); + assertThat(body, is(subject.xmlDocWithAddress)); + + if (r == null) { + throw new IllegalStateException(); + } + + // UDP thread started? + r.startNow(r).get(5, TimeUnit.SECONDS); + assertThat(subject.upnpAnnouncementThreadRunning(), is(true)); + + // Send M-SEARCH UPNP "packet" and check if the result contains our bridge ID + try (DatagramSocket sendSocket = new DatagramSocket()) { + sendSocket.setSoTimeout(700); + byte[] bytes = "M-SEARCH".getBytes(); + sendSocket.send(new DatagramPacket(bytes, bytes.length, subject.MULTI_ADDR_IPV4, UpnpServer.UPNP_PORT)); + byte[] buffer = new byte[1000]; + DatagramPacket p = new DatagramPacket(buffer, buffer.length); + sendSocket.receive(p); + String received = new String(buffer); + assertThat(received, CoreMatchers.startsWith("HTTP/1.1 200 OK")); + assertThat(received, CoreMatchers.containsString("hue-bridgeid: DEMOUUID")); + } + + r.dispose(); + assertThat(subject.upnpAnnouncementThreadRunning(), is(false)); + } + + @Test + public void handEventTest() throws InterruptedException, ExecutionException, TimeoutException { + subject.handleEvent(null); + subject.configChangeFuture.get(5, TimeUnit.SECONDS); + assertThat(subject.upnpAnnouncementThreadRunning(), is(true)); + + subject.deactivate(); + assertThat(subject.upnpAnnouncementThreadRunning(), is(false)); + } +} diff --git a/bundles/org.openhab.io.neeo/src/main/java/org/openhab/io/neeo/internal/servletservices/NeeoBrainService.java b/bundles/org.openhab.io.neeo/src/main/java/org/openhab/io/neeo/internal/servletservices/NeeoBrainService.java index 97ea0f11045e8..25a7488f649fa 100644 --- a/bundles/org.openhab.io.neeo/src/main/java/org/openhab/io/neeo/internal/servletservices/NeeoBrainService.java +++ b/bundles/org.openhab.io.neeo/src/main/java/org/openhab/io/neeo/internal/servletservices/NeeoBrainService.java @@ -333,7 +333,7 @@ private void handleUnsubscribe(HttpServletResponse resp, String adapterName) thr api.getDeviceKeys().remove(uid); NeeoUtil.write(resp, gson.toJson(ReturnStatus.SUCCESS)); } catch (IllegalArgumentException e) { - logger.debug("AdapterName {} is not a valid thinguid - ignoring"); + logger.debug("AdapterName {} is not a valid thinguid - ignoring", adapterName); NeeoUtil.write(resp, gson.toJson(new ReturnStatus("AdapterName not a valid ThingUID: " + adapterName))); } } @@ -358,7 +358,7 @@ private void handleSubscribe(HttpServletResponse resp, String adapterName, Strin api.getDeviceKeys().put(uid, deviceKey); NeeoUtil.write(resp, gson.toJson(ReturnStatus.SUCCESS)); } catch (IllegalArgumentException e) { - logger.debug("AdapterName {} is not a valid thinguid - ignoring"); + logger.debug("AdapterName {} is not a valid thinguid - ignoring", adapterName); NeeoUtil.write(resp, gson.toJson(new ReturnStatus("AdapterName not a valid ThingUID: " + adapterName))); } } @@ -377,7 +377,7 @@ private void handleDirectory(HttpServletRequest req, HttpServletResponse resp, P Objects.requireNonNull(resp, "resp cannot be null"); Objects.requireNonNull(pathInfo, "pathInfo cannot be null"); - logger.debug("handleDirectory{}: {}", pathInfo); + logger.debug("handleDirectory {}", pathInfo); final NeeoDevice device = context.getDefinitions().getDevice(pathInfo.getThingUid()); if (device != null) { diff --git a/bundles/org.openhab.io.openhabcloud/.classpath b/bundles/org.openhab.io.openhabcloud/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.io.openhabcloud/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.io.openhabcloud/.project b/bundles/org.openhab.io.openhabcloud/.project new file mode 100644 index 0000000000000..d2e586f58e69e --- /dev/null +++ b/bundles/org.openhab.io.openhabcloud/.project @@ -0,0 +1,23 @@ + + + org.openhab.io.openhabcloud + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/io/org.openhab.io.openhabcloud/NOTICE b/bundles/org.openhab.io.openhabcloud/NOTICE similarity index 100% rename from addons/io/org.openhab.io.openhabcloud/NOTICE rename to bundles/org.openhab.io.openhabcloud/NOTICE diff --git a/bundles/org.openhab.io.openhabcloud/README.md b/bundles/org.openhab.io.openhabcloud/README.md new file mode 100644 index 0000000000000..22cac3fbcb31c --- /dev/null +++ b/bundles/org.openhab.io.openhabcloud/README.md @@ -0,0 +1,65 @@ +# openHAB Cloud Connector + +The openHAB Cloud Connector allows connecting the local openHAB runtime to a remote [openHAB Cloud](https://github.com/openhab/openhab-cloud/blob/master/README.md) instance, such as [myopenHAB.org](http://www.myopenHAB.org), which is an instance of the +openHAB Cloud service hosted by the [openHAB Foundation](http://www.openhabfoundation.org/). + +## Features + +The openHAB Cloud service (and thus the connector to it) is useful for different use cases: + +* It allows remote access to local openHAB instances without having to expose ports to the Internet or to require a complex VPN setup. +* It serves as a connector to Google Cloud Messaging (GCM) and Apple Push Notifications (APN) for pushing notifications to mobile phone apps. +* It brings integration possibilities with services that require an OAuth2 authentication against a web server, such as IFTTT or Amazon Alexa Skills. + +## UUID and Secret + +To authenticate with the openHAB Cloud service the add-on generates two values when the add-on is installed. +These values need to be entered in your account settings of the openHAB Cloud service. +The first one is a unique identifier, which allows to identify your runtime. +One can think of it as something similar like a username for the cloud authentication. +The second one is a random secret key which serves as a password. +Both values are written to the local file system. +If you loose these files for some reason, openHAB will automatically generate new ones. +You will then have to reconfigure UUID and secret in the openHAB Cloud service under the _My account_ section. + +Location of UUID and Secret: + +| File | Regular Installation | APT Installation | +|--------|------------------------------|---------------------------------------| +| UUID | userdata/uuid | /var/lib/openhab2/uuid | +| Secret | userdata/openhabcloud/secret | /var/lib/openhab2/openhabcloud/secret | + +## Configuration + +After installing this add-on, you will find configuration options in the Paper UI under _Configuration->Services->IO->openHAB Cloud_: + +![Configuration](contrib/doc/cfg.png) + +Please note, that you don't need to expose all your items in this settings dialog. +You just need to expose those items, which you want to be accessible by a third party service. + +Alternatively, you can configure the settings in the file `conf/services/openhabcloud.cfg`: + +``` +############################## openHAB Cloud Connector ############################# + +# The URL of the openHAB Cloud service to connect to. +# Optional, default is set to the service offered by the openHAB Foundation +# (https://myopenhab.org/) +#baseURL= + +# Defines the mode in which you want to operate the connector. +# Possible values are: +# - notification: Only push notifications are enabled, no remote access is allowed. +# - remote: Push notifications and remote access are enabled. +# Optional, default is 'remote'. +#mode= + +# A comma-separated list of items to be exposed to external services like IFTTT. +# Events of those items are pushed to the openHAB Cloud and commands received for +# these items from the openHAB Cloud service are accepted and sent to the local bus. +# Optional, default is an empty list. +#expose= +``` + +Note: The exposed items will show up after they receive an update to their state. diff --git a/addons/io/org.openhab.io.openhabcloud/doc/cfg.png b/bundles/org.openhab.io.openhabcloud/contrib/doc/cfg.png similarity index 100% rename from addons/io/org.openhab.io.openhabcloud/doc/cfg.png rename to bundles/org.openhab.io.openhabcloud/contrib/doc/cfg.png diff --git a/bundles/org.openhab.io.openhabcloud/pom.xml b/bundles/org.openhab.io.openhabcloud/pom.xml new file mode 100644 index 0000000000000..8f9e9eedb30bc --- /dev/null +++ b/bundles/org.openhab.io.openhabcloud/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + + org.openhab.addons.reactor.bundles + org.openhab.addons.bundles + 2.5.0-SNAPSHOT + + + org.openhab.io.openhabcloud + + openHAB Add-ons :: Bundles :: openHAB Cloud Connector + + + + org.json + json + 20180813 + provided + + + com.squareup.okhttp3 + okhttp + 3.12.1 + provided + + + com.squareup.okio + okio + 1.15.0 + provided + + + io.socket + socket.io-client + 1.0.0 + provided + + + io.socket + engine.io-client + 1.0.0 + provided + + + + diff --git a/addons/io/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/NotificationAction.java b/bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/NotificationAction.java similarity index 100% rename from addons/io/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/NotificationAction.java rename to bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/NotificationAction.java diff --git a/addons/io/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudClient.java b/bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudClient.java similarity index 100% rename from addons/io/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudClient.java rename to bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudClient.java diff --git a/addons/io/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudClientListener.java b/bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudClientListener.java similarity index 100% rename from addons/io/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudClientListener.java rename to bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudClientListener.java diff --git a/addons/io/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudService.java b/bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudService.java similarity index 93% rename from addons/io/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudService.java rename to bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudService.java index 10a325bccd3a6..f478f211bad82 100644 --- a/addons/io/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudService.java +++ b/bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudService.java @@ -17,6 +17,8 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -99,9 +101,9 @@ public CloudService() { /** * This method sends notification message to mobile app through the openHAB Cloud service * - * @param userId the {@link String} containing the openHAB Cloud user id to send message to - * @param message the {@link String} containing a message to send to specified user id - * @param icon the {@link String} containing a name of the icon to be used with this notification + * @param userId the {@link String} containing the openHAB Cloud user id to send message to + * @param message the {@link String} containing a message to send to specified user id + * @param icon the {@link String} containing a name of the icon to be used with this notification * @param severity the {@link String} containing severity (good, info, warning, error) of notification */ public void sendNotification(String userId, String message, String icon, String severity) { @@ -113,8 +115,8 @@ public void sendNotification(String userId, String message, String icon, String * Sends an advanced notification to log. Log notifications are not pushed to user * devices but are shown to all account users in notifications log * - * @param message the {@link String} containing a message to send to specified user id - * @param icon the {@link String} containing a name of the icon to be used with this notification + * @param message the {@link String} containing a message to send to specified user id + * @param icon the {@link String} containing a name of the icon to be used with this notification * @param severity the {@link String} containing severity (good, info, warning, error) of notification */ public void sendLogNotification(String message, String icon, String severity) { @@ -126,8 +128,8 @@ public void sendLogNotification(String message, String icon, String severity) { * Sends a broadcast notification. Broadcast notifications are pushed to all * mobile devices of all users of the account * - * @param message the {@link String} containing a message to send to specified user id - * @param icon the {@link String} containing a name of the icon to be used with this notification + * @param message the {@link String} containing a message to send to specified user id + * @param icon the {@link String} containing a name of the icon to be used with this notification * @param severity the {@link String} containing severity (good, info, warning, error) of notification */ public void sendBroadcastNotification(String message, String icon, String severity) { @@ -236,8 +238,8 @@ public Class getActionClass() { private String readFirstLine(File file) { List lines = null; - try { - lines = IOUtils.readLines(new FileInputStream(file)); + try (InputStream fis = new FileInputStream(file)) { + lines = IOUtils.readLines(fis); } catch (IOException ioe) { // no exception handling - we just return the empty String } @@ -251,8 +253,8 @@ private String readFirstLine(File file) { private void writeFile(File file, String content) { // create intermediary directories file.getParentFile().mkdirs(); - try { - IOUtils.write(content, new FileOutputStream(file)); + try (OutputStream fos = new FileOutputStream(file)) { + IOUtils.write(content, fos); logger.debug("Created file '{}' with content '{}'", file.getAbsolutePath(), content); } catch (FileNotFoundException e) { logger.error("Couldn't create file '{}'.", file.getPath(), e); diff --git a/addons/io/org.openhab.io.openhabcloud/ESH-INF/config/config.xml b/bundles/org.openhab.io.openhabcloud/src/main/resources/ESH-INF/config/config.xml similarity index 100% rename from addons/io/org.openhab.io.openhabcloud/ESH-INF/config/config.xml rename to bundles/org.openhab.io.openhabcloud/src/main/resources/ESH-INF/config/config.xml diff --git a/bundles/org.openhab.io.transport.modbus/.classpath b/bundles/org.openhab.io.transport.modbus/.classpath new file mode 100644 index 0000000000000..5e8a55fefbd91 --- /dev/null +++ b/bundles/org.openhab.io.transport.modbus/.classpath @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/addons/binding/org.openhab.binding.modbus/.gitignore b/bundles/org.openhab.io.transport.modbus/.gitignore similarity index 100% rename from addons/binding/org.openhab.binding.modbus/.gitignore rename to bundles/org.openhab.io.transport.modbus/.gitignore diff --git a/bundles/org.openhab.io.transport.modbus/.project b/bundles/org.openhab.io.transport.modbus/.project new file mode 100644 index 0000000000000..94acf165b6f73 --- /dev/null +++ b/bundles/org.openhab.io.transport.modbus/.project @@ -0,0 +1,23 @@ + + + org.openhab.io.transport.modbus + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.onewiregpio/NOTICE b/bundles/org.openhab.io.transport.modbus/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.onewiregpio/NOTICE rename to bundles/org.openhab.io.transport.modbus/NOTICE diff --git a/addons/io/org.openhab.io.transport.modbus/README.md b/bundles/org.openhab.io.transport.modbus/README.md similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/README.md rename to bundles/org.openhab.io.transport.modbus/README.md diff --git a/bundles/org.openhab.io.transport.modbus/pom.xml b/bundles/org.openhab.io.transport.modbus/pom.xml new file mode 100644 index 0000000000000..9b8330f8f0fa1 --- /dev/null +++ b/bundles/org.openhab.io.transport.modbus/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.io.transport.modbus + + openHAB Add-ons :: Bundles :: Modbus Transport + + + + org.apache.commons + commons-pool2 + 2.4.3 + provided + + + net.wimpi + jamod + 1.2.3.OH + provided + + + + diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicBitArray.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicBitArray.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicBitArray.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicBitArray.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusReadRequestBlueprint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusReadRequestBlueprint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusReadRequestBlueprint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusReadRequestBlueprint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusRegister.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusRegister.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusRegister.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusRegister.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusRegisterArray.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusRegisterArray.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusRegisterArray.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusRegisterArray.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusWriteCoilRequestBlueprint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusWriteCoilRequestBlueprint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusWriteCoilRequestBlueprint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusWriteCoilRequestBlueprint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusWriteRegisterRequestBlueprint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusWriteRegisterRequestBlueprint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusWriteRegisterRequestBlueprint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicModbusWriteRegisterRequestBlueprint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicPollTaskImpl.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicPollTaskImpl.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicPollTaskImpl.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicPollTaskImpl.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicWriteTask.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicWriteTask.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicWriteTask.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BasicWriteTask.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BitArray.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BitArray.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BitArray.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/BitArray.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusBitUtilities.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusBitUtilities.java similarity index 88% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusBitUtilities.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusBitUtilities.java index d12372459adb9..1c02e9446b5bf 100644 --- a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusBitUtilities.java +++ b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusBitUtilities.java @@ -71,6 +71,7 @@ public class ModbusBitUtilities { * - registers (index) and (index + 1) are interpreted as signed 32bit floating point number. * - it assumed that the first register contains the most significant 16 bits * - it is assumed that each register is encoded in most significant bit first order + * - floating point NaN and infinity will return as empty optional * FLOAT32_SWAP: * - Same as FLOAT32 but registers swapped * INT64: @@ -87,16 +88,18 @@ public class ModbusBitUtilities { * * @param registers list of registers, each register represent 16bit of data * @param index zero based item index. Interpretation of this depends on type, see examples above. - * With type larger or equal to 16 bits, the index tells the register index to start reading from. + * With type larger or equal to 16 bits, the index tells the register index to start reading + * from. * With type less than 16 bits, the index tells the N'th item to read from the registers. * @param type item type, e.g. unsigned 16bit integer (ModbusBindingProvider.ValueType.UINT16) - * @return number representation queried value + * @return number representation queried value, DecimalType. Empty optional is returned + * with NaN and infinity floating point values * @throws NotImplementedException in cases where implementation is lacking for the type. This can be considered a * bug * @throws IllegalArgumentException when index is out of bounds of registers * */ - public static DecimalType extractStateFromRegisters(ModbusRegisterArray registers, int index, + public static Optional extractStateFromRegisters(ModbusRegisterArray registers, int index, ModbusConstants.ValueType type) { int endBitIndex = (type.getBits() >= 16 ? 16 * index : type.getBits() * index) + type.getBits() - 1; // each register has 16 bits @@ -108,37 +111,43 @@ public static DecimalType extractStateFromRegisters(ModbusRegisterArray register } switch (type) { case BIT: - return new DecimalType((registers.getRegister(index / 16).toUnsignedShort() >> (index % 16)) & 1); + return Optional + .of(new DecimalType((registers.getRegister(index / 16).toUnsignedShort() >> (index % 16)) & 1)); case INT8: - return new DecimalType(registers.getRegister(index / 2).getBytes()[1 - (index % 2)]); + return Optional.of(new DecimalType(registers.getRegister(index / 2).getBytes()[1 - (index % 2)])); case UINT8: - return new DecimalType( - (registers.getRegister(index / 2).toUnsignedShort() >> (8 * (index % 2))) & 0xff); + return Optional.of(new DecimalType( + (registers.getRegister(index / 2).toUnsignedShort() >> (8 * (index % 2))) & 0xff)); case INT16: { ByteBuffer buff = ByteBuffer.allocate(2); buff.put(registers.getRegister(index).getBytes()); - return new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getShort(0)); + return Optional.of(new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getShort(0))); } case UINT16: - return new DecimalType(registers.getRegister(index).toUnsignedShort()); + return Optional.of(new DecimalType(registers.getRegister(index).toUnsignedShort())); case INT32: { ByteBuffer buff = ByteBuffer.allocate(4); buff.put(registers.getRegister(index).getBytes()); buff.put(registers.getRegister(index + 1).getBytes()); - return new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getInt(0)); + return Optional.of(new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getInt(0))); } case UINT32: { ByteBuffer buff = ByteBuffer.allocate(8); buff.position(4); buff.put(registers.getRegister(index).getBytes()); buff.put(registers.getRegister(index + 1).getBytes()); - return new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getLong(0)); + return Optional.of(new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getLong(0))); } case FLOAT32: { ByteBuffer buff = ByteBuffer.allocate(4); buff.put(registers.getRegister(index).getBytes()); buff.put(registers.getRegister(index + 1).getBytes()); - return new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getFloat(0)); + try { + return Optional.of(new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getFloat(0))); + } catch (NumberFormatException e) { + // floating point NaN or infinity encountered + return Optional.empty(); + } } case INT64: { ByteBuffer buff = ByteBuffer.allocate(8); @@ -146,7 +155,7 @@ public static DecimalType extractStateFromRegisters(ModbusRegisterArray register buff.put(registers.getRegister(index + 1).getBytes()); buff.put(registers.getRegister(index + 2).getBytes()); buff.put(registers.getRegister(index + 3).getBytes()); - return new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getLong(0)); + return Optional.of(new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getLong(0))); } case UINT64: { ByteBuffer buff = ByteBuffer.allocate(8); @@ -154,26 +163,32 @@ public static DecimalType extractStateFromRegisters(ModbusRegisterArray register buff.put(registers.getRegister(index + 1).getBytes()); buff.put(registers.getRegister(index + 2).getBytes()); buff.put(registers.getRegister(index + 3).getBytes()); - return new DecimalType(new BigDecimal(new BigInteger(1, buff.order(ByteOrder.BIG_ENDIAN).array()))); + return Optional.of( + new DecimalType(new BigDecimal(new BigInteger(1, buff.order(ByteOrder.BIG_ENDIAN).array())))); } case INT32_SWAP: { ByteBuffer buff = ByteBuffer.allocate(4); buff.put(registers.getRegister(index + 1).getBytes()); buff.put(registers.getRegister(index).getBytes()); - return new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getInt(0)); + return Optional.of(new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getInt(0))); } case UINT32_SWAP: { ByteBuffer buff = ByteBuffer.allocate(8); buff.position(4); buff.put(registers.getRegister(index + 1).getBytes()); buff.put(registers.getRegister(index).getBytes()); - return new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getLong(0)); + return Optional.of(new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getLong(0))); } case FLOAT32_SWAP: { ByteBuffer buff = ByteBuffer.allocate(4); buff.put(registers.getRegister(index + 1).getBytes()); buff.put(registers.getRegister(index).getBytes()); - return new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getFloat(0)); + try { + return Optional.of(new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getFloat(0))); + } catch (NumberFormatException e) { + // floating point NaN or infinity encountered + return Optional.empty(); + } } case INT64_SWAP: { ByteBuffer buff = ByteBuffer.allocate(8); @@ -181,7 +196,7 @@ public static DecimalType extractStateFromRegisters(ModbusRegisterArray register buff.put(registers.getRegister(index + 2).getBytes()); buff.put(registers.getRegister(index + 1).getBytes()); buff.put(registers.getRegister(index).getBytes()); - return new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getLong(0)); + return Optional.of(new DecimalType(buff.order(ByteOrder.BIG_ENDIAN).getLong(0))); } case UINT64_SWAP: { ByteBuffer buff = ByteBuffer.allocate(8); @@ -189,7 +204,8 @@ public static DecimalType extractStateFromRegisters(ModbusRegisterArray register buff.put(registers.getRegister(index + 2).getBytes()); buff.put(registers.getRegister(index + 1).getBytes()); buff.put(registers.getRegister(index).getBytes()); - return new DecimalType(new BigDecimal(new BigInteger(1, buff.order(ByteOrder.BIG_ENDIAN).array()))); + return Optional.of( + new DecimalType(new BigDecimal(new BigInteger(1, buff.order(ByteOrder.BIG_ENDIAN).array())))); } default: throw new IllegalArgumentException(type.getConfigValue()); diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusCallback.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusCallback.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusCallback.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusCallback.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusConnectionException.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusConnectionException.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusConnectionException.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusConnectionException.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusConstants.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusConstants.java similarity index 83% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusConstants.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusConstants.java index 0461faf1b23b0..a7b265abeb1e0 100644 --- a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusConstants.java +++ b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusConstants.java @@ -34,22 +34,22 @@ public class ModbusConstants { * */ public static enum ValueType { - BIT("bit", 1), - INT8("int8", 8), - UINT8("uint8", 8), - INT16("int16", 16), - UINT16("uint16", 16), - INT32("int32", 32), - UINT32("uint32", 32), - FLOAT32("float32", 32), - INT64("int64", 64), - UINT64("uint64", 64), + BIT("bit", 1), + INT8("int8", 8), + UINT8("uint8", 8), + INT16("int16", 16), + UINT16("uint16", 16), + INT32("int32", 32), + UINT32("uint32", 32), + FLOAT32("float32", 32), + INT64("int64", 64), + UINT64("uint64", 64), - INT32_SWAP("int32_swap", 32), - UINT32_SWAP("uint32_swap", 32), - FLOAT32_SWAP("float32_swap", 32), - INT64_SWAP("int64_swap", 64), - UINT64_SWAP("uint64_swap", 64); + INT32_SWAP("int32_swap", 32), + UINT32_SWAP("uint32_swap", 32), + FLOAT32_SWAP("float32_swap", 32), + INT64_SWAP("int64_swap", 64), + UINT64_SWAP("uint64_swap", 64); private final String configValue; private final int bits; diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusManager.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusManager.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusManager.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusManager.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusManagerListener.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusManagerListener.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusManagerListener.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusManagerListener.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusReadCallback.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusReadCallback.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusReadCallback.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusReadCallback.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusReadFunctionCode.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusReadFunctionCode.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusReadFunctionCode.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusReadFunctionCode.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusReadRequestBlueprint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusReadRequestBlueprint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusReadRequestBlueprint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusReadRequestBlueprint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusRegister.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusRegister.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusRegister.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusRegister.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusRegisterArray.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusRegisterArray.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusRegisterArray.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusRegisterArray.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusRequestBlueprint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusRequestBlueprint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusRequestBlueprint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusRequestBlueprint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusResponse.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusResponse.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusResponse.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusResponse.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusSlaveErrorResponseException.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusSlaveErrorResponseException.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusSlaveErrorResponseException.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusSlaveErrorResponseException.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusSlaveIOException.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusSlaveIOException.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusSlaveIOException.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusSlaveIOException.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusTransportException.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusTransportException.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusTransportException.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusTransportException.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusUnexpectedTransactionIdException.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusUnexpectedTransactionIdException.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusUnexpectedTransactionIdException.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusUnexpectedTransactionIdException.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteCallback.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteCallback.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteCallback.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteCallback.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteCoilRequestBlueprint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteCoilRequestBlueprint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteCoilRequestBlueprint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteCoilRequestBlueprint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteFunctionCode.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteFunctionCode.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteFunctionCode.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteFunctionCode.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteRegisterRequestBlueprint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteRegisterRequestBlueprint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteRegisterRequestBlueprint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteRegisterRequestBlueprint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteRequestBlueprint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteRequestBlueprint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteRequestBlueprint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteRequestBlueprint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteRequestBlueprintVisitor.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteRequestBlueprintVisitor.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteRequestBlueprintVisitor.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/ModbusWriteRequestBlueprintVisitor.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/PollTask.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/PollTask.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/PollTask.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/PollTask.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/TaskWithEndpoint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/TaskWithEndpoint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/TaskWithEndpoint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/TaskWithEndpoint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/WriteTask.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/WriteTask.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/WriteTask.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/WriteTask.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/EndpointPoolConfiguration.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/EndpointPoolConfiguration.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/EndpointPoolConfiguration.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/EndpointPoolConfiguration.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusIPSlaveEndpoint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusIPSlaveEndpoint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusIPSlaveEndpoint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusIPSlaveEndpoint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusSerialSlaveEndpoint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusSerialSlaveEndpoint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusSerialSlaveEndpoint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusSerialSlaveEndpoint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusSlaveEndpoint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusSlaveEndpoint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusSlaveEndpoint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusSlaveEndpoint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusSlaveEndpointVisitor.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusSlaveEndpointVisitor.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusSlaveEndpointVisitor.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusSlaveEndpointVisitor.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusTCPSlaveEndpoint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusTCPSlaveEndpoint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusTCPSlaveEndpoint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusTCPSlaveEndpoint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusUDPSlaveEndpoint.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusUDPSlaveEndpoint.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusUDPSlaveEndpoint.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/endpoint/ModbusUDPSlaveEndpoint.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/AggregateStopWatch.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/AggregateStopWatch.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/AggregateStopWatch.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/AggregateStopWatch.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/BitArrayWrappingBitVector.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/BitArrayWrappingBitVector.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/BitArrayWrappingBitVector.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/BitArrayWrappingBitVector.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusConnectionPool.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusConnectionPool.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusConnectionPool.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusConnectionPool.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusLibraryWrapper.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusLibraryWrapper.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusLibraryWrapper.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusLibraryWrapper.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusManagerImpl.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusManagerImpl.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusManagerImpl.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusManagerImpl.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusPoolConfig.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusPoolConfig.java similarity index 97% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusPoolConfig.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusPoolConfig.java index 507132296d82d..61cde162aa765 100644 --- a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusPoolConfig.java +++ b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusPoolConfig.java @@ -34,7 +34,7 @@ @NonNullByDefault public class ModbusPoolConfig extends GenericKeyedObjectPoolConfig { - private EvictionPolicy evictionPolicy = new DefaultEvictionPolicy(); + private EvictionPolicy evictionPolicy = new DefaultEvictionPolicy<>(); public ModbusPoolConfig() { // When the pool is exhausted, multiple calling threads may be simultaneously blocked waiting for instances diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusResponseImpl.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusResponseImpl.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusResponseImpl.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusResponseImpl.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusSlaveErrorResponseExceptionImpl.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusSlaveErrorResponseExceptionImpl.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusSlaveErrorResponseExceptionImpl.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusSlaveErrorResponseExceptionImpl.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusSlaveIOExceptionImpl.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusSlaveIOExceptionImpl.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusSlaveIOExceptionImpl.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/ModbusSlaveIOExceptionImpl.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/RegisterArrayWrappingInputRegister.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/RegisterArrayWrappingInputRegister.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/RegisterArrayWrappingInputRegister.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/RegisterArrayWrappingInputRegister.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/SimpleStopWatch.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/SimpleStopWatch.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/SimpleStopWatch.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/SimpleStopWatch.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/pooling/ModbusSlaveConnectionFactory.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/pooling/ModbusSlaveConnectionFactory.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/pooling/ModbusSlaveConnectionFactory.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/pooling/ModbusSlaveConnectionFactory.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/pooling/ModbusSlaveConnectionFactoryImpl.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/pooling/ModbusSlaveConnectionFactoryImpl.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/pooling/ModbusSlaveConnectionFactoryImpl.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/internal/pooling/ModbusSlaveConnectionFactoryImpl.java diff --git a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/json/WriteRequestJsonUtilities.java b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/json/WriteRequestJsonUtilities.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/json/WriteRequestJsonUtilities.java rename to bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/json/WriteRequestJsonUtilities.java index c1193f1c07577..403e841c2a498 100644 --- a/addons/io/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/json/WriteRequestJsonUtilities.java +++ b/bundles/org.openhab.io.transport.modbus/src/main/java/org/openhab/io/transport/modbus/json/WriteRequestJsonUtilities.java @@ -21,12 +21,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.io.transport.modbus.BasicBitArray; -import org.openhab.io.transport.modbus.ModbusRegister; -import org.openhab.io.transport.modbus.BasicModbusRegisterArray; import org.openhab.io.transport.modbus.BasicModbusRegister; +import org.openhab.io.transport.modbus.BasicModbusRegisterArray; import org.openhab.io.transport.modbus.BasicModbusWriteCoilRequestBlueprint; -import org.openhab.io.transport.modbus.ModbusWriteFunctionCode; import org.openhab.io.transport.modbus.BasicModbusWriteRegisterRequestBlueprint; +import org.openhab.io.transport.modbus.ModbusRegister; +import org.openhab.io.transport.modbus.ModbusWriteFunctionCode; import org.openhab.io.transport.modbus.ModbusWriteRequestBlueprint; import com.google.gson.JsonArray; diff --git a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/AbstractRequestComparer.java b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/AbstractRequestComparer.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/AbstractRequestComparer.java rename to bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/AbstractRequestComparer.java diff --git a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/BasicBitArrayTest.java b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/BasicBitArrayTest.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/BasicBitArrayTest.java rename to bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/BasicBitArrayTest.java diff --git a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesCommandToRegistersTest.java b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesCommandToRegistersTest.java similarity index 98% rename from addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesCommandToRegistersTest.java rename to bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesCommandToRegistersTest.java index 20bf0c2604b59..decbc0c535f14 100644 --- a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesCommandToRegistersTest.java +++ b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesCommandToRegistersTest.java @@ -16,6 +16,9 @@ import static org.junit.Assert.assertThat; import java.util.Collection; +import java.util.Collections; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang.NotImplementedException; import org.eclipse.smarthome.core.library.types.DecimalType; @@ -33,8 +36,6 @@ import org.openhab.io.transport.modbus.ModbusConstants.ValueType; import org.openhab.io.transport.modbus.ModbusRegisterArray; -import com.google.common.collect.ImmutableList; - @RunWith(Parameterized.class) public class BitUtilitiesCommandToRegistersTest { @@ -62,7 +63,7 @@ private static short[] shorts(int... ints) { @Parameters public static Collection data() { - return ImmutableList + return Collections.unmodifiableList(Stream .of(new Object[] { new DecimalType("1.0"), ValueType.BIT, IllegalArgumentException.class }, new Object[] { new DecimalType("1.0"), ValueType.INT8, IllegalArgumentException.class }, // @@ -286,7 +287,8 @@ ValueType.UINT64_SWAP, shorts(0x0, 0x0, 0x8, 0x0), }, // out of bounds of unsigned 64bit new DecimalType("3498348904359085439088905"), // should pick the low 64 bits - ValueType.UINT64_SWAP, shorts(0x7909, 0x772E, 0xBBB7, 0xDFC5), }); + ValueType.UINT64_SWAP, shorts(0x7909, 0x772E, 0xBBB7, 0xDFC5), }) + .collect(Collectors.toList())); } diff --git a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesExtractStateFromRegistersTest.java b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesExtractStateFromRegistersTest.java similarity index 95% rename from addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesExtractStateFromRegistersTest.java rename to bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesExtractStateFromRegistersTest.java index d4b804ebf74ec..dd8df1aab35ca 100644 --- a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesExtractStateFromRegistersTest.java +++ b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesExtractStateFromRegistersTest.java @@ -17,9 +17,13 @@ import java.nio.ByteBuffer; import java.util.Collection; +import java.util.Collections; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.Stream; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.library.types.DecimalType; import org.junit.Rule; import org.junit.Test; @@ -34,8 +38,6 @@ import org.openhab.io.transport.modbus.ModbusRegister; import org.openhab.io.transport.modbus.ModbusRegisterArray; -import com.google.common.collect.ImmutableList; - @RunWith(Parameterized.class) public class BitUtilitiesExtractStateFromRegistersTest { @@ -66,7 +68,7 @@ private static ModbusRegisterArray shortArrayToRegisterArray(int... arr) { @Parameters public static Collection data() { - return ImmutableList.of( + return Collections.unmodifiableList(Stream.of( // // BIT // @@ -254,6 +256,8 @@ ValueType.UINT32_SWAP, shortArrayToRegisterArray(4468, 1), 0 }, shortArrayToRegisterArray(0x4, 0xC47B, 0x199A), 1 }, new Object[] { new DecimalType(-1004.4f), ValueType.FLOAT32, shortArrayToRegisterArray(0xC47B, 0x199A, 0x4), 0 }, + new Object[] { // equivalent of NaN + Optional.empty(), ValueType.FLOAT32, shortArrayToRegisterArray(0x7fc0, 0x0000), 0 }, new Object[] { new DecimalType(-1004.4f), ValueType.FLOAT32, shortArrayToRegisterArray(0x4, 0x0, 0x0, 0x0, 0xC47B, 0x199A), 4 }, new Object[] { IllegalArgumentException.class, ValueType.FLOAT32, shortArrayToRegisterArray(4, -1004), @@ -275,6 +279,8 @@ ValueType.UINT32_SWAP, shortArrayToRegisterArray(4468, 1), 0 }, shortArrayToRegisterArray(0x199A, 0xC47B), 0 }, new Object[] { new DecimalType("64000"), ValueType.FLOAT32_SWAP, shortArrayToRegisterArray(0x0000, 0x477A), 0 }, + new Object[] { // equivalent of NaN + Optional.empty(), ValueType.FLOAT32_SWAP, shortArrayToRegisterArray(0x0000, 0x7fc0), 0 }, new Object[] { // out of bounds of unsigned 16bit (0 to 65,535) new DecimalType(70004.4f), ValueType.FLOAT32_SWAP, shortArrayToRegisterArray(0xBA33, 0x4788), @@ -350,6 +356,7 @@ ValueType.UINT32_SWAP, shortArrayToRegisterArray(4468, 1), 0 }, // 70004 -> 0x00011174 (32bit) -> 0x1174 (16bit) ValueType.INT64_SWAP, shortArrayToRegisterArray(0x0, 0x0, 0x8, 0x0), 0 }, new Object[] { new DecimalType("-2322243636186679031"), ValueType.INT64_SWAP, + shortArrayToRegisterArray(0x7909, 0x772E, 0xBBB7, 0xDFC5), 0 }, // @@ -370,7 +377,8 @@ ValueType.INT64_SWAP, shortArrayToRegisterArray(0x0, 0x0, 0x8, 0x0), 0 }, new Object[] { // out of bounds of unsigned 64bit new DecimalType("16124500437522872585"), ValueType.UINT64_SWAP, - shortArrayToRegisterArray(0x7909, 0x772E, 0xBBB7, 0xDFC5), 0 }); + shortArrayToRegisterArray(0x7909, 0x772E, 0xBBB7, 0xDFC5), 0 }) + .collect(Collectors.toList())); } @@ -381,8 +389,13 @@ public void testCommandToRegisters() { shouldThrow.expect((Class) expectedResult); } - DecimalType actualState = ModbusBitUtilities.extractStateFromRegisters(this.registers, this.index, this.type); + Optional<@NonNull DecimalType> actualState = ModbusBitUtilities.extractStateFromRegisters(this.registers, + this.index, this.type); + // Wrap given expectedResult to Optional, if necessary + Optional<@NonNull DecimalType> expectedStateWrapped = expectedResult instanceof DecimalType + ? Optional.of((DecimalType) expectedResult) + : (Optional<@NonNull DecimalType>) expectedResult; assertThat(String.format("registers=%s, index=%d, type=%s", registers, index, type), actualState, - is(equalTo(expectedResult))); + is(equalTo(expectedStateWrapped))); } } diff --git a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesExtractStringFromRegistersTest.java b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesExtractStringFromRegistersTest.java similarity index 95% rename from addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesExtractStringFromRegistersTest.java rename to bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesExtractStringFromRegistersTest.java index a1a177c44f902..0f090a6ed2ad3 100644 --- a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesExtractStringFromRegistersTest.java +++ b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesExtractStringFromRegistersTest.java @@ -18,8 +18,10 @@ import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.Collection; +import java.util.Collections; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.Stream; import org.eclipse.smarthome.core.library.types.StringType; import org.junit.Rule; @@ -28,13 +30,11 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import org.openhab.io.transport.modbus.BasicModbusRegister; +import org.openhab.io.transport.modbus.BasicModbusRegisterArray; import org.openhab.io.transport.modbus.ModbusBitUtilities; import org.openhab.io.transport.modbus.ModbusRegister; import org.openhab.io.transport.modbus.ModbusRegisterArray; -import org.openhab.io.transport.modbus.BasicModbusRegisterArray; -import org.openhab.io.transport.modbus.BasicModbusRegister; - -import com.google.common.collect.ImmutableList; @RunWith(Parameterized.class) public class BitUtilitiesExtractStringFromRegistersTest { @@ -68,7 +68,7 @@ private static ModbusRegisterArray shortArrayToRegisterArray(int... arr) { @Parameters public static Collection data() { - return ImmutableList.of( + return Collections.unmodifiableList(Stream.of( new Object[] { new StringType(""), shortArrayToRegisterArray(0), 0, 0, Charset.forName("UTF-8") }, new Object[] { new StringType("hello"), shortArrayToRegisterArray(0x6865, 0x6c6c, 0x6f00), 0, 5, Charset.forName("UTF-8") }, @@ -92,7 +92,8 @@ public static Collection data() { new Object[] { IllegalArgumentException.class, shortArrayToRegisterArray(0, 0), 0, -1, Charset.forName("UTF-8") }, new Object[] { IllegalArgumentException.class, shortArrayToRegisterArray(0, 0), 0, 5, - Charset.forName("UTF-8") }); + Charset.forName("UTF-8") }) + .collect(Collectors.toList())); } @SuppressWarnings({ "unchecked", "rawtypes" }) diff --git a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesTranslateCommand2BooleanTest.java b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesTranslateCommand2BooleanTest.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesTranslateCommand2BooleanTest.java rename to bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/BitUtilitiesTranslateCommand2BooleanTest.java diff --git a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/CoilMatcher.java b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/CoilMatcher.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/CoilMatcher.java rename to bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/CoilMatcher.java diff --git a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/IntegrationTestSupport.java b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/IntegrationTestSupport.java similarity index 97% rename from addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/IntegrationTestSupport.java rename to bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/IntegrationTestSupport.java index 59d61b70bd7b9..5a11c8ec2c8c0 100644 --- a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/IntegrationTestSupport.java +++ b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/IntegrationTestSupport.java @@ -14,7 +14,7 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.io.File; diff --git a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/ModbusSlaveEndpointTestCase.java b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/ModbusSlaveEndpointTestCase.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/ModbusSlaveEndpointTestCase.java rename to bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/ModbusSlaveEndpointTestCase.java diff --git a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/RegisterMatcher.java b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/RegisterMatcher.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/RegisterMatcher.java rename to bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/RegisterMatcher.java diff --git a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/ResultCaptor.java b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/ResultCaptor.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/ResultCaptor.java rename to bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/ResultCaptor.java diff --git a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/SmokeTest.java b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/SmokeTest.java similarity index 99% rename from addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/SmokeTest.java rename to bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/SmokeTest.java index d9c72c8e8c943..03632163399e4 100644 --- a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/SmokeTest.java +++ b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/SmokeTest.java @@ -20,6 +20,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang.StringUtils; import org.junit.Test; @@ -46,8 +48,6 @@ import org.openhab.io.transport.modbus.internal.BitArrayWrappingBitVector; import org.slf4j.LoggerFactory; -import com.google.common.collect.ImmutableSet; - import net.wimpi.modbus.msg.ModbusRequest; import net.wimpi.modbus.msg.WriteCoilRequest; import net.wimpi.modbus.msg.WriteMultipleCoilsRequest; @@ -900,9 +900,11 @@ public void testGetRegisteredRegularPolls() { modbusManager.registerRegularPoll(task, 50, 0); modbusManager.registerRegularPoll(task2, 50, 0); - assertThat(modbusManager.getRegisteredRegularPolls(), is(equalTo(ImmutableSet.of(task, task2)))); + assertThat(modbusManager.getRegisteredRegularPolls(), + is(equalTo(Stream.of(task, task2).collect(Collectors.toSet())))); modbusManager.unregisterRegularPoll(task); - assertThat(modbusManager.getRegisteredRegularPolls(), is(equalTo(ImmutableSet.of(task2)))); + assertThat(modbusManager.getRegisteredRegularPolls(), + is(equalTo(Stream.of(task2).collect(Collectors.toSet())))); } } diff --git a/addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/WriteRequestJsonUtilitiesTest.java b/bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/WriteRequestJsonUtilitiesTest.java similarity index 100% rename from addons/io/org.openhab.io.transport.modbus.test/src/test/java/org/openhab/io/transport/modbus/test/WriteRequestJsonUtilitiesTest.java rename to bundles/org.openhab.io.transport.modbus/src/test/java/org/openhab/io/transport/modbus/test/WriteRequestJsonUtilitiesTest.java diff --git a/bundles/org.openhab.transform.bin2json/.classpath b/bundles/org.openhab.transform.bin2json/.classpath index 5616244f3457f..5e8a55fefbd91 100644 --- a/bundles/org.openhab.transform.bin2json/.classpath +++ b/bundles/org.openhab.transform.bin2json/.classpath @@ -1,32 +1,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.transform.jsonpath/src/main/java/org/openhab/transform/jsonpath/internal/JSonPathTransformationService.java b/bundles/org.openhab.transform.jsonpath/src/main/java/org/openhab/transform/jsonpath/internal/JSonPathTransformationService.java index 974ac14bddfe2..297a37a479c77 100644 --- a/bundles/org.openhab.transform.jsonpath/src/main/java/org/openhab/transform/jsonpath/internal/JSonPathTransformationService.java +++ b/bundles/org.openhab.transform.jsonpath/src/main/java/org/openhab/transform/jsonpath/internal/JSonPathTransformationService.java @@ -13,6 +13,7 @@ package org.openhab.transform.jsonpath.internal; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -80,11 +81,23 @@ private String flattenList(List list) { return list.get(0).toString(); } if (list.size() > 1) { + if (list.get(0) instanceof Number || list.get(0) instanceof Boolean) { + return createNumberList(list); + } else if (list.get(0) instanceof String) { + return createStringList(list); + } logger.warn( - "JsonPath expressions with more than one result are not allowed, please adapt your selector. Result: {}", + "JsonPath expressions with more than one result are only supported for Boolean, Number and String data types, please adapt your selector. Result: {}", list); } return UnDefType.NULL.toFullString(); } + private String createNumberList(List list) { + return list.stream().map(n -> String.valueOf(n)).collect(Collectors.joining(", ", "[", "]")); + } + + private String createStringList(List list) { + return list.stream().map(n -> "\"" + String.valueOf(n) + "\"").collect(Collectors.joining(", ", "[", "]")); + } } diff --git a/bundles/org.openhab.transform.jsonpath/src/test/java/org/openhab/transform/jsonpath/internal/JSonPathTransformationServiceTest.java b/bundles/org.openhab.transform.jsonpath/src/test/java/org/openhab/transform/jsonpath/internal/JSonPathTransformationServiceTest.java index f53999336cf66..3b5d2d184ae7d 100644 --- a/bundles/org.openhab.transform.jsonpath/src/test/java/org/openhab/transform/jsonpath/internal/JSonPathTransformationServiceTest.java +++ b/bundles/org.openhab.transform.jsonpath/src/test/java/org/openhab/transform/jsonpath/internal/JSonPathTransformationServiceTest.java @@ -18,7 +18,6 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.openhab.transform.jsonpath.internal.JSonPathTransformationService; /** * @author Gaël L'hopital @@ -90,7 +89,7 @@ public void testIndefinite_filteredToSingle() throws TransformationException { @Test public void testIndefinite_notFiltered() throws TransformationException { String transformedResponse = processor.transform("$.*.id", jsonArray); - assertEquals("NULL", transformedResponse); + assertEquals("[1, 2]", transformedResponse); } @Test @@ -99,4 +98,35 @@ public void testIndefinite_noMatch() throws TransformationException { assertEquals("NULL", transformedResponse); } + @Test + public void testBooleanList() throws TransformationException { + final String list = "[true, false, true, true, false]"; + final String json = "{\"data\":" + list + "}"; + String transformedResponse = processor.transform("$.data", json); + assertEquals(list, transformedResponse); + } + + @Test + public void testNumberList() throws TransformationException { + final String list = "[0, 160, 253, -9, 21]"; + final String json = "{\"data\":" + list + "}"; + String transformedResponse = processor.transform("$.data", json); + assertEquals(list, transformedResponse); + } + + @Test + public void testDoubleList() throws TransformationException { + final String list = "[1.0, 2.16, 5.253, -3.9, 21.21]"; + final String json = "{\"data\":" + list + "}"; + String transformedResponse = processor.transform("$.data", json); + assertEquals(list, transformedResponse); + } + + @Test + public void testStringList() throws TransformationException { + final String list = "[\"test1\", \"test2\", \"test3\", \"test4\"]"; + final String json = "{\"data\":" + list + "}"; + String transformedResponse = processor.transform("$.data", json); + assertEquals(list, transformedResponse); + } } diff --git a/bundles/org.openhab.voice.marytts/.classpath b/bundles/org.openhab.voice.marytts/.classpath index e87e315b9978f..59b7c95efa144 100644 --- a/bundles/org.openhab.voice.marytts/.classpath +++ b/bundles/org.openhab.voice.marytts/.classpath @@ -29,5 +29,17 @@ + + + + + + + + + + + + diff --git a/bundles/org.openhab.voice.pollytts/.classpath b/bundles/org.openhab.voice.pollytts/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.voice.pollytts/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.openhab.voice.pollytts/.project b/bundles/org.openhab.voice.pollytts/.project new file mode 100644 index 0000000000000..4812ba6e7620b --- /dev/null +++ b/bundles/org.openhab.voice.pollytts/.project @@ -0,0 +1,23 @@ + + + org.openhab.voice.pollytts + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/voice/org.openhab.voice.pollytts/NOTICE b/bundles/org.openhab.voice.pollytts/NOTICE similarity index 100% rename from addons/voice/org.openhab.voice.pollytts/NOTICE rename to bundles/org.openhab.voice.pollytts/NOTICE diff --git a/addons/voice/org.openhab.voice.pollytts/README.md b/bundles/org.openhab.voice.pollytts/README.md similarity index 100% rename from addons/voice/org.openhab.voice.pollytts/README.md rename to bundles/org.openhab.voice.pollytts/README.md diff --git a/bundles/org.openhab.voice.pollytts/pom.xml b/bundles/org.openhab.voice.pollytts/pom.xml new file mode 100644 index 0000000000000..379d2f2ead87e --- /dev/null +++ b/bundles/org.openhab.voice.pollytts/pom.xml @@ -0,0 +1,50 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 2.5.0-SNAPSHOT + + + org.openhab.voice.pollytts + + openHAB Add-ons :: Bundles :: Polly Text-to-Speech + + + + + com.fasterxml.jackson.core + jackson-core + 2.9.8 + provided + + + com.fasterxml.jackson.core + jackson-annotations + 2.9.8 + provided + + + com.fasterxml.jackson.core + jackson-databind + 2.9.8 + provided + + + com.amazonaws + aws-java-sdk-core + 1.11.490 + provided + + + com.amazonaws + aws-java-sdk-polly + 1.11.490 + provided + + + + diff --git a/addons/voice/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/PollyTTSAudioStream.java b/bundles/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/PollyTTSAudioStream.java similarity index 100% rename from addons/voice/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/PollyTTSAudioStream.java rename to bundles/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/PollyTTSAudioStream.java diff --git a/addons/voice/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/PollyTTSService.java b/bundles/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/PollyTTSService.java similarity index 100% rename from addons/voice/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/PollyTTSService.java rename to bundles/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/PollyTTSService.java diff --git a/addons/voice/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/PollyTTSVoice.java b/bundles/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/PollyTTSVoice.java similarity index 100% rename from addons/voice/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/PollyTTSVoice.java rename to bundles/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/PollyTTSVoice.java diff --git a/addons/voice/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/cloudapi/CachedPollyTTSCloudImpl.java b/bundles/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/cloudapi/CachedPollyTTSCloudImpl.java similarity index 100% rename from addons/voice/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/cloudapi/CachedPollyTTSCloudImpl.java rename to bundles/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/cloudapi/CachedPollyTTSCloudImpl.java diff --git a/addons/voice/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/cloudapi/PollyTTSCloudImpl.java b/bundles/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/cloudapi/PollyTTSCloudImpl.java similarity index 100% rename from addons/voice/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/cloudapi/PollyTTSCloudImpl.java rename to bundles/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/cloudapi/PollyTTSCloudImpl.java diff --git a/addons/voice/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/cloudapi/PollyTTSConfig.java b/bundles/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/cloudapi/PollyTTSConfig.java similarity index 100% rename from addons/voice/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/cloudapi/PollyTTSConfig.java rename to bundles/org.openhab.voice.pollytts/src/main/java/org/openhab/voice/pollytts/internal/cloudapi/PollyTTSConfig.java diff --git a/addons/voice/org.openhab.voice.pollytts/ESH-INF/config/config.xml b/bundles/org.openhab.voice.pollytts/src/main/resources/ESH-INF/config/config.xml similarity index 100% rename from addons/voice/org.openhab.voice.pollytts/ESH-INF/config/config.xml rename to bundles/org.openhab.voice.pollytts/src/main/resources/ESH-INF/config/config.xml diff --git a/bundles/pom.xml b/bundles/pom.xml index bf0b69fda94cf..9de1bd227c8e1 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -1,6 +1,5 @@ - + 4.0.0 @@ -8,7 +7,6 @@ org.openhab.addons org.openhab.addons.reactor 2.5.0-SNAPSHOT - ../poms/bnd org.openhab.addons.bundles @@ -28,7 +26,15 @@ org.openhab.binding.autelis org.openhab.binding.avmfritz org.openhab.binding.bigassfan + org.openhab.binding.bluetooth + org.openhab.binding.bluetooth.bluegiga + org.openhab.binding.bluetooth.bluez + org.openhab.binding.bluetooth.blukii + org.openhab.binding.bluetooth.ruuvitag + org.openhab.binding.boschindego org.openhab.binding.bosesoundtouch + org.openhab.binding.buienradar + org.openhab.binding.chromecast org.openhab.binding.cm11a org.openhab.binding.coolmasternet org.openhab.binding.daikin @@ -40,14 +46,19 @@ org.openhab.binding.dlinksmarthome org.openhab.binding.dmx org.openhab.binding.dscalarm + org.openhab.binding.dsmr org.openhab.binding.dwdunwetter org.openhab.binding.elerotransmitterstick + org.openhab.binding.enocean org.openhab.binding.evohome + org.openhab.binding.exec + org.openhab.binding.feed org.openhab.binding.feican org.openhab.binding.folding org.openhab.binding.freebox org.openhab.binding.fronius org.openhab.binding.fsinternetradio + org.openhab.binding.ftpupload org.openhab.binding.gardena org.openhab.binding.globalcache org.openhab.binding.gpstracker @@ -60,6 +71,8 @@ org.openhab.binding.hue org.openhab.binding.hyperion org.openhab.binding.icloud + org.openhab.binding.ihc + org.openhab.binding.innogysmarthome org.openhab.binding.ipp org.openhab.binding.irtrans org.openhab.binding.jeelink @@ -72,10 +85,13 @@ org.openhab.binding.lametrictime org.openhab.binding.leapmotion org.openhab.binding.lgtvserial + org.openhab.binding.lgwebos org.openhab.binding.lifx org.openhab.binding.lirc org.openhab.binding.logreader + org.openhab.binding.loxone org.openhab.binding.lutron + org.openhab.binding.mail org.openhab.binding.max org.openhab.binding.mcp23017 org.openhab.binding.meteoblue @@ -85,6 +101,7 @@ org.openhab.binding.miio org.openhab.binding.milight org.openhab.binding.minecraft + org.openhab.binding.modbus org.openhab.binding.mqtt org.openhab.binding.mqtt.generic org.openhab.binding.mqtt.homeassistant @@ -97,14 +114,18 @@ org.openhab.binding.network org.openhab.binding.nibeheatpump org.openhab.binding.nibeuplink + org.openhab.binding.nikohomecontrol org.openhab.binding.ntp org.openhab.binding.nuki org.openhab.binding.oceanic org.openhab.binding.onebusaway + org.openhab.binding.onewiregpio org.openhab.binding.onewire + org.openhab.binding.onkyo org.openhab.binding.opensprinkler org.openhab.binding.openuv org.openhab.binding.openweathermap + org.openhab.binding.orvibo org.openhab.binding.pentair org.openhab.binding.phc org.openhab.binding.pioneeravr @@ -118,6 +139,7 @@ org.openhab.binding.robonect org.openhab.binding.rotelra1x org.openhab.binding.russound + org.openhab.binding.samsungtv org.openhab.binding.satel org.openhab.binding.seneye org.openhab.binding.sensebox @@ -125,6 +147,7 @@ org.openhab.binding.silvercrestwifisocket org.openhab.binding.sleepiq org.openhab.binding.smaenergymeter + org.openhab.binding.smartmeter org.openhab.binding.solaredge org.openhab.binding.solarlog org.openhab.binding.somfytahoma @@ -133,6 +156,7 @@ org.openhab.binding.spotify org.openhab.binding.squeezebox org.openhab.binding.synopanalyzer + org.openhab.binding.systeminfo org.openhab.binding.tado org.openhab.binding.tankerkoenig org.openhab.binding.tellstick @@ -149,13 +173,22 @@ org.openhab.binding.wemo org.openhab.binding.wifiled org.openhab.binding.windcentrale + org.openhab.binding.xmltv + org.openhab.binding.xmppclient + org.openhab.binding.yamahareceiver org.openhab.binding.yeelight + org.openhab.binding.zoneminder org.openhab.binding.zway org.openhab.extensionservice.marketplace org.openhab.extensionservice.marketplace.automation + org.openhab.io.azureiothub + org.openhab.io.homekit + org.openhab.io.hueemulation org.openhab.io.imperihome org.openhab.io.javasound org.openhab.io.neeo + org.openhab.io.openhabcloud + org.openhab.io.transport.modbus org.openhab.io.mqttembeddedbroker org.openhab.io.webaudio org.openhab.persistence.mapdb @@ -173,6 +206,7 @@ org.openhab.voice.mactts org.openhab.voice.marytts org.openhab.voice.picotts + org.openhab.voice.pollytts org.openhab.voice.voicerss diff --git a/features/karaf/pom.xml b/features/karaf/pom.xml deleted file mode 100644 index 9869f10c1e521..0000000000000 --- a/features/karaf/pom.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - - 4.0.0 - - - org.openhab.addons.features - org.openhab.addons.reactor.features - 2.5.0-SNAPSHOT - - - org.openhab.addons.features.karaf - org.openhab.addons.reactor.features.karaf - pom - - openHAB Add-ons :: Features :: Karaf - - - openhab-addons - openhab-addons-external - - - - - - org.apache.karaf.features - framework - ${karaf.version} - kar - provided - - - - - org.apache.karaf.features - standard - ${karaf.version} - features - xml - provided - - - - - - - - org.apache.karaf.tooling - karaf-maven-plugin - ${karaf.version} - true - - 80 - true - true - false - true - true - - - - compile - - features-generate-descriptor - - generate-resources - - - karaf-feature-verification - - verify - - process-resources - - - - mvn:org.apache.karaf.features/framework/${karaf.version}/xml/features - mvn:org.apache.karaf.features/standard/${karaf.version}/xml/features - - file:${project.build.directory}/feature/feature.xml - - org.apache.karaf.features:framework - 1.8 - - framework - - - openhab-* - - false - true - first - - - - - - - - - diff --git a/features/karaf/openhab-addons-external/.project b/features/openhab-addons-external/.project similarity index 100% rename from features/karaf/openhab-addons-external/.project rename to features/openhab-addons-external/.project diff --git a/features/karaf/openhab-addons-external/pom.xml b/features/openhab-addons-external/pom.xml similarity index 100% rename from features/karaf/openhab-addons-external/pom.xml rename to features/openhab-addons-external/pom.xml diff --git a/features/karaf/openhab-addons-external/src/main/resources/conf/cometvisu.cfg b/features/openhab-addons-external/src/main/resources/conf/cometvisu.cfg similarity index 100% rename from features/karaf/openhab-addons-external/src/main/resources/conf/cometvisu.cfg rename to features/openhab-addons-external/src/main/resources/conf/cometvisu.cfg diff --git a/features/karaf/openhab-addons-external/src/main/resources/conf/openhabcloud.cfg b/features/openhab-addons-external/src/main/resources/conf/openhabcloud.cfg similarity index 100% rename from features/karaf/openhab-addons-external/src/main/resources/conf/openhabcloud.cfg rename to features/openhab-addons-external/src/main/resources/conf/openhabcloud.cfg diff --git a/features/karaf/openhab-addons-external/src/main/resources/conf/voicerss.cfg b/features/openhab-addons-external/src/main/resources/conf/voicerss.cfg similarity index 100% rename from features/karaf/openhab-addons-external/src/main/resources/conf/voicerss.cfg rename to features/openhab-addons-external/src/main/resources/conf/voicerss.cfg diff --git a/features/karaf/openhab-addons-external/src/main/resources/lib/libLeap.dylib b/features/openhab-addons-external/src/main/resources/lib/libLeap.dylib similarity index 100% rename from features/karaf/openhab-addons-external/src/main/resources/lib/libLeap.dylib rename to features/openhab-addons-external/src/main/resources/lib/libLeap.dylib diff --git a/features/karaf/openhab-addons-external/src/main/resources/lib/libLeapJava.dylib b/features/openhab-addons-external/src/main/resources/lib/libLeapJava.dylib similarity index 100% rename from features/karaf/openhab-addons-external/src/main/resources/lib/libLeapJava.dylib rename to features/openhab-addons-external/src/main/resources/lib/libLeapJava.dylib diff --git a/features/karaf/openhab-addons/.project b/features/openhab-addons/.project similarity index 100% rename from features/karaf/openhab-addons/.project rename to features/openhab-addons/.project diff --git a/features/karaf/openhab-addons/pom.xml b/features/openhab-addons/pom.xml similarity index 100% rename from features/karaf/openhab-addons/pom.xml rename to features/openhab-addons/pom.xml diff --git a/features/karaf/openhab-addons/src/main/feature/feature.xml b/features/openhab-addons/src/main/feature/feature.xml similarity index 81% rename from features/karaf/openhab-addons/src/main/feature/feature.xml rename to features/openhab-addons/src/main/feature/feature.xml index e40c262c7131e..4d600646ea145 100644 --- a/features/karaf/openhab-addons/src/main/feature/feature.xml +++ b/features/openhab-addons/src/main/feature/feature.xml @@ -1,4 +1,4 @@ - + + mvn:org.igniterealtime.smack/smack-java7/4.3.3 + mvn:org.igniterealtime.smack/smack-extensions/4.3.3 + mvn:org.igniterealtime.smack/smack-im/4.3.3 + mvn:org.igniterealtime.smack/smack-tcp/4.3.3 + + mvn:org.jxmpp/jxmpp-core/0.6.3 + mvn:org.jxmpp/jxmpp-jid/0.6.3 + mvn:org.jxmpp/jxmpp-util-cache/0.6.3 + mvn:org.minidns/minidns-core/0.3.3 + mvn:org.igniterealtime.smack/smack-core/4.3.3 + mvn:org.igniterealtime.smack/smack-resolver-javax/4.3.3 + mvn:org.igniterealtime.smack/smack-sasl-javax/4.3.3 + wrap:mvn:xpp3/xpp3/1.1.4c$Bundle-Name=XPP3%20Library&Bundle-SymbolicName=xpp3-xpp3&Bundle-Version=1.1.4 + mvn:org.openhab.addons.bundles/org.openhab.binding.xmppclient/${project.version} + + + + openhab-runtime-base + wrap + wrap:mvn:javax.activation/activation/1.1.1$Bundle-Name=Javax%20Activation&Bundle-SymbolicName=javax.activation.activation&Bundle-Version=1.1.1 + mvn:javax.xml.bind/jaxb-api/2.2.12 + mvn:com.sun.xml.bind/jaxb-impl/2.2.11 + mvn:com.sun.xml.bind/jaxb-core/2.2.11 + mvn:org.openhab.addons.bundles/org.openhab.binding.xmltv/${project.version} + openhab-runtime-base openhab-transport-upnp - mvn:org.openhab.binding/org.openhab.binding.yamahareceiver/${project.version} + mvn:org.openhab.addons.bundles/org.openhab.binding.yamahareceiver/${project.version} @@ -937,7 +1022,8 @@ openhab-runtime-base - mvn:org.openhab.binding/org.openhab.binding.zoneminder/${project.version} + mvn:org.jsoup/jsoup/1.10.1 + mvn:org.openhab.addons.bundles/org.openhab.binding.zoneminder/${project.version} @@ -948,18 +1034,15 @@ - - openhab-runtime-base - mvn:org.openhab.io/org.openhab.io.transport.feed/${project.version} - - openhab-runtime-base openhab-transport-serial - mvn:org.openhab.io/org.openhab.io.transport.modbus/${project.version} + wrap + wrap:mvn:net.wimpi/jamod/1.2.3.OH$Bundle-Name=JAVA%20Modbus&Bundle-SymbolicName=net.wimpi.jamod&Bundle-Version=1.2.3.OH + mvn:org.apache.commons/commons-pool2/2.4.3 + mvn:org.openhab.addons.bundles/org.openhab.io.transport.modbus/${project.version} - openhab-runtime-base wrap @@ -977,23 +1060,44 @@ openhab-runtime-base openhab-transport-mqtt - mvn:org.openhab.io/org.openhab.io.azureiothub/${project.version} + wrap + wrap:mvn:com.microsoft.azure.sdk.iot/iot-device-client/1.2.27$Bundle-Name=Azure%20IoT%20Hub%20Device%20Client&Bundle-SymbolicName=com.microsoft.azure.sdk.iot.iot-device-client&Bundle-Version=1.2.27 + wrap:mvn:com.microsoft.azure.sdk.iot/iot-service-client/1.4.20$Bundle-Name=Azure%20IoT%20Hub%20Service%20Client&Bundle-SymbolicName=com.microsoft.azure.sdk.iot.iot-service-client&Bundle-Version=1.4.20 + wrap:mvn:com.microsoft.azure/azure-eventhubs/0.14.0$Bundle-Name=Azure%20EventHubs&Bundle-SymbolicName=com.microsoft.azure.azure-eventhubs&Bundle-Version=0.14.0 + mvn:javax.json/javax.json-api/1.0 + mvn:org.openhab.addons.bundles/org.openhab.io.azureiothub/${project.version} openhab-runtime-base - mvn:org.openhab.io/org.openhab.io.openhabcloud/${project.version} + mvn:org.json/json/20180813 + wrap:mvn:com.squareup.okhttp3/okhttp/3.12.1$Bundle-Name=OkHttp&Bundle-SymbolicName=com.squareup.okhttp&Bundle-Version=3.12.1 + wrap:mvn:com.squareup.okio/okio/1.15.0$Bundle-Name=Okio&Bundle-SymbolicName=com.squareup.okio&Bundle-Version=1.15.0 + wrap:mvn:io.socket/socket.io-client/1.0.0$Bundle-Name=Socket.IO%20Client&Bundle-SymbolicName=io.socket.socket.io-client&Bundle-Version=1.0.0 + wrap:mvn:io.socket/engine.io-client/1.0.0$Bundle-Name=Engine.IO%20Client&Bundle-SymbolicName=io.socket.engine.io-client&Bundle-Version=1.0.0 + mvn:org.openhab.addons.bundles/org.openhab.io.openhabcloud/${project.version} openhab-runtime-base - mvn:org.openhab.io/org.openhab.io.hueemulation/${project.version} + openhab-transport-upnp + openhab-core-automation + mvn:org.openhab.addons.bundles/org.openhab.io.hueemulation/${project.version} openhab-runtime-base openhab-transport-mdns - mvn:org.openhab.io/org.openhab.io.homekit/${project.version} + wrap + mvn:org.glassfish/javax.json/1.0.4 + mvn:javax.json/javax.json-api/1.0 + wrap:mvn:io.netty/netty-all/4.0.32.Final + wrap:mvn:com.nimbusds/srp6a/1.5.2 + mvn:net.i2p.crypto/eddsa/0.1.0 + mvn:org.bouncycastle/bcprov-jdk15on/1.51 + wrap:mvn:org.zeromq/curve25519-java/0.1.0 + wrap:mvn:com.beowulfe.hap/hap/1.1.4 + mvn:org.openhab.addons.bundles/org.openhab.io.homekit/${project.version} @@ -1064,7 +1168,7 @@ mvn:com.fasterxml.jackson.core/jackson-core/2.9.8 mvn:com.fasterxml.jackson.core/jackson-databind/2.9.8 mvn:com.fasterxml.jackson.core/jackson-annotations/2.9.8 - mvn:com.google.guava/guava/20.0 + mvn:com.google.guava/guava/21.0 mvn:ch.obermuhlner/big-math/2.1.0 mvn:org.jsoup/jsoup/1.8.3 wrap:mvn:com.google.re2j/re2j/1.2$Bundle-Name=re2j&Bundle-SymbolicName=com.google.re2j.re2j&Bundle-Version=1.2 @@ -1127,12 +1231,6 @@ mvn:org.openhab.addons.bundles/org.openhab.voice.googletts/${project.version} - - openhab-runtime-base mvn:org.openhab.addons.bundles/org.openhab.voice.mactts/${project.version} @@ -1150,7 +1248,16 @@ openhab-runtime-base - mvn:org.openhab.voice/org.openhab.voice.pollytts/${project.version} + mvn:org.apache.httpcomponents/httpcore-osgi/4.4.9 + mvn:org.apache.httpcomponents/httpclient-osgi/4.5.5 + mvn:com.fasterxml.jackson.core/jackson-annotations/2.9.8 + mvn:com.fasterxml.jackson.core/jackson-core/2.9.8 + mvn:com.fasterxml.jackson.core/jackson-databind/2.9.8 + wrap:mvn:com.amazonaws/aws-java-sdk-core/1.11.490 + wrap:mvn:com.amazonaws/aws-java-sdk-polly/1.11.490 + mvn:commons-logging/commons-logging/1.2 + mvn:joda-time/joda-time/2.8.1 + mvn:org.openhab.addons.bundles/org.openhab.voice.pollytts/${project.version} diff --git a/features/pom.xml b/features/pom.xml index bf429d0e11049..e7ae425a2b45d 100644 --- a/features/pom.xml +++ b/features/pom.xml @@ -1,4 +1,5 @@ - + + 4.0.0 @@ -6,17 +7,95 @@ org.openhab.addons org.openhab.addons.reactor 2.5.0-SNAPSHOT - ../poms/bnd - org.openhab.addons.features - org.openhab.addons.reactor.features + org.openhab.addons.features.karaf + org.openhab.addons.reactor.features.karaf pom - openHAB Add-ons :: Features + openHAB Add-ons :: Features :: Karaf - karaf + openhab-addons + openhab-addons-external + + + + org.apache.karaf.features + framework + ${karaf.version} + kar + provided + + + + + org.apache.karaf.features + standard + ${karaf.version} + features + xml + provided + + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + ${karaf.version} + true + + 80 + true + true + false + true + true + + + + compile + + features-generate-descriptor + + generate-resources + + + karaf-feature-verification + + verify + + process-resources + + + + mvn:org.apache.karaf.features/framework/${karaf.version}/xml/features + mvn:org.apache.karaf.features/standard/${karaf.version}/xml/features + + file:${project.build.directory}/feature/feature.xml + + org.apache.karaf.features:framework + 1.8 + + framework + + + openhab-* + + false + true + first + + + + + + + + diff --git a/features/readme.md b/features/readme.md new file mode 100644 index 0000000000000..41307bb003b7a --- /dev/null +++ b/features/readme.md @@ -0,0 +1,7 @@ +## Karaf features + +In this directory you find Karaf features defined. +Karaf features allow to define dependencies for openHAB addons and OSGi bundles in general. + +If you require an external library in your openHAB addon, you most likely want to edit the Karaf features file as well. + diff --git a/itests/.project b/itests/.project new file mode 100644 index 0000000000000..f084ed24a4c9c --- /dev/null +++ b/itests/.project @@ -0,0 +1,17 @@ + + + org.openhab.addons.reactor.itests + + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + + diff --git a/itests/org.openhab.binding.feed.tests/.classpath b/itests/org.openhab.binding.feed.tests/.classpath new file mode 100644 index 0000000000000..cd377e474d1ca --- /dev/null +++ b/itests/org.openhab.binding.feed.tests/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/itests/org.openhab.binding.feed.tests/.project b/itests/org.openhab.binding.feed.tests/.project new file mode 100644 index 0000000000000..dcae9b8e127dd --- /dev/null +++ b/itests/org.openhab.binding.feed.tests/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.feed.tests + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.onkyo/NOTICE b/itests/org.openhab.binding.feed.tests/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.onkyo/NOTICE rename to itests/org.openhab.binding.feed.tests/NOTICE diff --git a/itests/org.openhab.binding.feed.tests/itest.bndrun b/itests/org.openhab.binding.feed.tests/itest.bndrun new file mode 100644 index 0000000000000..d556a1fe2178c --- /dev/null +++ b/itests/org.openhab.binding.feed.tests/itest.bndrun @@ -0,0 +1,73 @@ +-include: ../itest-common.bndrun + +Bundle-SymbolicName: ${project.artifactId} +Fragment-Host: org.openhab.binding.feed + +-runrequires: \ + bnd.identity;id='org.openhab.binding.feed.tests',\ + bnd.identity;id='org.openhab.core.binding.xml',\ + bnd.identity;id='org.openhab.core.thing.xml',\ + osgi.identity;filter:='(&(osgi.identity=org.ops4j.pax.web.pax-web-runtime)(version>=7.2.3))' + + +# We would like to use the "volatile" storage only +-runblacklist: \ + bnd.identity;id='org.openhab.core.storage.json',\ + bnd.identity;id='org.openhab.core.storage.mapdb' + +-runvm: -Dorg.osgi.service.http.port=9090 + +# +# done +# +-runbundles: \ + org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\ + org.apache.servicemix.specs.annotation-api-1.3;version='[1.3.0,1.3.1)',\ + org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\ + org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\ + org.apache.servicemix.bundles.jdom;version='[2.0.6,2.0.7)',\ + com.google.gson;version='[2.8.2,2.8.3)',\ + javax.measure.unit-api;version='[1.0.0,1.0.1)',\ + org.apache.commons.collections;version='[3.2.1,3.2.2)',\ + org.apache.commons.lang;version='[2.6.0,2.6.1)',\ + org.apache.felix.scr;version='[2.1.10,2.1.11)',\ + org.apache.felix.configadmin,\ + org.apache.servicemix.bundles.xstream;version='[1.4.7,1.4.8)',\ + org.apache.xbean.bundleutils;version='[4.12.0,4.12.1)',\ + org.apache.xbean.finder;version='[4.12.0,4.12.1)',\ + org.objectweb.asm;version='[7.0.0,7.0.1)',\ + org.objectweb.asm.commons;version='[7.0.0,7.0.1)',\ + org.objectweb.asm.tree;version='[7.0.0,7.0.1)',\ + org.openhab.binding.feed;version='[2.5.0,2.5.1)',\ + org.openhab.binding.feed.tests;version='[2.5.0,2.5.1)',\ + org.openhab.core;version='[2.5.0,2.5.1)',\ + org.openhab.core.binding.xml;version='[2.5.0,2.5.1)',\ + org.openhab.core.config.core;version='[2.5.0,2.5.1)',\ + org.openhab.core.config.xml;version='[2.5.0,2.5.1)',\ + org.openhab.core.io.console;version='[2.5.0,2.5.1)',\ + org.openhab.core.test;version='[2.5.0,2.5.1)',\ + org.openhab.core.thing;version='[2.5.0,2.5.1)',\ + org.openhab.core.thing.xml;version='[2.5.0,2.5.1)',\ + org.ops4j.pax.web.pax-web-api;version='[7.2.3,7.2.4)',\ + org.osgi.service.event;version='[1.4.0,1.4.1)',\ + osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\ + osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\ + slf4j.api;version='[1.7.25,1.7.26)',\ + tec.uom.lib.uom-lib-common;version='[1.0.2,1.0.3)',\ + tec.uom.se;version='[1.0.8,1.0.9)',\ + slf4j.simple;version='[1.7.21,1.7.22)',\ + com.rometools.rome;version='[1.12.0,1.12.1)',\ + org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ + org.apache.commons.io;version='[2.2.0,2.2.1)',\ + org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ + org.eclipse.jetty.http;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.io;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.security;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.server;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.servlet;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.util;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.xml;version='[9.4.11,9.4.12)',\ + org.ops4j.pax.swissbox.optional.jcl;version='[1.8.2,1.8.3)',\ + org.ops4j.pax.web.pax-web-jetty;version='[7.2.3,7.2.4)',\ + org.ops4j.pax.web.pax-web-runtime;version='[7.2.3,7.2.4)',\ + org.ops4j.pax.web.pax-web-spi;version='[7.2.3,7.2.4)' \ No newline at end of file diff --git a/itests/org.openhab.binding.feed.tests/pom.xml b/itests/org.openhab.binding.feed.tests/pom.xml new file mode 100644 index 0000000000000..8eb45829dd09e --- /dev/null +++ b/itests/org.openhab.binding.feed.tests/pom.xml @@ -0,0 +1,40 @@ + + + + 4.0.0 + + + org.openhab.addons.itests + org.openhab.addons.reactor.itests + 2.5.0-SNAPSHOT + + + org.openhab.binding.feed.tests + + openHAB Add-ons :: Integration Tests :: Feed Binding Tests + + + + org.openhab.addons.bundles + org.openhab.binding.feed + ${project.version} + + + com.rometools + rome + 1.12.0 + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.jdom + 2.0.6_1 + + + org.slf4j + slf4j-api + + + + + + diff --git a/addons/binding/org.openhab.binding.feed.test/src/test/java/org/openhab/binding/feed/test/FeedHandlerTest.java b/itests/org.openhab.binding.feed.tests/src/main/java/org/openhab/binding/feed/test/FeedHandlerTest.java similarity index 100% rename from addons/binding/org.openhab.binding.feed.test/src/test/java/org/openhab/binding/feed/test/FeedHandlerTest.java rename to itests/org.openhab.binding.feed.tests/src/main/java/org/openhab/binding/feed/test/FeedHandlerTest.java diff --git a/addons/binding/org.openhab.binding.feed.test/src/test/java/org/openhab/binding/feed/test/SlowTests.java b/itests/org.openhab.binding.feed.tests/src/main/java/org/openhab/binding/feed/test/SlowTests.java similarity index 100% rename from addons/binding/org.openhab.binding.feed.test/src/test/java/org/openhab/binding/feed/test/SlowTests.java rename to itests/org.openhab.binding.feed.tests/src/main/java/org/openhab/binding/feed/test/SlowTests.java diff --git a/addons/binding/org.openhab.binding.feed.test/src/test/resources/input/rss_2.0.xml b/itests/org.openhab.binding.feed.tests/src/main/resources/input/rss_2.0.xml similarity index 100% rename from addons/binding/org.openhab.binding.feed.test/src/test/resources/input/rss_2.0.xml rename to itests/org.openhab.binding.feed.tests/src/main/resources/input/rss_2.0.xml diff --git a/addons/binding/org.openhab.binding.feed.test/src/test/resources/input/rss_2.0_changed.xml b/itests/org.openhab.binding.feed.tests/src/main/resources/input/rss_2.0_changed.xml similarity index 100% rename from addons/binding/org.openhab.binding.feed.test/src/test/resources/input/rss_2.0_changed.xml rename to itests/org.openhab.binding.feed.tests/src/main/resources/input/rss_2.0_changed.xml diff --git a/itests/org.openhab.binding.systeminfo.tests/.classpath b/itests/org.openhab.binding.systeminfo.tests/.classpath new file mode 100644 index 0000000000000..7c632d2631a3b --- /dev/null +++ b/itests/org.openhab.binding.systeminfo.tests/.classpath @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/itests/org.openhab.binding.systeminfo.tests/.project b/itests/org.openhab.binding.systeminfo.tests/.project new file mode 100644 index 0000000000000..5ab19c92d9666 --- /dev/null +++ b/itests/org.openhab.binding.systeminfo.tests/.project @@ -0,0 +1,23 @@ + + + org.openhab.binding.systeminfo.tests + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.samsungtv/NOTICE b/itests/org.openhab.binding.systeminfo.tests/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.samsungtv/NOTICE rename to itests/org.openhab.binding.systeminfo.tests/NOTICE diff --git a/itests/org.openhab.binding.systeminfo.tests/itest.bndrun b/itests/org.openhab.binding.systeminfo.tests/itest.bndrun new file mode 100644 index 0000000000000..55a0f3e077b07 --- /dev/null +++ b/itests/org.openhab.binding.systeminfo.tests/itest.bndrun @@ -0,0 +1,66 @@ +-include: ../itest-common.bndrun + +Bundle-SymbolicName: ${project.artifactId} +Fragment-Host: org.openhab.binding.systeminfo + +-runrequires: \ + bnd.identity;id='org.openhab.binding.systeminfo.tests',\ + bnd.identity;id='org.openhab.core.binding.xml',\ + bnd.identity;id='org.openhab.core.thing.xml' + +# We would like to use the "volatile" storage only +-runblacklist: \ + bnd.identity;id='org.openhab.core.storage.json',\ + bnd.identity;id='org.openhab.core.storage.mapdb' + +-runsystemcapabilities: ${native_capability} +# +# done +# +-runbundles: \ + com.google.gson;version='[2.8.2,2.8.3)',\ + javax.measure.unit-api;version='[1.0.0,1.0.1)',\ + org.apache.commons.collections;version='[3.2.1,3.2.2)',\ + org.apache.commons.io;version='[2.2.0,2.2.1)',\ + org.apache.commons.lang;version='[2.6.0,2.6.1)',\ + org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\ + org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ + org.apache.felix.scr;version='[2.1.10,2.1.11)',\ + org.eclipse.jetty.http;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.io;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.security;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.server;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.servlet;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.util;version='[9.4.11,9.4.12)',\ + org.openhab.binding.systeminfo;version='[2.5.0,2.5.1)',\ + org.openhab.binding.systeminfo.tests;version='[2.5.0,2.5.1)',\ + org.openhab.core;version='[2.5.0,2.5.1)',\ + org.openhab.core.config.core;version='[2.5.0,2.5.1)',\ + org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\ + org.openhab.core.io.console;version='[2.5.0,2.5.1)',\ + org.openhab.core.test;version='[2.5.0,2.5.1)',\ + org.openhab.core.thing;version='[2.5.0,2.5.1)',\ + org.osgi.service.event;version='[1.4.0,1.4.1)',\ + osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\ + osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\ + tec.uom.lib.uom-lib-common;version='[1.0.2,1.0.3)',\ + tec.uom.se;version='[1.0.8,1.0.9)',\ + org.apache.servicemix.bundles.xstream;version='[1.4.7,1.4.8)',\ + org.openhab.core.binding.xml;version='[2.5.0,2.5.1)',\ + org.openhab.core.config.xml;version='[2.5.0,2.5.1)',\ + org.openhab.core.thing.xml;version='[2.5.0,2.5.1)',\ + net.bytebuddy.byte-buddy;version='[1.9.7,1.9.8)',\ + net.bytebuddy.byte-buddy-agent;version='[1.9.7,1.9.8)',\ + org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\ + org.apache.servicemix.specs.annotation-api-1.3;version='[1.3.0,1.3.1)',\ + org.apache.servicemix.specs.jaxb-api-2.2;version='[2.9.0,2.9.1)',\ + org.apache.servicemix.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\ + org.mockito.mockito-core;version='[2.25.0,2.25.1)',\ + org.objenesis;version='[2.6.0,2.6.1)',\ + com.github.oshi.oshi-core;version='[3.13.0,3.13.1)',\ + com.sun.jna;version='[5.3.0,5.3.1)',\ + com.sun.jna.platform;version='[5.3.0,5.3.1)',\ + org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ + slf4j.api;version='[1.7.25,1.7.26)',\ + ch.qos.logback.core;version='[1.2.3,1.2.4)',\ + slf4j.simple;version='[1.7.21,1.7.22)' \ No newline at end of file diff --git a/itests/org.openhab.binding.systeminfo.tests/pom.xml b/itests/org.openhab.binding.systeminfo.tests/pom.xml new file mode 100644 index 0000000000000..ef3847283e3a6 --- /dev/null +++ b/itests/org.openhab.binding.systeminfo.tests/pom.xml @@ -0,0 +1,45 @@ + + + + 4.0.0 + + + org.openhab.addons.itests + org.openhab.addons.reactor.itests + 2.5.0-SNAPSHOT + + + org.openhab.binding.systeminfo.tests + + openHAB Add-ons :: Integration Tests :: Systeminfo Binding Tests + + + + org.openhab.addons.bundles + org.openhab.binding.systeminfo + ${project.version} + + + net.java.dev.jna + jna-platform + 5.3.0 + + + net.java.dev.jna + jna + 5.3.0 + + + com.github.oshi + oshi-core + 3.13.0 + + + org.slf4j + slf4j-api + + + + + + diff --git a/addons/binding/org.openhab.binding.systeminfo.test/src/test/java/org/openhab/binding/systeminfo/test/SysteminfoOSGiTest.java b/itests/org.openhab.binding.systeminfo.tests/src/main/java/org/openhab/binding/systeminfo/test/SysteminfoOSGiTest.java similarity index 99% rename from addons/binding/org.openhab.binding.systeminfo.test/src/test/java/org/openhab/binding/systeminfo/test/SysteminfoOSGiTest.java rename to itests/org.openhab.binding.systeminfo.tests/src/main/java/org/openhab/binding/systeminfo/test/SysteminfoOSGiTest.java index 9ee44765dae33..c8bd352320497 100644 --- a/addons/binding/org.openhab.binding.systeminfo.test/src/test/java/org/openhab/binding/systeminfo/test/SysteminfoOSGiTest.java +++ b/itests/org.openhab.binding.systeminfo.tests/src/main/java/org/openhab/binding/systeminfo/test/SysteminfoOSGiTest.java @@ -302,7 +302,7 @@ private void testInvalidConfiguration() { is(equalTo(ThingStatus.OFFLINE))); assertThat(systemInfoThing.getStatusInfo().getStatusDetail(), is(equalTo(ThingStatusDetail.HANDLER_INITIALIZING_ERROR))); - assertThat(systemInfoThing.getStatusInfo().getDescription(), is(equalTo("Thing can not be initialized!"))); + assertThat(systemInfoThing.getStatusInfo().getDescription(), is(equalTo("Thing cannot be initialized!"))); }); } diff --git a/itests/org.openhab.io.hueemulation.tests/.classpath b/itests/org.openhab.io.hueemulation.tests/.classpath new file mode 100644 index 0000000000000..3c5e7d17553a8 --- /dev/null +++ b/itests/org.openhab.io.hueemulation.tests/.classpath @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/itests/org.openhab.io.hueemulation.tests/.project b/itests/org.openhab.io.hueemulation.tests/.project new file mode 100644 index 0000000000000..bca259e372088 --- /dev/null +++ b/itests/org.openhab.io.hueemulation.tests/.project @@ -0,0 +1,23 @@ + + + org.openhab.io.hueemulation.tests + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/addons/binding/org.openhab.binding.smartmeter.test/NOTICE b/itests/org.openhab.io.hueemulation.tests/NOTICE similarity index 100% rename from addons/binding/org.openhab.binding.smartmeter.test/NOTICE rename to itests/org.openhab.io.hueemulation.tests/NOTICE diff --git a/itests/org.openhab.io.hueemulation.tests/itest.bndrun b/itests/org.openhab.io.hueemulation.tests/itest.bndrun new file mode 100644 index 0000000000000..5a94730bcffd8 --- /dev/null +++ b/itests/org.openhab.io.hueemulation.tests/itest.bndrun @@ -0,0 +1,38 @@ +-include: ../itest-common.bndrun + +Bundle-SymbolicName: ${project.artifactId} +Fragment-Host: org.openhab.io.hueemulation + +-runrequires: bnd.identity;id='org.openhab.io.hueemulation.tests' + +# +# done +# +-runbundles: \ + ch.qos.logback.classic;version='[1.2.3,1.2.4)',\ + ch.qos.logback.core;version='[1.2.3,1.2.4)',\ + com.google.gson;version='[2.8.2,2.8.3)',\ + javax.measure.unit-api;version='[1.0.0,1.0.1)',\ + org.apache.commons.io;version='[2.2.0,2.2.1)',\ + org.apache.commons.lang;version='[2.6.0,2.6.1)',\ + org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\ + org.apache.felix.scr;version='[2.1.10,2.1.11)',\ + org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ + org.eclipse.jetty.http;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.io;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.security;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.server;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.servlet;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.util;version='[9.4.11,9.4.12)',\ + org.openhab.core;version='[2.5.0,2.5.1)',\ + org.openhab.core.config.core;version='[2.5.0,2.5.1)',\ + org.openhab.core.persistence;version='[2.5.0,2.5.1)',\ + org.openhab.core.test;version='[2.5.0,2.5.1)',\ + org.openhab.io.hueemulation;version='[2.5.0,2.5.1)',\ + org.openhab.io.hueemulation.tests;version='[2.5.0,2.5.1)',\ + org.osgi.service.event;version='[1.4.0,1.4.1)',\ + osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\ + osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\ + slf4j.api;version='[1.7.25,1.7.26)',\ + tec.uom.lib.uom-lib-common;version='[1.0.2,1.0.3)',\ + tec.uom.se;version='[1.0.8,1.0.9)' diff --git a/itests/org.openhab.io.hueemulation.tests/pom.xml b/itests/org.openhab.io.hueemulation.tests/pom.xml new file mode 100644 index 0000000000000..00e2fb617dfde --- /dev/null +++ b/itests/org.openhab.io.hueemulation.tests/pom.xml @@ -0,0 +1,24 @@ + + + + 4.0.0 + + + org.openhab.addons.itests + org.openhab.addons.reactor.itests + 2.5.0-SNAPSHOT + + + org.openhab.io.hueemulation.tests + + openHAB Add-ons :: Integration Tests :: Hue Emulation Service Tests + + + + org.openhab.addons.bundles + org.openhab.io.hueemulation + ${project.version} + + + + diff --git a/itests/org.openhab.io.hueemulation.tests/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationServiceOSGiTest.java b/itests/org.openhab.io.hueemulation.tests/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationServiceOSGiTest.java new file mode 100644 index 0000000000000..47e929945a103 --- /dev/null +++ b/itests/org.openhab.io.hueemulation.tests/src/main/java/org/openhab/io/hueemulation/internal/HueEmulationServiceOSGiTest.java @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.io.hueemulation.internal; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertThat; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.events.EventPublisher; +import org.eclipse.smarthome.core.items.ItemRegistry; +import org.eclipse.smarthome.test.java.JavaOSGiTest; +import org.eclipse.smarthome.test.storage.VolatileStorageService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.jupnp.UpnpService; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.openhab.core.automation.RuleRegistry; +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.event.EventAdmin; + +/** + * Integration tests for {@link HueEmulationService}. + * + * @author David Graeff - Initial contribution + */ +public class HueEmulationServiceOSGiTest extends JavaOSGiTest { + private HueEmulationService hueService; + VolatileStorageService volatileStorageService = new VolatileStorageService(); + + private @Nullable RuleRegistry ruleRegistry; + private @Nullable ItemRegistry itemRegistry; + private @Nullable EventAdmin eventAdmin; + private @Nullable UpnpService upnpService; + + @Mock + ConfigurationAdmin configurationAdmin; + + @Mock + EventPublisher eventPublisher; + + String host; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + registerService(volatileStorageService); + registerService(configurationAdmin); + + eventAdmin = getService(EventAdmin.class, EventAdmin.class); + assertThat(eventAdmin, notNullValue()); + + itemRegistry = getService(ItemRegistry.class, ItemRegistry.class); + assertThat(itemRegistry, notNullValue()); + + ruleRegistry = getService(RuleRegistry.class, RuleRegistry.class); + assertThat(ruleRegistry, notNullValue()); + + upnpService = getService(UpnpService.class, UpnpService.class); + assertThat(upnpService, notNullValue()); + + hueService = getService(HueEmulationService.class, HueEmulationService.class); + assertThat(hueService, notNullValue()); + + } + + @After + public void tearDown() { + unregisterService(volatileStorageService); + } + + @Test(timeout = 5000) + public void UpnpServiceTest() throws InterruptedException, ExecutionException, TimeoutException, IOException { + waitFor(() -> !hueService.cs.ds.config.ipaddress.isEmpty(), 5000, 100); + host = "http://" + hueService.cs.ds.config.ipaddress + ":" + + String.valueOf(hueService.cs.config.discoveryHttpPort); + + HttpURLConnection c = (HttpURLConnection) new URL(host + "/description.xml").openConnection(); + assertThat(c.getResponseCode(), is(200)); + String body = read(c); + assertThat(body, containsString(hueService.cs.config.uuid)); + + } + + @SuppressWarnings("null") + private String read(HttpURLConnection urlConnection) throws IOException { + String result = ""; + final InputStream _is; + if (urlConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) { + _is = urlConnection.getInputStream(); + } else { + /* error from server */ + _is = urlConnection.getErrorStream(); + } + try (InputStream in = new BufferedInputStream(_is)) { + if (in != null) { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in)); + String line = ""; + + while ((line = bufferedReader.readLine()) != null) { + result += line; + } + } + } + return result; + } + +} diff --git a/itests/pom.xml b/itests/pom.xml index 2b8d9b740debe..2a1c490bf0d67 100644 --- a/itests/pom.xml +++ b/itests/pom.xml @@ -8,7 +8,6 @@ org.openhab.addons org.openhab.addons.reactor 2.5.0-SNAPSHOT - ../poms/bnd org.openhab.addons.itests @@ -20,14 +19,17 @@ org.openhab.binding.astro.tests org.openhab.binding.avmfritz.tests + org.openhab.binding.feed.tests org.openhab.binding.hue.tests org.openhab.binding.max.tests org.openhab.binding.mqtt.homeassistant.tests org.openhab.binding.mqtt.homie.tests org.openhab.binding.nest.tests org.openhab.binding.ntp.tests + org.openhab.binding.systeminfo.tests org.openhab.binding.tradfri.tests org.openhab.binding.wemo.tests + org.openhab.io.hueemulation.tests org.openhab.persistence.mapdb.tests diff --git a/pom.xml b/pom.xml index e4124116f8e19..17db62cf6d6d0 100644 --- a/pom.xml +++ b/pom.xml @@ -1,24 +1,773 @@ - + + 4.0.0 - org.openhab.addons.tmp - org.openhab.addons.tmp + org.openhab.addons + org.openhab.addons.reactor 2.5.0-SNAPSHOT + pom + openHAB 2 Add-ons + This project contains the official add-ons of openHAB + + + openHAB.org + http://www.openhab.org + + + + + Eclipse Public License 2.0 + https://www.eclipse.org/legal/epl-2.0/ + + + + + scm:git:https://github.com/openhab/openhab2-addons.git + scm:git:https://github.com/openhab/openhab2-addons.git + HEAD + https://github.com/openhab/openhab2-addons + + + + Github + https://github.com/openhab/openhab2-addons/issues + + + + + bintray + https://api.bintray.com/maven/openhab/mvn/openhab2-addons/;publish=1 + + + jfrog + https://openhab.jfrog.io/openhab/libs-snapshot-local + + + + + UTF-8 + UTF-8 + 1.8 + ${oh.java.version} + ${oh.java.version} + ${oh.java.version} + + 4.2.0 + 4.2.4 + 0.6.1 + 1.7.21 + + + + + + + + + + jcenter + JCenter Repository + https://jcenter.bintray.com + + true + never + + + false + + + + + openhab-artifactory-release + JFrog Artifactory Repository + https://openhab.jfrog.io/openhab/libs-release + + true + never + + + false + + + + + + + jcenter + https://jcenter.bintray.com + + + openhab-artifactory-release + https://openhab.jfrog.io/openhab/libs-release + + + + + + + org.openhab.core.bom + org.openhab.core.bom.compile + ${project.version} + pom + provided + + + org.openhab.core.bom + org.openhab.core.bom.compile-model + ${project.version} + pom + provided + + + org.openhab.core.bom + org.openhab.core.bom.openhab-core + ${project.version} + pom + provided + + + org.openhab.core.bom + org.openhab.core.bom.runtime + ${project.version} + pom + runtime + + + org.openhab.core.bom + org.openhab.core.bom.test + ${project.version} + pom + test + + + + + + + + src/main/resources + + + . + + NOTICE + *.xsd + + + + + + + + + + + biz.aQute.bnd + bnd-maven-plugin + ${bnd.version} + + + + + + + + + bnd-process + + + + + + + + + + biz.aQute.bnd + bnd-indexer-maven-plugin + ${bnd.version} + + REQUIRED + false + + + + index + + index + + + ${project.artifactId} + + + + test-index + + index + + + ${project.artifactId} + ${project.build.directory}/test-index.xml + + test + + + + + + + + + biz.aQute.bnd + bnd-resolver-maven-plugin + ${bnd.version} + + false + + + + + + + resolve + + + + + + + + biz.aQute.bnd + bnd-export-maven-plugin + ${bnd.version} + + true + true + + + + + export + + + + + + + + biz.aQute.bnd + bnd-testing-maven-plugin + ${bnd.version} + + + + testing + + + + + + + + + biz.aQute.bnd + bnd-baseline-maven-plugin + ${bnd.version} + + false + + + + + baseline + + + + + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.0.0 + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + eclipse + + -err:+nullAnnot(org.eclipse.jdt.annotation.Nullable|org.eclipse.jdt.annotation.NonNull|org.eclipse.jdt.annotation.NonNullByDefault),+inheritNullAnnot,-nullUncheckedConversion + -warn:+null,+inheritNullAnnot,+nullAnnotConflict,-nullUncheckedConversion,+nullAnnotRedundant,+nullDereference + + true + true + + + + org.codehaus.plexus + plexus-compiler-eclipse + 2.8.5 + + + org.eclipse.jdt + ecj + 3.16.0 + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M2 + + + + org.apache.maven.plugins + maven-install-plugin + 2.5.2 + + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.3 + + !${quality.skip} + + + + + org.apache.maven.plugins + maven-plugin-plugin + 3.6.0 + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.2 + + clean install + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.0.2 + + + + org.apache.maven.plugins + maven-site-plugin + 3.7.1 + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.1 + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + + com.mycila + license-maven-plugin + 3.0 + + ${basedir} +

    licenses/epl-2.0/header.txt
    + false + true + true + true + + xml-header-style + + + licenses/epl-2.0/xml-header-style.xml + + + **/org/openhab/**/*.java + **/feature.xml + **/OSGI-INF/*.xml + + + target/** + **/pom.xml + _*.java + + true + + 2019 + + UTF-8 + + + + + check + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + [4.2.1,) + + features-generate-descriptor + verify + + + + + + + + + org.codehaus.mojo + exec-maven-plugin + [1.4.0,) + + java + + + + + + + + + biz.aQute.bnd + bnd-indexer-maven-plugin + [3.1.0,) + + index + local-index + + + + + + + + + org.commonjava.maven.plugins + directory-maven-plugin + [0.3.1,) + + directory-of + + + + + + + + + + + + org.openhab.tools.sat + sat-plugin + ${sat.version} + + ${basedirRoot}/tools/checkstyle/ruleset.properties + ${basedirRoot}/tools/checkstyle/suppressions.xml + + + + sat-all + + checkstyle + pmd + spotbugs + report + + verify + + + + + + + + + org.commonjava.maven.plugins + directory-maven-plugin + 0.3.1 + + + directories + + directory-of + + initialize + + basedirRoot + + org.openhab.addons + org.openhab.addons.reactor + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-java + + enforce + + + + + [1.8.0-40,1.9),[9.0,12.0) + + + + + + + + + + + + skip-check + + + skipChecks + + + + + + + org.openhab.tools.sat + sat-plugin + ${sat.version} + + + sat-all + none + + + + + + + + + check-bundles + + + src + + + + + + org.openhab.tools.sat + sat-plugin + + + + + + add-libraries-cli + + + lib/ + + + !m2e.version + + + + ${.}/../../lib/; filter:=*.jar; lib:=true + + + + + com.googlecode.addjars-maven-plugin + addjars-maven-plugin + 1.0.5 + + + + add-jars + + + + + ${project.basedir}/lib + + **/*.jar + + provided + + + + + + + + + - + + skip-itest + + + !skipTests + + + + itests + + + + with-bnd-resolver-resolve + + + withResolver + + + + + + + biz.aQute.bnd + bnd-resolver-maven-plugin + ${bnd.version} + + + + resolve + + package + + + + + + + + + openhab-snapshot-repository + + + !noOhSnapRepo + + + + + openhab-artifactory-snapshot + JFrog Artifactory Repository + https://openhab.jfrog.io/openhab/libs-snapshot + + false + + + true + always + + + + + - poms/tycho - poms/bnd + bom + bundles + features diff --git a/poms/bnd/pom.xml b/poms/bnd/pom.xml deleted file mode 100644 index 146c752495065..0000000000000 --- a/poms/bnd/pom.xml +++ /dev/null @@ -1,769 +0,0 @@ - - - 4.0.0 - - org.openhab.addons - org.openhab.addons.reactor - 2.5.0-SNAPSHOT - - pom - - openHAB Add-ons - This project contains the add-ons of openHAB - - - openHAB.org - http://www.openhab.org - - - - - Eclipse Public License 2.0 - https://www.eclipse.org/legal/epl-2.0/ - - - - - scm:git:https://github.com/openhab/openhab2-addons.git - scm:git:https://github.com/openhab/openhab2-addons.git - HEAD - https://github.com/openhab/openhab2-addons - - - - Github - https://github.com/openhab/openhab2-addons/issues - - - - - bintray - https://api.bintray.com/maven/openhab/mvn/openhab2-addons/;publish=1 - - - jfrog - https://openhab.jfrog.io/openhab/libs-snapshot-local - - - - - UTF-8 - UTF-8 - 1.8 - ${oh.java.version} - ${oh.java.version} - ${oh.java.version} - - 4.2.0 - 4.2.2 - 0.6.1 - 1.7.21 - - - - - - - - - jcenter - JCenter Repository - https://jcenter.bintray.com - - true - never - - - false - - - - - openhab-artifactory-release - JFrog Artifactory Repository - https://openhab.jfrog.io/openhab/libs-release - - true - never - - - false - - - - - - - jcenter - https://jcenter.bintray.com - - - openhab-artifactory-release - https://openhab.jfrog.io/openhab/libs-release - - - - - - - org.openhab.core.bom - org.openhab.core.bom.compile - ${project.version} - pom - provided - - - org.openhab.core.bom - org.openhab.core.bom.compile-model - ${project.version} - pom - provided - - - org.openhab.core.bom - org.openhab.core.bom.openhab-core - ${project.version} - pom - provided - - - org.openhab.core.bom - org.openhab.core.bom.runtime - ${project.version} - pom - runtime - - - org.openhab.core.bom - org.openhab.core.bom.test - ${project.version} - pom - test - - - - - - - - src/main/resources - - - . - - NOTICE - *.xsd - - - - - - - - - - - biz.aQute.bnd - bnd-maven-plugin - ${bnd.version} - - - - - - - - - bnd-process - - - - - - - - - - biz.aQute.bnd - bnd-indexer-maven-plugin - ${bnd.version} - - REQUIRED - false - - - - index - - index - - - ${project.artifactId} - - - - test-index - - index - - - ${project.artifactId} - ${project.build.directory}/test-index.xml - - test - - - - - - - - - biz.aQute.bnd - bnd-resolver-maven-plugin - ${bnd.version} - - false - - - - - - - resolve - - - - - - - - biz.aQute.bnd - bnd-export-maven-plugin - ${bnd.version} - - true - true - - - - - export - - - - - - - - biz.aQute.bnd - bnd-testing-maven-plugin - ${bnd.version} - - - - testing - - - - - - - - - biz.aQute.bnd - bnd-baseline-maven-plugin - ${bnd.version} - - false - - - - - baseline - - - - - - - - - org.apache.maven.plugins - maven-clean-plugin - 3.0.0 - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.0 - - eclipse - - -err:+nullAnnot(org.eclipse.jdt.annotation.Nullable|org.eclipse.jdt.annotation.NonNull|org.eclipse.jdt.annotation.NonNullByDefault),+inheritNullAnnot,-nullUncheckedConversion - -warn:+null,+inheritNullAnnot,+nullAnnotConflict,-nullUncheckedConversion,+nullAnnotRedundant,+nullDereference - - true - true - - - - org.codehaus.plexus - plexus-compiler-eclipse - 2.8.5 - - - org.eclipse.jdt - ecj - 3.16.0 - - - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.8.2 - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.0.0-M2 - - - - org.apache.maven.plugins - maven-install-plugin - 2.5.2 - - - - org.apache.maven.plugins - maven-jar-plugin - 3.0.2 - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.3 - - !${quality.skip} - - - - - org.apache.maven.plugins - maven-plugin-plugin - 3.6.0 - - - - org.apache.maven.plugins - maven-release-plugin - 2.5.2 - - clean install - - - - - org.apache.maven.plugins - maven-resources-plugin - 3.0.2 - - - - org.apache.maven.plugins - maven-site-plugin - 3.7.1 - - - - org.apache.maven.plugins - maven-source-plugin - 3.0.1 - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.1 - - - - org.codehaus.mojo - build-helper-maven-plugin - 3.0.0 - - - - com.mycila - license-maven-plugin - 3.0 - - ${basedir} -
    licenses/epl-2.0/header.txt
    - false - true - true - true - - xml-header-style - - - licenses/epl-2.0/xml-header-style.xml - - - **/org/openhab/**/*.java - **/feature.xml - **/OSGI-INF/*.xml - - - target/** - **/pom.xml - _*.java - - true - - 2019 - - UTF-8 -
    - - - - check - - - -
    - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.apache.karaf.tooling - karaf-maven-plugin - [4.2.1,) - - features-generate-descriptor - verify - - - - - - - - - org.codehaus.mojo - exec-maven-plugin - [1.4.0,) - - java - - - - - - - - - biz.aQute.bnd - bnd-indexer-maven-plugin - [3.1.0,) - - index - local-index - - - - - - - - - org.commonjava.maven.plugins - directory-maven-plugin - [0.3.1,) - - directory-of - - - - - - - - - - - - org.openhab.tools.sat - sat-plugin - ${sat.version} - - ${basedirRoot}/../../tools/checkstyle/ruleset.properties - ${basedirRoot}/../../tools/checkstyle/suppressions.xml - - - - sat-all - - checkstyle - pmd - spotbugs - report - - verify - - - -
    -
    - - - - org.commonjava.maven.plugins - directory-maven-plugin - 0.3.1 - - - directories - - directory-of - - initialize - - basedirRoot - - org.openhab.addons - org.openhab.addons.reactor - - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - - - enforce-java - - enforce - - - - - [1.8.0-40,1.9),[9.0,12.0) - - - - - - - -
    - - - - skip-check - - - skipChecks - - - - - - - org.openhab.tools.sat - sat-plugin - ${sat.version} - - - sat-all - none - - - - - - - - - check-bundles - - - src - - - - - - org.openhab.tools.sat - sat-plugin - - - - - - add-libraries-cli - - - lib/ - - - !m2e.version - - - - ${.}/../../lib/; filter:=*.jar; lib:=true - - - - - com.googlecode.addjars-maven-plugin - addjars-maven-plugin - 1.0.5 - - - - add-jars - - - - - ${project.basedir}/lib - - **/*.jar - - provided - - - - - - - - - - - - add-libraries-eclipse - - - lib/ - - - m2e.version - - - - ${.}/lib/; filter:=*.jar; lib:=true - - - - - skip-itest - - - !skipTests - - - - ../../itests - - - - with-bnd-resolver-resolve - - - withResolver - - - - - - - biz.aQute.bnd - bnd-resolver-maven-plugin - ${bnd.version} - - - - resolve - - package - - - - - - - - - openhab-snapshot-repository - - - !noOhSnapRepo - - - - - openhab-artifactory-snapshot - JFrog Artifactory Repository - https://openhab.jfrog.io/openhab/libs-snapshot - - false - - - true - always - - - - - - - - ../../bom - ../../bundles - ../../features - - -
    diff --git a/poms/tycho/pom.xml b/poms/tycho/pom.xml deleted file mode 100644 index 397f0bbc157fa..0000000000000 --- a/poms/tycho/pom.xml +++ /dev/null @@ -1,237 +0,0 @@ - - 4.0.0 - - - org.openhab - pom-tycho - 2.5.0-SNAPSHOT - - - - org.openhab - pom-addons2 - 2.5.0-SNAPSHOT - pom - openHAB 2 Add-ons - - This is the open Home Automation Bus (openHAB) - - - openHAB.org - http://www.openhab.org - - - - - Eclipse Public License 2.0 - https://www.eclipse.org/legal/epl-2.0/ - - - - - ../../addons - - - - scm:git:${scm.gitBaseUrl}/openhab2-addons.git - scm:git:${scm.gitBaseUrl}/openhab2-addons.git - HEAD - https://github.com/openhab/openhab2-addons - - - - Github - https://github.com/openhab/openhab2-addons/issues - - - - - bintray - https://api.bintray.com/maven/openhab/mvn/openhab2-addons/;publish=1 - - - jfrog - https://openhab.jfrog.io/openhab/libs-snapshot-local - - - - - 2.5.0-SNAPSHOT - - - - - - - - true - never - - - false - - jcenter - JCenter Repository - https://jcenter.bintray.com/ - - - - - true - never - - - false - - openhab-artifactory-release - JFrog Artifactory Repository - https://openhab.jfrog.io/openhab/libs-release - - - - - p2-openhab-deps-repo - https://dl.bintray.com/openhab/p2/openhab-deps-repo/${ohdr.version} - p2 - - - - - - - - true - never - - - false - - jcenter - JCenter Repository - https://jcenter.bintray.com/ - - - - true - never - - - false - - artifactory - JFrog Artifactory Repository - https://openhab.jfrog.io/openhab/libs-release - - - - - - - - org.commonjava.maven.plugins - directory-maven-plugin - 0.3.1 - - - tycho-dir - - directory-of - - initialize - - tychodirRoot - - org.openhab - pom-addons2 - - - - - - - org.openhab.tools.sat - sat-plugin - {sat.version} - - ${tychodirRoot}/../../tools/checkstyle.properties - ${tychodirRoot}/../../tools/checkstyle_suppressions.xml - - - - - com.mycila - license-maven-plugin - 3.0 - - ${basedirRoot} -
    licenses/epl-2.0/header.txt
    - false - true - true - true - - xml-header-style - - - licenses/epl-2.0/xml-header-style.xml - - - **/org/openhab/**/*.java - **/feature.xml - - - target/** - **/pom.xml - _*.java - - true - - 2019 - - UTF-8 -
    - - - - check - - - -
    - -
    -
    - - - org.commonjava.maven.plugins - directory-maven-plugin - - -
    - - - - openhab-snapshot-repository - - - !noOhSnapRepo - - - - - openhab-artifactory-snapshot - JFrog Artifactory Repository - https://openhab.jfrog.io/openhab/libs-snapshot - - false - - - true - always - - - - - - -
    diff --git a/tools/checkstyle/suppressions.xml b/tools/checkstyle/suppressions.xml index b12a9ff80f48c..5f45b79892423 100644 --- a/tools/checkstyle/suppressions.xml +++ b/tools/checkstyle/suppressions.xml @@ -19,4 +19,6 @@ + +