diff --git a/.gitignore b/.gitignore index ebbad56526f89..22acbbd1f42db 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .DS_Store *.iml npm-debug.log +.build.log .metadata/ bin/ @@ -16,3 +17,6 @@ xtend-gen/ bundles/**/src/main/history features/**/src/main/history features/**/src/main/feature + +.vscode +.factorypath diff --git a/.travis.yml b/.travis.yml index b5f2860ca3816..1897e094dc88e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,42 +10,18 @@ jdk: cache: directories: - $HOME/.m2 - - $HOME/.p2 + - $HOME/.bnd/cache/ before_cache: # remove resolver-status.properties, they change with each run and invalidate the cache - find $HOME/.m2 -name resolver-status.properties -exec rm {} \; - -before_install: - - echo "MAVEN_OPTS='-Xms1g -Xmx2g -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn'" > ~/.mavenrc -install: - - | - function prevent_timeout() { - local i=0 - while [ -e /proc/$1 ]; do - # print zero width char every 3 minutes while building - if [ "$i" -eq "180" ]; then printf %b '\u200b'; i=0; else i=$((i+1)); fi - sleep 1 - done - } - function print_reactor_summary() { - sed -ne '/\[INFO\] Reactor Summary:/,$ p' "$1" | sed 's/\[INFO\] //' - } - function mvnp() { - set -o pipefail # exit build with error when pipes fail - local command=(mvn $@) - exec "${command[@]}" 2>&1 | # execute, redirect stderr to stdout - tee .build.log | # write output to log - stdbuf -oL grep -E '^\[INFO\] Building .+ \[.+\]$' | # filter progress - sed -uE 's/^\[INFO\] Building (.*[^ ])[ ]+\[([0-9]+\/[0-9]+)\]$/\2| \1/' | # prefix project name with progress - sed -e :a -e 's/^.\{1,6\}|/ &/;ta' & # right align progress with padding - local pid=$! - prevent_timeout $pid & - wait $pid - } -after_success: - - print_reactor_summary .build.log -after_failure: - - tail -n 2000 .build.log -script: - - mvnp clean install -B -DskipChecks=true -DskipTests=true + +notifications: + webhooks: https://www.travisbuddy.com/ + +travisBuddy: + insertMode: update + successBuildLog: true + +install: true +script: ./buildci.sh "$TRAVIS_COMMIT_RANGE" diff --git a/CODEOWNERS b/CODEOWNERS index 2ca976d6f5e9f..63afc660f4523 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -122,6 +122,7 @@ /bundles/org.openhab.binding.plugwise/ @wborn /bundles/org.openhab.binding.powermax/ @lolodomo /bundles/org.openhab.binding.pulseaudio/ @peuter +/bundles/org.openhab.binding.pushbullet/ @hakan42 /bundles/org.openhab.binding.regoheatpump/ @crnjan /bundles/org.openhab.binding.rfxcom/ @martinvw @paulianttila /bundles/org.openhab.binding.rme/ @kgoderis diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index bf733d6844d3b..6f00784adc50a 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -600,6 +600,11 @@ org.openhab.binding.pulseaudio ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.pushbullet + ${project.version} + org.openhab.addons.bundles org.openhab.binding.regoheatpump diff --git a/buildci.sh b/buildci.sh new file mode 100755 index 0000000000000..f2b5072b08fbd --- /dev/null +++ b/buildci.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +function prevent_timeout() { + local i=0 + while [ -e /proc/$1 ]; do + # print zero width char every 3 minutes while building + if [ "$i" -eq "180" ]; then printf %b '\u200b'; i=0; else i=$((i+1)); fi + sleep 1 + done +} + +function print_reactor_summary() { + sed -ne '/\[INFO\] Reactor Summary:/,$ p' "$1" | sed 's/\[INFO\] //' +} + +function mvnp() { + set -o pipefail # exit build with error when pipes fail + local command=(mvn $@) + exec "${command[@]}" 2>&1 | # execute, redirect stderr to stdout + stdbuf -o0 grep -vE "Download(ed|ing) from [a-z.]+: https:" | # filter out downloads + tee .build.log | # write output to log + stdbuf -oL grep -E '^\[INFO\] Building .+ \[.+\]$' | # filter progress + stdbuf -o0 sed -uE 's/^\[INFO\] Building (.*[^ ])[ ]+\[([0-9]+\/[0-9]+)\]$/\2| \1/' | # prefix project name with progress + stdbuf -o0 sed -e :a -e 's/^.\{1,6\}|/ &/;ta' & # right align progress with padding + local pid=$! + prevent_timeout $pid & + wait $pid +} + +COMMITS=${1:-"master...HEAD"} + +# Determine if this is a new addon -> Perform tests + integration tests and all SAT checks with increased warning level +CHANGED_DIR=`git diff --dirstat=files,0 $COMMITS bundles/ | sed 's/^[ 0-9.]\+% bundles\///g' | grep -o -P "^([^/]*)" | uniq` +CDIR=`pwd` + +if [ ! -z "$CHANGED_DIR" ] && [ -e "bundles/$CHANGED_DIR" ]; then + echo "Single addon pull request: Building $CHANGED_DIR" + echo "MAVEN_OPTS='-Xms1g -Xmx2g -Dorg.slf4j.simpleLogger.log.org.openhab.tools.analysis.report.ReportUtility=DEBUG -Dorg.slf4j.simpleLogger.defaultLogLevel=WARN'" > ~/.mavenrc + cd "bundles/$CHANGED_DIR" + mvn clean install -B 2>&1 | + stdbuf -o0 grep -vE "Download(ed|ing) from [a-z.]+: https:" | # Filter out Download(s) + stdbuf -o0 grep -v "target/code-analysis" | # filter out some debug code from reporting utility + tee $CDIR/.build.log + if [ $? -ne 0 ]; then + exit 1 + fi + + if [ -e "../itests/$CHANGED_DIR" ]; then + echo "Single addon pull request: Building itest $CHANGED_DIR" + cd "../itests/$CHANGED_DIR" + mvn clean install -B 2>&1 | + stdbuf -o0 grep -vE "Download(ed|ing) from [a-z.]+: https:" | # Filter out Download(s) + stdbuf -o0 grep -v "target/code-analysis" | # filter out some debug code from reporting utility + tee -a $CDIR/.build.log + if [ $? -ne 0 ]; then + exit 1 + fi + fi +else + echo "Build all" + echo "MAVEN_OPTS='-Xms1g -Xmx2g -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn'" > ~/.mavenrc + mvnp clean install -B -DskipChecks=true -DskipTests=true + if [ $? -eq 0 ]; then + print_reactor_summary .build.log + else + tail -n 1000 .build.log + exit 1 + fi +fi diff --git a/bundles/org.openhab.binding.allplay/lib/tchaikovsky-sources.jar b/bundles/org.openhab.binding.allplay/lib/tchaikovsky-sources.jar deleted file mode 100644 index c3c04bb5785ab..0000000000000 Binary files a/bundles/org.openhab.binding.allplay/lib/tchaikovsky-sources.jar and /dev/null differ diff --git a/bundles/org.openhab.binding.allplay/lib/tchaikovsky.jar b/bundles/org.openhab.binding.allplay/lib/tchaikovsky.jar deleted file mode 100644 index c5eb425105870..0000000000000 Binary files a/bundles/org.openhab.binding.allplay/lib/tchaikovsky.jar and /dev/null differ diff --git a/bundles/org.openhab.binding.allplay/pom.xml b/bundles/org.openhab.binding.allplay/pom.xml index b7a991931811c..e792ca0297934 100644 --- a/bundles/org.openhab.binding.allplay/pom.xml +++ b/bundles/org.openhab.binding.allplay/pom.xml @@ -13,8 +13,13 @@ openHAB Add-ons :: Bundles :: AllPlay Binding - - org.slf4j.impl.*;resolution:=optional,de.kaizencode.tchaikovsky.* - + + + org.openhab.osgiify + de.kaizencode.tchaikovsky + 0.0.1 + provided + + diff --git a/bundles/org.openhab.binding.allplay/src/main/feature/feature.xml b/bundles/org.openhab.binding.allplay/src/main/feature/feature.xml index 386e237564666..0beed0ef008a1 100644 --- a/bundles/org.openhab.binding.allplay/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.allplay/src/main/feature/feature.xml @@ -4,6 +4,7 @@ openhab-runtime-base + mvn:org.openhab.osgiify/de.kaizencode.tchaikovsky/0.0.1 mvn:org.openhab.addons.bundles/org.openhab.binding.allplay/${project.version} diff --git a/bundles/org.openhab.binding.amazondashbutton/pom.xml b/bundles/org.openhab.binding.amazondashbutton/pom.xml index c68f3dda9127c..ffa046ecdae10 100644 --- a/bundles/org.openhab.binding.amazondashbutton/pom.xml +++ b/bundles/org.openhab.binding.amazondashbutton/pom.xml @@ -21,14 +21,14 @@ provided - org.pcap4j - pcap4j-core + org.openhab.osgiify + org.pcap4j.pcap4j-core 1.6.6 provided - org.pcap4j - pcap4j-packetfactory-static + org.openhab.osgiify + org.pcap4j.pcap4j-packetfactory-static 1.6.6 provided diff --git a/bundles/org.openhab.binding.amazondashbutton/src/main/feature/feature.xml b/bundles/org.openhab.binding.amazondashbutton/src/main/feature/feature.xml index bf017f318d3e9..eb9e5f48a4c1a 100644 --- a/bundles/org.openhab.binding.amazondashbutton/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.amazondashbutton/src/main/feature/feature.xml @@ -4,10 +4,9 @@ openhab-runtime-base - wrap mvn:net.java.dev.jna/jna/4.2.1 - wrap:mvn:org.pcap4j/pcap4j-core/1.6.6$Bundle-Name=Pcap4J%20Core&Bundle-SymbolicName=org.pcap4j.pcap4j-core&Bundle-Version=1.6.6 - wrap:mvn:org.pcap4j/pcap4j-packetfactory-static/1.6.6$Bundle-Name=Pcap4J%20Static%20Packet%20Factory&Bundle-SymbolicName=org.pcap4j.pcap4j-packetfactory-static&Bundle-Version=1.6.6 + mvn:org.openhab.osgiify/org.pcap4j.pcap4j-core/1.6.6 + mvn:org.openhab.osgiify/org.pcap4j.pcap4j-packetfactory-static/1.6.6 mvn:org.openhab.addons.bundles/org.openhab.binding.amazondashbutton/${project.version} diff --git a/bundles/org.openhab.binding.amazonechocontrol/.classpath b/bundles/org.openhab.binding.amazonechocontrol/.classpath index 084e4c97f76f2..a5d95095ccaaf 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/.classpath +++ b/bundles/org.openhab.binding.amazonechocontrol/.classpath @@ -1,14 +1,13 @@ - + + - - + - @@ -19,6 +18,11 @@ + + + + + diff --git a/bundles/org.openhab.binding.amazonechocontrol/.project b/bundles/org.openhab.binding.amazonechocontrol/.project index 29779e20d1765..875f2ada3f4f4 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/.project +++ b/bundles/org.openhab.binding.amazonechocontrol/.project @@ -10,21 +10,6 @@ - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - org.eclipse.m2e.core.maven2Builder @@ -32,8 +17,7 @@ - org.eclipse.m2e.core.maven2Nature - org.eclipse.pde.PluginNature org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature diff --git a/bundles/org.openhab.binding.amazonechocontrol/README.md b/bundles/org.openhab.binding.amazonechocontrol/README.md index f4a85122bdda7..6c788b07af8f0 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/README.md +++ b/bundles/org.openhab.binding.amazonechocontrol/README.md @@ -1,6 +1,6 @@ # Amazon Echo Control Binding -This binding can control Amazon Echo devices (Alexa) and their smart bulbs. +This binding can control Amazon Echo devices (Alexa). It provides features to control and view the current state of echo devices: @@ -23,6 +23,8 @@ It provides features to control and view the current state of echo devices: - start playing music by providing the voice command as text (Works with all music providers) - get last spoken voice command - change the volume of the alarm +- change the equalizer settings +- get information about the next alarm, reminder and timer Also this binding includes the features to control your lights connected to your Echo devices: @@ -50,6 +52,8 @@ Some ideas what you can do in your home by using rules and other openHAB control - Have different flash briefing in the morning and evening - Let alexa say 'welcome' to you if you open the door - Implement own handling for voice commands in a rule +- Change the equalizer settings depending on the bluetooth connection +- Turn on a light on your alexa alarm time With the possibility to control your lights you could do: @@ -93,9 +97,6 @@ If the device type is not known by the binding, the device will not be discovere But you can define any device listed in your alexa app with the best matching existing device (e.g. echo). You will find the required serial number in settings of the device in the alexa app. -Also you will see your lights in the inbox of openhab. They will automatically be discoverd by openhab based on your amazon login. -If you configured any groups of lights in your amazon alexa app they will be discovered by openhab too. - ## Binding Configuration The binding does not have any configuration. @@ -122,40 +123,48 @@ It will be configured at runtime by using the save channel to store the current ## Channels -| Channel Type ID | Item Type | Access Mode | Thing Type | Description -|-----------------------|-------------|-----------|-------------------------------|----------------------------------------------------------------------- -| player | Player | R/W | echo, echoshow, echospot, wha | Control the music player e.g. pause/continue/next track/previous track -| volume | Dimmer | R/W | echo, echoshow, echospot | Control the volume -| shuffle | Switch | R/W | echo, echoshow, echospot, wha | Shuffle play if applicable, e.g. playing a playlist -| imageUrl | String | R | echo, echoshow, echospot, wha | Url of the album image or radio station logo -| title | String | R | echo, echoshow, echospot, wha | Title of the current media -| subtitle1 | String | R | echo, echoshow, echospot, wha | Subtitle of the current media -| subtitle2 | String | R | echo, echoshow, echospot, wha | Additional subtitle of the current media -| providerDisplayName | String | R | echo, echoshow, echospot, wha | Name of the music provider -| bluetoothMAC | String | R/W | echo, echoshow, echospot | Bluetooth device MAC. Used to connect to a specific device or disconnect if a empty string was provided -| bluetooth | Switch | R/W | echo, echoshow, echospot | Connect/Disconnect to the last used bluetooth device (works after a bluetooth connection was established after the openHAB start) -| bluetoothDeviceName | String | R | echo, echoshow, echospot | User friendly name of the connected bluetooth device -| radioStationId | String | R/W | echo, echoshow, echospot, wha | Start playing of a TuneIn radio station by specifying it's id or stops playing if a empty string was provided -| radio | Switch | R/W | echo, echoshow, echospot, wha | Start playing of the last used TuneIn radio station (works after the radio station started after the openhab start) -| amazonMusicTrackId | String | R/W | echo, echoshow, echospot, wha | Start playing of a Amazon Music track by it's id od stops playing if a empty string was provided +| Channel Type ID | Item Type | Access Mode | Thing Type | Description +|-----------------------|-------------|-------------|------------|------------------------------------------------------------------------------------------ +| player | Player | R/W | echo, echoshow, echospot, wha | Control the music player (Supported commands: PLAY or ON, PAUSE or OFF, NEXT, PREVIOUS, REWIND, FASTFORWARD) +| volume | Dimmer | R/W | echo, echoshow, echospot | Control the volume +| equalizerTreble | Number | R/W | echo, echoshow, echospot | Control the treble (value from -6 to 6) +| equalizerMidrange | Number | R/W | echo, echoshow, echospot | Control the midrange (value from -6 to 6) +| equalizerBass | Number | R/W | echo, echoshow, echospot | Control the bass (value from -6 to 6) +| shuffle | Switch | R/W | echo, echoshow, echospot, wha | Shuffle play if applicable, e.g. playing a playlist +| imageUrl | String | R | echo, echoshow, echospot, wha | Url of the album image or radio station logo +| title | String | R | echo, echoshow, echospot, wha | Title of the current media +| subtitle1 | String | R | echo, echoshow, echospot, wha | Subtitle of the current media +| subtitle2 | String | R | echo, echoshow, echospot, wha | Additional subtitle of the current media +| providerDisplayName | String | R | echo, echoshow, echospot, wha | Name of the music provider +| bluetoothMAC | String | R/W | echo, echoshow, echospot | Bluetooth device MAC. Used to connect to a specific device or disconnect if a empty string was provided +| bluetooth | Switch | R/W | echo, echoshow, echospot | Connect/Disconnect to the last used bluetooth device (works after a bluetooth connection was established after the openHAB start) +| bluetoothDeviceName | String | R | echo, echoshow, echospot | User friendly name of the connected bluetooth device +| radioStationId | String | R/W | echo, echoshow, echospot, wha | Start playing of a TuneIn radio station by specifying it's id or stops playing if a empty string was provided +| radio | Switch | R/W | echo, echoshow, echospot, wha | Start playing of the last used TuneIn radio station (works after the radio station started after the openhab start) +| amazonMusicTrackId | String | R/W | echo, echoshow, echospot, wha | Start playing of a Amazon Music track by it's id od stops playing if a empty string was provided | amazonMusicPlayListId | String | W | echo, echoshow, echospot, wha | Write Only! Start playing of a Amazon Music playlist by specifying it's id od stops playing if a empty string was provided. Selection will only work in PaperUI -| amazonMusic | Switch | R/W | echo, echoshow, echospot, wha | Start playing of the last used Amazon Music song (works after at least one song was started after the openhab start) -| remind | String | R/W | echo, echoshow, echospot | Write Only! Speak the reminder and sends a notification to the Alexa app (Currently the reminder is played and notified two times, this seems to be a bug in the amazon software) -| startRoutine | String | W | echo, echoshow, echospot | Write Only! Type in what you normally say to Alexa without the preceding "Alexa," -| musicProviderId | String | R/W | echo, echoshow, echospot | Current Music provider +| amazonMusic | Switch | R/W | echo, echoshow, echospot, wha | Start playing of the last used Amazon Music song (works after at least one song was started after the openhab start) +| remind | String | R/W | echo, echoshow, echospot | Write Only! Speak the reminder and sends a notification to the Alexa app (Currently the reminder is played and notified two times, this seems to be a bug in the amazon software) +| nextReminder | DateTime | R | echo, echoshow, echospot | Next reminder on the device +| playAlarmSound | String | W | echo, echoshow, echospot | Write Only! Plays ans Alarm sound +| nextAlarm | DateTime | R | echo, echoshow, echospot | Next alarm on the device +| nextMusicAlarm | DateTime | R | echo, echoshow, echospot | Next music alarm on the device +| nextTimer | DateTime | R | echo, echoshow, echospot | Next timer on the device +| startRoutine | String | W | echo, echoshow, echospot | Write Only! Type in what you normally say to Alexa without the preceding "Alexa," +| musicProviderId | String | R/W | echo, echoshow, echospot | Current Music provider | playMusicVoiceCommand | String | W | echo, echoshow, echospot | Write Only! Voice command as text. E.g. 'Yesterday from the Beatles' | startCommand | String | W | echo, echoshow, echospot | Write Only! Used to start anything. Available options: Weather, Traffic, GoodMorning, SingASong, TellStory, FlashBriefing and FlashBriefing. (Note: The options are case sensitive) -| textToSpeech | String | W | echo, echoshow, echospot | Write Only! Write some text to this channel and alexa will speak it +| textToSpeech | String | W | echo, echoshow, echospot | Write Only! Write some text to this channel and alexa will speak it. It is possible to use plain text or SSML: e.g. `I want to tell you a secret.I am not a real human.` | textToSpeechVolume | Dimmer | R/W | echo, echoshow, echospot | Volume of the textToSpeech channel, if 0 the current volume will be used -| lastVoiceCommand | String | R/W | echo, echoshow, echospot | Last voice command spoken to the device. Writing to the channel starts voice output. -| mediaProgress | Dimmer | R/W | echo, echoshow, echospot | Media progress in percent -| mediaProgressTime | Number:Time | R/W | echo, echoshow, echospot | Media play time -| mediaLength | Number:Time | R | echo, echoshow, echospot | Media length -| notificationVolume | Dimmer | R | echo, echoshow, echospot | Notification volume -| ascendingAlarm | Switch | R/W | echo, echoshow, echospot | Ascending alarm up to the configured volume -| save | Switch | W | flashbriefingprofile | Write Only! Stores the current configuration of flash briefings within the thing -| active | Switch | R/W | flashbriefingprofile | Active the profile -| playOnDevice | String | W | flashbriefingprofile | Specify the echo serial number or name to start the flash briefing. +| lastVoiceCommand | String | R/W | echo, echoshow, echospot | Last voice command spoken to the device. Writing to the channel starts voice output. +| mediaProgress | Dimmer | R/W | echo, echoshow, echospot | Media progress in percent +| mediaProgressTime | Number:Time | R/W | echo, echoshow, echospot | Media play time +| mediaLength | Number:Time | R | echo, echoshow, echospot | Media length +| notificationVolume | Dimmer | R | echo, echoshow, echospot | Notification volume +| ascendingAlarm | Switch | R/W | echo, echoshow, echospot | Ascending alarm up to the configured volume +| save | Switch | W | flashbriefingprofile | Write Only! Stores the current configuration of flash briefings within the thing +| active | Switch | R/W | flashbriefingprofile | Active the profile +| playOnDevice | String | W | flashbriefingprofile | Specify the echo serial number or name to start the flash briefing. | lightState | Switch | R/W | light, lightGroup | Shows and changes the state (ON/OFF) of your light or lightgroup | lightBrightness | Dimmer | R/W | light, lightGroup | Shows and changes the brightness of your light or lightgroup | lightColor | String | R/W | light, lightGroup | Shows and changes the color of your light (groups are not able to show their color!) @@ -199,6 +208,9 @@ Group Alexa_Living_Room // Player control Player Echo_Living_Room_Player "Player" (Alexa_Living_Room) {channel="amazonechocontrol:echo:account1:echo1:player"} Dimmer Echo_Living_Room_Volume "Volume [%.0f %%]" (Alexa_Living_Room) {channel="amazonechocontrol:echo:account1:echo1:volume"} +Number Echo_Living_Room_Treble "Treble" (Alexa_Living_Room) {channel="amazonechocontrol:echo:account1:echo1:equalizerTreble"} +Number Echo_Living_Room_Midrange "Midrange" (Alexa_Living_Room) {channel="amazonechocontrol:echo:account1:echo1:equalizerMidrange"} +Number Echo_Living_Room_Bass "Bass" (Alexa_Living_Room) {channel="amazonechocontrol:echo:account1:echo1:equalizerBass"} Switch Echo_Living_Room_Shuffle "Shuffle" (Alexa_Living_Room) {channel="amazonechocontrol:echo:account1:echo1:shuffle"} // Media channels @@ -243,6 +255,11 @@ Switch Echo_Living_Room_AscendingAlarm "Ascending alarm" // Feedbacks String Echo_Living_Room_LastVoiceCommand "Last voice command" (Alexa_Living_Room) {channel="amazonechocontrol:echo:account1:echo1:lastVoiceCommand"} +DateTime Echo_Living_Room_NextReminder "Next reminder" (Alexa_Living_Room) {channel="amazonechocontrol:echo:account1:echo1:nextReminder"} +DateTime Echo_Living_Room_NextAlarm "Next alarm" (Alexa_Living_Room) {channel="amazonechocontrol:echo:account1:echo1:nextAlarm"} +DateTime Echo_Living_Room_NextMusicAlarm "Next music alarm" (Alexa_Living_Room) {channel="amazonechocontrol:echo:account1:echo1:nextMusicAlarm"} +DateTime Echo_Living_Room_NextTimer "Next timer" (Alexa_Living_Room) {channel="amazonechocontrol:echo:account1:echo1:nextTimer"} + // Flashbriefings Switch FlashBriefing_Technical_Save "Save (Write only)" { channel="amazonechocontrol:flashbriefingprofile:account1:flashbriefing1:save"} @@ -253,6 +270,7 @@ Switch FlashBriefing_LifeStyle_Save "Save (Write only)" { channel="amazonechoc Switch FlashBriefing_LifeStyle_Active "Active" { channel="amazonechocontrol:flashbriefingprofile:account1:flashbriefing2:active"} String FlashBriefing_LifeStyle_Play "Play (Write only)" { channel="amazonechocontrol:flashbriefingprofile:account1:flashbriefing2:playOnDevice"} + // Lights and lightgroups - you will find the applianceId in the properties of your light or lightgroup! Switch Light_State "On/Off" { channel="amazonechocontrol:lightGroup:account1:applianceId:lightState" } Dimmer Light_Brightness "Brightness" { channel="amazonechocontrol:lightGroup:account1:applianceId:lightBrightness" } @@ -268,6 +286,10 @@ sitemap amazonechocontrol label="Echo Devices" Frame label="Alexa" { Default item=Echo_Living_Room_Player Slider item=Echo_Living_Room_Volume + Setpoint item=Echo_Living_Room_Volume minValue=0 maxValue=100 step=5 + Setpoint item=Echo_Living_Room_Treble minValue=-6 maxValue=6 step=1 + Setpoint item=Echo_Living_Room_Midrange minValue=-6 maxValue=6 step=1 + Setpoint item=Echo_Living_Room_Bass minValue=-6 maxValue=6 step=1 Slider item=Echo_Living_Room_MediaProgress Text item=Echo_Living_Room_MediaProgressTime Text item=Echo_Living_Room_MediaLength @@ -318,13 +340,12 @@ sitemap amazonechocontrol label="Echo Devices" Switch item=FlashBriefing_LifeStyle_Active Text item=FlashBriefing_LifeStyle_Play } - - Frame label="Lights and light groups" { - Switch item=Light_State - Slider item=Light_Brightness - Selection item=Light_Color mappings=[ ''='', 'red'='Red', 'crimson'='Crimson', 'salmon'='Salmon', 'orange'='Orange', 'gold'='Gold', 'yellow'='Yellow', 'green'='Green', 'turquoise'='Turquoise', 'cyan'='Cyan', 'sky_blue'='Sky Blue', 'blue'='Blue', 'purple'='Purple', 'magenta'='Magenta', 'pink'='Pink', 'lavender'='Lavender' ] - Selection item=Light_White mappings=[ ''='', 'warm_white'='Warm white', 'soft_white'='Soft white', 'white'='White', 'daylight_white'='Daylight white', 'cool_white'='Cool white' ] - } + Frame label="Lights and light groups" { + Switch item=Light_State + Slider item=Light_Brightness + Selection item=Light_Color mappings=[ ''='', 'red'='Red', 'crimson'='Crimson', 'salmon'='Salmon', 'orange'='Orange', 'gold'='Gold', 'yellow'='Yellow', 'green'='Green', 'turquoise'='Turquoise', 'cyan'='Cyan', 'sky_blue'='Sky Blue', 'blue'='Blue', 'purple'='Purple', 'magenta'='Magenta', 'pink'='Pink', 'lavender'='Lavender' ] + Selection item=Light_White mappings=[ ''='', 'warm_white'='Warm white', 'soft_white'='Soft white', 'white'='White', 'daylight_white'='Daylight white', 'cool_white'='Cool white' ] + } } ``` @@ -350,6 +371,17 @@ then end ``` +You can also use [SSML](https://docs.aws.amazon.com/polly/latest/dg/supported-ssml.html) to provide a better voice experience + +```php +rule "Say welcome if the door opens" +when + Item Door_Contact changed to OPEN +then + Echo_Living_Room_TTS.sendCommand('I want to tell you a secret.I am not a real human..Can you believe it?') +end +``` + ## Playing an alarm sound for 15 seconds with an openHAB rule if an door contact was opened: 1) Do get the ID of your sound, follow the steps in "How To Get IDs" diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/AccountServlet.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/AccountServlet.java index e977ef38aa323..a4c32e59db2ab 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/AccountServlet.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/AccountServlet.java @@ -76,19 +76,20 @@ public class AccountServlet extends HttpServlet { private final Logger logger = LoggerFactory.getLogger(AccountServlet.class); - HttpService httpService; + final HttpService httpService; String servletUrlWithoutRoot; - String servletUrl; + final String servletUrl; AccountHandler account; String id; @Nullable Connection connectionToInitialize; - Gson gson = new Gson(); + final Gson gson; - public AccountServlet(HttpService httpService, String id, AccountHandler account) { + public AccountServlet(HttpService httpService, String id, AccountHandler account, Gson gson) { this.httpService = httpService; this.account = account; this.id = id; + this.gson = gson; try { servletUrlWithoutRoot = "amazonechocontrol/" + URLEncoder.encode(id, "UTF8"); } catch (UnsupportedEncodingException e) { @@ -112,7 +113,7 @@ private Connection reCreateConnection() { if (oldConnection == null) { oldConnection = account.findConnection(); } - return new Connection(oldConnection); + return new Connection(oldConnection, this.gson); } public void dispose() { @@ -157,7 +158,7 @@ void doVerb(String verb, @Nullable HttpServletRequest req, @Nullable HttpServlet Map map = req.getParameterMap(); String domain = map.get("domain")[0]; String loginData = connection.serializeLoginData(); - Connection newConnection = new Connection(null); + Connection newConnection = new Connection(null, this.gson); if (newConnection.tryRestoreLogin(loginData, domain)) { account.setConnection(newConnection); } @@ -282,7 +283,7 @@ protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResp } // handle commands if (baseUrl.equals("/newdevice") || baseUrl.equals("/newdevice/")) { - this.connectionToInitialize = new Connection(null); + this.connectionToInitialize = new Connection(null, this.gson); this.account.setConnection(null); resp.sendRedirect(this.servletUrl); return; @@ -301,9 +302,7 @@ protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResp Device device = account.findDeviceJson(serialNumber); if (device != null) { Thing thing = account.findThingBySerialNumber(device.serialNumber); - if (thing != null) { - handleIds(resp, connection, device, thing); - } + handleIds(resp, connection, device, thing); return; } } @@ -331,7 +330,7 @@ protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResp } public Map getQueryMap(@Nullable String query) { - Map map = new HashMap(); + Map map = new HashMap<>(); if (query != null) { String[] params = query.split("&"); for (String param : params) { @@ -374,6 +373,12 @@ private void handleDefaultPageResult(HttpServletResponse resp, String message, C html.append(" | "); html.append(StringEscapeUtils.escapeHtml("Logout and create new device id")); html.append(""); + // customer id + html.append("
Customer Id: "); + html.append(StringEscapeUtils.escapeHtml(connection.getCustomerId())); + // customer name + html.append("
Customer Name: "); + html.append(StringEscapeUtils.escapeHtml(connection.getCustomerName())); // device name html.append("
App name: "); html.append(StringEscapeUtils.escapeHtml(connection.getDeviceName())); @@ -393,7 +398,7 @@ private void handleDefaultPageResult(HttpServletResponse resp, String message, C // device list html.append( - ""); + "
DeviceSerial NumberStateThingTypeFamily
"); for (Device device : this.account.getLastKnownDevices()) { html.append(""); + html.append(StringEscapeUtils.escapeHtml(nullReplacement(device.deviceOwnerCustomerId))); + html.append(""); + html.append(""); } html.append("
DeviceSerial NumberStateThingFamilyTypeCustomer Id
"); @@ -409,14 +414,18 @@ private void handleDefaultPageResult(HttpServletResponse resp, String message, C + URLEncoder.encode(device.serialNumber, "UTF8") + "'>" + StringEscapeUtils.escapeHtml(accountHandler.getLabel()) + ""); } else { - html.append("Not defined"); + html.append("" + + StringEscapeUtils.escapeHtml("Not defined") + ""); } html.append(""); html.append(StringEscapeUtils.escapeHtml(nullReplacement(device.deviceFamily))); html.append(""); html.append(StringEscapeUtils.escapeHtml(nullReplacement(device.deviceType))); html.append(""); - html.append("
"); createPageEndAndSent(resp, html); @@ -467,18 +476,36 @@ private void createPageEndAndSent(HttpServletResponse resp, StringBuilder html) } } - private void handleIds(HttpServletResponse resp, Connection connection, Device device, Thing thing) + private void handleIds(HttpServletResponse resp, Connection connection, Device device, @Nullable Thing thing) throws IOException, URISyntaxException { - StringBuilder html = createPageStart("Channel Options - " + thing.getLabel()); - + StringBuilder html; + if (thing != null) { + html = createPageStart("Channel Options - " + thing.getLabel()); + } else { + html = createPageStart("Device Information - No thing defined"); + } renderBluetoothMacChannel(connection, device, html); renderAmazonMusicPlaylistIdChannel(connection, device, html); renderPlayAlarmSoundChannel(connection, device, html); renderMusicProviderIdChannel(connection, html); - + renderCapabilities(connection, device, html); createPageEndAndSent(resp, html); } + private void renderCapabilities(Connection connection, Device device, StringBuilder html) { + html.append("

Capabilities

"); + html.append(""); + String[] capabilities = device.capabilities; + if (capabilities != null) { + for (String capability : capabilities) { + html.append(""); + } + } + html.append("
Name
"); + html.append(StringEscapeUtils.escapeHtml(capability)); + html.append("
"); + } + private void renderMusicProviderIdChannel(Connection connection, StringBuilder html) { html.append("

" + StringEscapeUtils.escapeHtml("Channel " + CHANNEL_MUSIC_PROVIDER_ID) + "

"); html.append(""); @@ -599,11 +626,11 @@ void handleProxyRequest(Connection connection, HttpServletResponse resp, String try { Map headers = null; if (referer != null) { - headers = new HashMap(); + headers = new HashMap<>(); headers.put("Referer", referer); } - urlConnection = connection.makeRequest(verb, url, postData, json, false, headers); + urlConnection = connection.makeRequest(verb, url, postData, json, false, headers, 0); if (urlConnection.getResponseCode() == 302) { { String location = urlConnection.getHeaderField("location"); diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/AmazonEchoControlBindingConstants.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/AmazonEchoControlBindingConstants.java index 3aca19e0b336d..6b4ca63780e0e 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/AmazonEchoControlBindingConstants.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/AmazonEchoControlBindingConstants.java @@ -45,13 +45,16 @@ public class AmazonEchoControlBindingConstants { public static final ThingTypeUID THING_TYPE_LIGHT = new ThingTypeUID(BINDING_ID, "light"); public static final ThingTypeUID THING_TYPE_LIGHT_GROUP = new ThingTypeUID(BINDING_ID, "lightGroup"); - public static final Set SUPPORTED_THING_TYPES_UIDS = new HashSet( + public static final Set SUPPORTED_THING_TYPES_UIDS = new HashSet<>( Arrays.asList(THING_TYPE_ACCOUNT, THING_TYPE_ECHO, THING_TYPE_ECHO_SPOT, THING_TYPE_ECHO_SHOW, THING_TYPE_ECHO_WHA, THING_TYPE_FLASH_BRIEFING_PROFILE, THING_TYPE_LIGHT, THING_TYPE_LIGHT_GROUP)); // List of all Channel ids public static final String CHANNEL_PLAYER = "player"; public static final String CHANNEL_VOLUME = "volume"; + public static final String CHANNEL_EQUALIZER_TREBLE = "equalizerTreble"; + public static final String CHANNEL_EQUALIZER_MIDRANGE = "equalizerMidrange"; + public static final String CHANNEL_EQUALIZER_BASS = "equalizerBass"; public static final String CHANNEL_ERROR = "error"; public static final String CHANNEL_SHUFFLE = "shuffle"; public static final String CHANNEL_LOOP = "loop"; @@ -86,6 +89,10 @@ public class AmazonEchoControlBindingConstants { public static final String CHANNEL_LIGHT_COLOR = "lightColor"; public static final String CHANNEL_LIGHT_WHITE_TEMPERATURE = "whiteTemperature"; public static final String CHANNEL_LIGHT_BRIGHTNESS = "lightBrightness"; + public static final String CHANNEL_NEXT_REMINDER = "nextReminder"; + public static final String CHANNEL_NEXT_ALARM = "nextAlarm"; + public static final String CHANNEL_NEXT_MUSIC_ALARM = "nextMusicAlarm"; + public static final String CHANNEL_NEXT_TIMER = "nextTimer"; public static final String CHANNEL_SAVE = "save"; public static final String CHANNEL_ACTIVE = "active"; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/AmazonEchoControlHandlerFactory.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/AmazonEchoControlHandlerFactory.java index b454dfaafdfdb..cbb670561e08c 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/AmazonEchoControlHandlerFactory.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/AmazonEchoControlHandlerFactory.java @@ -44,6 +44,8 @@ import org.osgi.service.component.annotations.ReferencePolicy; import org.osgi.service.http.HttpService; +import com.google.gson.Gson; + /** * The {@link AmazonEchoControlHandlerFactory} is responsible for creating things and thing * handlers. @@ -62,7 +64,9 @@ public class AmazonEchoControlHandlerFactory extends BaseThingHandlerFactory { StorageService storageService; @Nullable BindingServlet bindingServlet; - + @Nullable + Gson gson; + @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); @@ -99,11 +103,17 @@ protected void deactivate(ComponentContext componentContext) { if (storageService == null) { return null; } + Gson gson = this.gson; + if (gson == null) + { + gson = new Gson(); + this.gson = gson; + } if (thingTypeUID.equals(THING_TYPE_ACCOUNT)) { Storage storage = storageService.getStorage(thing.getUID().toString(), String.class.getClassLoader()); - AccountHandler bridgeHandler = new AccountHandler((Bridge) thing, httpService, storage); + AccountHandler bridgeHandler = new AccountHandler((Bridge) thing, httpService, storage, gson); registerDiscoveryService(bridgeHandler); BindingServlet bindingServlet = this.bindingServlet; if (bindingServlet != null) { @@ -122,7 +132,7 @@ protected void deactivate(ComponentContext componentContext) { return new SmartHomeDeviceHandler(thing, storage); } if (SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) { - return new EchoHandler(thing); + return new EchoHandler(thing, gson); } return null; } @@ -136,7 +146,7 @@ private synchronized void registerDiscoveryService(AccountHandler bridgeHandler) AmazonEchoDiscovery discoveryService = new AmazonEchoDiscovery(bridgeHandler); discoveryService.activate(); this.discoveryServiceRegistrations.put(bridgeHandler.getThing().getUID(), bundleContext - .registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable())); + .registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>())); } @Override diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/BindingServlet.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/BindingServlet.java index bcefee5a83cde..112cd13a9a79b 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/BindingServlet.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/BindingServlet.java @@ -49,7 +49,7 @@ public class BindingServlet extends HttpServlet { String servletUrl; HttpService httpService; - List accountHandlers = new ArrayList(); + List accountHandlers = new ArrayList<>(); public BindingServlet(HttpService httpService) { this.httpService = httpService; @@ -126,5 +126,4 @@ protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResp logger.warn("return html failed with uri syntax error {}", e); } } - } diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/Connection.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/Connection.java index 5fd3ace4fe898..a462bba67546f 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/Connection.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/Connection.java @@ -12,7 +12,12 @@ */ package org.openhab.binding.amazonechocontrol.internal; -import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.*; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.DEVICE_PROPERTY_APPLIANCE_ID; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.INTERFACE_BRIGHTNESS; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.INTERFACE_COLOR; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.INTERFACE_COLOR_TEMPERATURE; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.THING_TYPE_LIGHT; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.THING_TYPE_LIGHT_GROUP; import java.io.IOException; import java.io.InputStream; @@ -34,7 +39,6 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; -import java.util.Hashtable; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -42,18 +46,20 @@ import java.util.Scanner; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; import javax.net.ssl.HttpsURLConnection; -import javax.xml.bind.DatatypeConverter; import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.util.StringUtil; +import org.eclipse.smarthome.core.common.ThreadPoolManager; import org.eclipse.smarthome.core.thing.Thing; +import org.eclipse.smarthome.core.util.HexUtils; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonActivities; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonActivities.Activity; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonAnnouncementContent; @@ -65,6 +71,8 @@ import org.openhab.binding.amazonechocontrol.internal.jsons.JsonAutomation.Payload; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonAutomation.Trigger; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonBluetoothStates; +import org.openhab.binding.amazonechocontrol.internal.jsons.JsonBootstrapResult; +import org.openhab.binding.amazonechocontrol.internal.jsons.JsonBootstrapResult.Authentication; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonColorTemperature; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonColors; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonDeviceNotificationState; @@ -72,6 +80,7 @@ import org.openhab.binding.amazonechocontrol.internal.jsons.JsonDevices; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonDevices.Device; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonEnabledFeeds; +import org.openhab.binding.amazonechocontrol.internal.jsons.JsonEqualizer; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonExchangeTokenResponse; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonExchangeTokenResponse.Cookie; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonFeed; @@ -81,6 +90,7 @@ import org.openhab.binding.amazonechocontrol.internal.jsons.JsonNotificationResponse; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonNotificationSound; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonNotificationSounds; +import org.openhab.binding.amazonechocontrol.internal.jsons.JsonNotificationsResponse; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlaySearchPhraseOperationPayload; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlayValidationResult; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlayerState; @@ -95,7 +105,6 @@ import org.openhab.binding.amazonechocontrol.internal.jsons.JsonRegisterAppResponse.Tokens; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonRenewTokenResponse; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonSmartHomeDeviceAlias; -import org.openhab.binding.amazonechocontrol.internal.jsons.JsonSmartHomeDevices; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonSmartHomeDevices.SmartHomeDevice; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonSmartHomeGroups.SmartHomeGroup; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonStartRoutineRequest; @@ -111,6 +120,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; @@ -122,6 +132,11 @@ */ @NonNullByDefault public class Connection { + + private static final String THING_THREADPOOL_NAME = "thingHandler"; + + protected final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool(THING_THREADPOOL_NAME); + private static final long expiresIn = 432000; // five days private static final Pattern charsetPattern = Pattern.compile("(?i)\\bcharset=\\s*\"?([^\\s;\"]*)"); @@ -141,11 +156,13 @@ public class Connection { private long renewTime = 0; private @Nullable String deviceName; private @Nullable String accountCustomerId; + private @Nullable String customerName; - private final Gson gson = new Gson(); + private final Gson gson; private final Gson gsonWithNullSerialization; - public Connection(@Nullable Connection oldConnection) { + public Connection(@Nullable Connection oldConnection, Gson gson) { + this.gson = gson; String frc = null; String serial = null; String deviceId = null; @@ -170,7 +187,7 @@ public Connection(@Nullable Connection oldConnection) { // generate serial byte[] serialBinary = new byte[16]; rand.nextBytes(serialBinary); - this.serial = DatatypeConverter.printHexBinary(serialBinary); + this.serial = HexUtils.bytesToHex(serialBinary); } if (deviceId != null) { this.deviceId = deviceId; @@ -247,13 +264,29 @@ public String getDeviceName() { return deviceName; } + public String getCustomerId() { + String customerId = this.accountCustomerId; + if (customerId == null) { + return "Unknown"; + } + return customerId; + } + + public String getCustomerName() { + String customerName = this.customerName; + if (customerName == null) { + return "Unknown"; + } + return customerName; + } + public String serializeLoginData() { Date loginTime = this.loginTime; if (refreshToken == null || loginTime == null) { return ""; } StringBuilder builder = new StringBuilder(); - builder.append("6\n"); // version + builder.append("7\n"); // version builder.append(frc); builder.append("\n"); builder.append(serial); @@ -334,8 +367,8 @@ public boolean tryRestoreLogin(@Nullable String data, @Nullable String overloade } Scanner scanner = new Scanner(data); String version = scanner.nextLine(); - // check if serialize version - if (!version.equals("5") && !version.equals("6")) { + // check if serialize version is supported + if (!"5".equals(version) && !"6".equals(version) && !"7".equals(version)) { scanner.close(); return null; } @@ -357,8 +390,12 @@ public boolean tryRestoreLogin(@Nullable String data, @Nullable String overloade if (intVersion > 5) { String accountCustomerId = scanner.nextLine(); - if (!StringUtils.equals(accountCustomerId, "null")) { - this.accountCustomerId = accountCustomerId; + // Note: version 5 have wrong customer id serialized. + // Only use it, if it at least version 6 of serialization + if (intVersion > 6) { + if (!StringUtils.equals(accountCustomerId, "null")) { + this.accountCustomerId = accountCustomerId; + } } } @@ -415,6 +452,29 @@ public boolean tryRestoreLogin(@Nullable String data, @Nullable String overloade return loginTime; } + private @Nullable Authentication tryGetBootstrap() throws IOException, URISyntaxException { + HttpsURLConnection connection = makeRequest("GET", alexaServer + "/api/bootstrap", null, false, false, null, 0); + String contentType = connection.getContentType(); + if (connection.getResponseCode() == 200 && StringUtils.startsWithIgnoreCase(contentType, "application/json")) { + try { + String bootstrapResultJson = convertStream(connection); + JsonBootstrapResult result = parseJson(bootstrapResultJson, JsonBootstrapResult.class); + Authentication authentication = result.authentication; + if (authentication != null && authentication.authenticated) { + this.customerName = authentication.customerName; + if (this.accountCustomerId == null) { + this.accountCustomerId = authentication.customerId; + } + return authentication; + } + } catch (JsonSyntaxException | IllegalStateException e) { + logger.info("No valid json received {}", e); + return null; + } + } + return null; + } + public String convertStream(HttpsURLConnection connection) throws IOException { InputStream input = connection.getInputStream(); if (input == null) { @@ -455,15 +515,20 @@ public String makeRequestAndReturnString(String url) throws IOException, URISynt public String makeRequestAndReturnString(String verb, String url, @Nullable String postData, boolean json, @Nullable Map customHeaders) throws IOException, URISyntaxException { - HttpsURLConnection connection = makeRequest(verb, url, postData, json, true, customHeaders); - return convertStream(connection); + HttpsURLConnection connection = makeRequest(verb, url, postData, json, true, customHeaders, 0); + String result = convertStream(connection); + this.logger.debug("Result of {} {}:{}", verb, url, result); + return result; } public HttpsURLConnection makeRequest(String verb, String url, @Nullable String postData, boolean json, - boolean autoredirect, @Nullable Map customHeaders) throws IOException, URISyntaxException { + boolean autoredirect, @Nullable Map customHeaders, int badRequestRepeats) + throws IOException, URISyntaxException { String currentUrl = url; - for (int i = 0; i < 30; i++) // loop for handling redirect, using automatic redirect is not possible, because - // all response headers must be catched + int redirectCounter = 0; + while (true) // loop for handling redirect and bad request, using automatic redirect is not possible, + // because + // all response headers must be catched { int code; HttpsURLConnection connection = null; @@ -481,7 +546,7 @@ public HttpsURLConnection makeRequest(String verb, String url, @Nullable String if (customHeaders != null) { for (String key : customHeaders.keySet()) { String value = customHeaders.get(key); - if (!StringUtil.isBlank(value)) { + if (StringUtils.isNotEmpty(value)) { connection.setRequestProperty(key, value); } } @@ -571,18 +636,35 @@ public HttpsURLConnection makeRequest(String verb, String url, @Nullable String } } } + if (code == 400 && badRequestRepeats > 0) { + scheduler.schedule(() -> { + logger.debug("Retry call to {}", url); + try { + makeRequest(verb, url, postData, json, autoredirect, customHeaders, badRequestRepeats - 1); + } catch (IOException | URISyntaxException e) { + logger.debug("Repeat fails {}", e); + } + }, 500, TimeUnit.MILLISECONDS); + return connection; + } if (code == 200) { logger.debug("Call to {} succeeded", url); return connection; } if (code == 302 && location != null) { logger.debug("Redirected to {}", location); + redirectCounter++; + if (redirectCounter > 30) { + throw new ConnectionException("Too many redirects"); + } currentUrl = location; if (autoredirect) { - continue; + continue; // repeat with new location } return connection; } + throw new HttpException(code, verb + " url '" + url + "' failed: " + connection.getResponseMessage()); + } catch (IOException e) { if (connection != null) { @@ -596,18 +678,14 @@ public HttpsURLConnection makeRequest(String verb, String url, @Nullable String } throw e; } - if (code != 200) { - throw new HttpException(code, verb + " url '" + url + "' failed: " + connection.getResponseMessage()); - } } - throw new ConnectionException("Too many redirects"); } public String registerConnectionAsApp(String oAutRedirectUrl) throws ConnectionException, IOException, URISyntaxException { URI oAutRedirectUri = new URI(oAutRedirectUrl); - Map queryParameters = new LinkedHashMap(); + Map queryParameters = new LinkedHashMap<>(); String query = oAutRedirectUri.getQuery(); String[] pairs = query.split("&"); for (String pair : pairs) { @@ -617,9 +695,9 @@ public String registerConnectionAsApp(String oAutRedirectUrl) } String accessToken = queryParameters.get("openid.oa2.access_token"); - Map cookieMap = new HashMap(); + Map cookieMap = new HashMap<>(); - List webSiteCookies = new ArrayList(); + List webSiteCookies = new ArrayList<>(); for (HttpCookie cookie : getSessionCookies("https://www.amazon.com")) { cookieMap.put(cookie.getName(), cookie.getValue()); webSiteCookies.add(new JsonWebSiteCookie(cookie.getName(), cookie.getValue())); @@ -632,7 +710,7 @@ public String registerConnectionAsApp(String oAutRedirectUrl) webSiteCookiesArray); String registerAppRequestJson = gson.toJson(registerAppRequest); - HashMap registerHeaders = new HashMap(); + HashMap registerHeaders = new HashMap<>(); registerHeaders.put("x-amzn-identity-auth-domain", "api.amazon.com"); String registerAppResultJson = makeRequestAndReturnString("POST", "https://api.amazon.com/auth/register", @@ -669,6 +747,7 @@ public String registerConnectionAsApp(String oAutRedirectUrl) setAmazonSite(host); try { exhangeToken(); + tryGetBootstrap(); } catch (Exception e) { logout(); throw e; @@ -699,7 +778,7 @@ private void exhangeToken() throws IOException, URISyntaxException { + "&requested_token_type=auth_cookies&source_token_type=refresh_token&di.hw.version=iPhone&di.sdk.version=6.10.0&cookies=" + cookiesBase64 + "&app_name=Amazon%20Alexa&di.os.version=11.4.1"; - HashMap exchangeTokenHeader = new HashMap(); + HashMap exchangeTokenHeader = new HashMap<>(); exchangeTokenHeader.put("Cookie", ""); String exchangeTokenJson = makeRequestAndReturnString("POST", @@ -782,22 +861,22 @@ public boolean verifyLogin() throws IOException, URISyntaxException { if (this.refreshToken == null) { return false; } - String response = makeRequestAndReturnString(alexaServer + "/api/bootstrap?version=0"); - Boolean result = response.contains("\"authenticated\":true"); - if (result) { + Authentication authentication = tryGetBootstrap(); + if (authentication != null && authentication.authenticated) { verifyTime = new Date(); if (loginTime == null) { loginTime = verifyTime; } + return true; } - return result; + return false; } public List getSessionCookies() { try { return cookieManager.getCookieStore().get(new URI(alexaServer)); } catch (URISyntaxException e) { - return new ArrayList(); + return new ArrayList<>(); } } @@ -805,7 +884,7 @@ public List getSessionCookies(String server) { try { return cookieManager.getCookieStore().get(new URI(server)); } catch (URISyntaxException e) { - return new ArrayList(); + return new ArrayList<>(); } } @@ -819,10 +898,10 @@ public void logout() { } // parser - private T parseJson(String json, Class type) throws JsonSyntaxException { + private T parseJson(String json, Class type) throws JsonSyntaxException, IllegalStateException { try { return gson.fromJson(json, type); - } catch (JsonSyntaxException e) { + } catch (JsonParseException | IllegalStateException e) { logger.warn("Parsing json failed {}", e); logger.warn("Illegal json: {}", json); throw e; @@ -893,8 +972,11 @@ public List getSmarthomeDeviceList() throws IOException, URISyn ArrayList smartDevices = new ArrayList<>(); for (int i = 0; i < smartHomeDeviceArray.size(); ++i) { - if (smartHomeDeviceArray.get(i).groupIdentity != null && group.applianceGroupIdentifier.value != null) { - if (smartHomeDeviceArray.get(i).groupIdentity.equals(group.applianceGroupIdentifier.value)) { + if (smartHomeDeviceArray.get(i).groupIdentity != null && group.applianceGroupIdentifier != null) { + String groupIdentity = smartHomeDeviceArray.get(i).groupIdentity; + @SuppressWarnings("null") + String applianceGroupIdentifier = group.applianceGroupIdentifier.value; + if (groupIdentity != null && groupIdentity.equals(applianceGroupIdentifier)) { smartDevices.add(smartHomeDeviceArray.get(i)); if (group.applianceGroupName != null) { alias[0] = new JsonSmartHomeDeviceAlias(group.applianceGroupName, true); @@ -907,8 +989,8 @@ public List getSmarthomeDeviceList() throws IOException, URISyn smartDevices.toArray(smartDevicesArray); if (group.applianceGroupName != null) { - SmartHomeDevice shdGroup = new JsonSmartHomeDevices().new SmartHomeDevice(uuid, "Amazon", - "Amazon Light Group", group.applianceGroupName, "reachable", uuid, alias, smartDevicesArray); + SmartHomeDevice shdGroup = new SmartHomeDevice(uuid, "Amazon", "Amazon Light Group", + group.applianceGroupName, "reachable", uuid, alias, smartDevicesArray); smartHomeDeviceArray.add(shdGroup); } @@ -943,7 +1025,7 @@ public List getSmarthomeDeviceGroups() throws IOException, URISy return smartHomeGroupArray; } - // Placeholder for an api-request + // Need to cache the light colors here public List getEchoLightColors() { ArrayList colors = new ArrayList<>(); String[] stringColors = { "red", "crimson", "salmon", "orange", "gold", "yellow", "green", "turquoise", "cyan", @@ -955,7 +1037,7 @@ public List getEchoLightColors() { return colors; } - // Placeholder for an api-request + // Need to cache the light temperature list here public List getEchoLightTemperatures() { ArrayList temperatures = new ArrayList<>(); String[] stringTemperatures = { "warm_white", "soft_white", "white", "daylight_white", "cool_white" }; @@ -1106,6 +1188,7 @@ public String getBulbState(Thing device) throws IOException, URISyntaxException String state = null; if (device.getThingTypeUID().equals(THING_TYPE_LIGHT) || device.getThingTypeUID().equals(THING_TYPE_LIGHT_GROUP)) { + JsonArray capabilities = this.getBulbCapabilities(applianceId); for (JsonElement capability : capabilities) { @@ -1213,7 +1296,7 @@ public JsonPlaylists getPlaylists(Device device) throws IOException, URISyntaxEx public void command(Device device, String command) throws IOException, URISyntaxException { String url = alexaServer + "/api/np/command?deviceSerialNumber=" + device.serialNumber + "&deviceType=" + device.deviceType; - makeRequest("POST", url, command, true, true, null); + makeRequest("POST", url, command, true, true, null, 0); } public void smartHomeCommand(String entityId, String action, @Nullable String color, double brightness) @@ -1237,7 +1320,7 @@ public void smartHomeCommand(String entityId, String action, @Nullable String co + ", \"entityType\": \"APPLIANCE\", \"parameters\": { \"action\": " + "\"" + action + "\"" + ", \"brightness\": \"" + brightness + "\" }}]}"; } - makeRequest("PUT", url, requestBody, true, true, null); + makeRequest("PUT", url, requestBody, true, true, null, 0); } public void notificationVolume(Device device, int volume) throws IOException, URISyntaxException { @@ -1245,7 +1328,7 @@ public void notificationVolume(Device device, int volume) throws IOException, UR + "/" + device.serialNumber; String command = "{\"deviceSerialNumber\":\"" + device.serialNumber + "\",\"deviceType\":\"" + device.deviceType + "\",\"softwareVersion\":\"" + device.softwareVersion + "\",\"volumeLevel\":" + volume + "}"; - makeRequest("PUT", url, command, true, true, null); + makeRequest("PUT", url, command, true, true, null, 0); } public void ascendingAlarm(Device device, boolean ascendingAlarm) throws IOException, URISyntaxException { @@ -1253,7 +1336,7 @@ public void ascendingAlarm(Device device, boolean ascendingAlarm) throws IOExcep String command = "{\"ascendingAlarmEnabled\":" + (ascendingAlarm ? "true" : "false") + ",\"deviceSerialNumber\":\"" + device.serialNumber + "\",\"deviceType\":\"" + device.deviceType + "\",\"deviceAccountId\":null}"; - makeRequest("PUT", url, command, true, true, null); + makeRequest("PUT", url, command, true, true, null, 0); } public DeviceNotificationState[] getDeviceNotificationStates() { @@ -1291,11 +1374,11 @@ public void bluetooth(Device device, @Nullable String address) throws IOExceptio // disconnect makeRequest("POST", alexaServer + "/api/bluetooth/disconnect-sink/" + device.deviceType + "/" + device.serialNumber, "", - true, true, null); + true, true, null, 0); } else { makeRequest("POST", alexaServer + "/api/bluetooth/pair-sink/" + device.deviceType + "/" + device.serialNumber, - "{\"bluetoothDeviceAddress\":\"" + address + "\"}", true, true, null); + "{\"bluetoothDeviceAddress\":\"" + address + "\"}", true, true, null, 0); } } @@ -1309,7 +1392,7 @@ public void playRadio(Device device, @Nullable String stationId) throws IOExcept + "&contentType=station&callSign=&mediaOwnerCustomerId=" + (StringUtils.isEmpty(this.accountCustomerId) ? device.deviceOwnerCustomerId : this.accountCustomerId), - "", true, true, null); + "", true, true, null, 0); } } @@ -1324,7 +1407,7 @@ public void playAmazonMusicTrack(Device device, @Nullable String trackId) throws + (StringUtils.isEmpty(this.accountCustomerId) ? device.deviceOwnerCustomerId : this.accountCustomerId) + "&shuffle=false", - command, true, true, null); + command, true, true, null, 0); } } @@ -1340,13 +1423,13 @@ public void playAmazonMusicPlayList(Device device, @Nullable String playListId) + (StringUtils.isEmpty(this.accountCustomerId) ? device.deviceOwnerCustomerId : this.accountCustomerId) + "&shuffle=false", - command, true, true, null); + command, true, true, null, 0); } } public void sendNotificationToMobileApp(String customerId, String text, @Nullable String title) throws IOException, URISyntaxException { - Map parameters = new Hashtable(); + Map parameters = new HashMap<>(); parameters.put("notificationMessage", text); parameters.put("alexaUrl", "#v2/behaviors"); if (title != null && !StringUtils.isEmpty(title)) { @@ -1358,9 +1441,9 @@ public void sendNotificationToMobileApp(String customerId, String text, @Nullabl executeSequenceCommand(null, "Alexa.Notifications.SendMobilePush", parameters); } - public void sendAnnouncement(Device device, String text, @Nullable String title) - throws IOException, URISyntaxException { - Map parameters = new Hashtable(); + public void sendAnnouncement(Device device, String text, String bodyText, @Nullable String title, int ttsVolume, + int standardVolume) throws IOException, URISyntaxException { + Map parameters = new HashMap<>(); parameters.put("expireAfter", "PT5S"); JsonAnnouncementContent[] contentArray = new JsonAnnouncementContent[1]; JsonAnnouncementContent content = new JsonAnnouncementContent(); @@ -1369,7 +1452,10 @@ public void sendAnnouncement(Device device, String text, @Nullable String title) } else { content.display.title = title; } - content.display.body = text; + content.display.body = bodyText; + if (text.startsWith("") && text.endsWith("")) { + content.speak.type = "ssml"; + } content.speak.value = text; contentArray[0] = content; @@ -1379,48 +1465,54 @@ public void sendAnnouncement(Device device, String text, @Nullable String title) JsonAnnouncementTarget target = new JsonAnnouncementTarget(); target.customerId = device.deviceOwnerCustomerId; TargetDevice[] devices = new TargetDevice[1]; - TargetDevice deviceTarget = target.new TargetDevice(); + TargetDevice deviceTarget = new TargetDevice(); + deviceTarget.deviceSerialNumber = device.serialNumber; + deviceTarget.deviceTypeId = device.deviceType; devices[0] = deviceTarget; target.devices = devices; parameters.put("target", target); - String customerId = device.deviceOwnerCustomerId; + String accountCustomerId = this.accountCustomerId; + String customerId = StringUtils.isEmpty(accountCustomerId) ? device.deviceOwnerCustomerId : accountCustomerId; + if (customerId != null) { parameters.put("customerId", customerId); } - executeSequenceCommand(null, "AlexaAnnouncement", parameters); + executeSequenceCommandWithVolume(device, "AlexaAnnouncement", parameters, ttsVolume, standardVolume); } public void textToSpeech(Device device, String text, int ttsVolume, int standardVolume) throws IOException, URISyntaxException { + Map parameters = new HashMap<>(); + parameters.put("textToSpeak", text); + executeSequenceCommandWithVolume(device, "Alexa.Speak", parameters, ttsVolume, standardVolume); + } + private void executeSequenceCommandWithVolume(@Nullable Device device, String command, + @Nullable Map parameters, int ttsVolume, int standardVolume) + throws IOException, URISyntaxException { if (ttsVolume != 0) { JsonArray nodesToExecute = new JsonArray(); - Map parameters = new Hashtable(); - + Map volumeParameters = new HashMap<>(); // add tts volume - parameters.clear(); - parameters.put("value", ttsVolume); - nodesToExecute.add(createExecutionNode(device, "Alexa.DeviceControls.Volume", parameters)); + volumeParameters.clear(); + volumeParameters.put("value", ttsVolume); + nodesToExecute.add(createExecutionNode(device, "Alexa.DeviceControls.Volume", volumeParameters)); - // add tts - parameters.clear(); - parameters.put("textToSpeak", text); - nodesToExecute.add(createExecutionNode(device, "Alexa.Speak", parameters)); + // add command + nodesToExecute.add(createExecutionNode(device, command, parameters)); // add volume - parameters.clear(); - parameters.put("value", standardVolume); - nodesToExecute.add(createExecutionNode(device, "Alexa.DeviceControls.Volume", parameters)); + volumeParameters.clear(); + volumeParameters.put("value", standardVolume); + nodesToExecute.add(createExecutionNode(device, "Alexa.DeviceControls.Volume", volumeParameters)); executeSequenceNodes(nodesToExecute); } else { - Map parameters = new Hashtable(); - parameters.put("textToSpeak", text); - executeSequenceCommand(device, "Alexa.Speak", parameters); + executeSequenceCommand(device, command, parameters); } } @@ -1441,10 +1533,10 @@ private void executeSequenceNode(JsonObject nodeToExecute) throws IOException, U request.sequenceJson = gson.toJson(sequenceJson); String json = gson.toJson(request); - Map headers = new HashMap(); + Map headers = new HashMap<>(); headers.put("Routines-Version", "1.1.218665"); - makeRequest("POST", alexaServer + "/api/behaviors/preview", json, true, true, null); + makeRequest("POST", alexaServer + "/api/behaviors/preview", json, true, true, null, 3); } private void executeSequenceNodes(JsonArray nodesToExecute) throws IOException, URISyntaxException { @@ -1552,14 +1644,14 @@ public void startRoutine(Device device, String utterance) throws IOException, UR request.sequenceJson = sequenceJson; String requestJson = gson.toJson(request); - makeRequest("POST", alexaServer + "/api/behaviors/preview", requestJson, true, true, null); + makeRequest("POST", alexaServer + "/api/behaviors/preview", requestJson, true, true, null, 3); } else { logger.warn("Routine {} not found", utterance); } } public JsonAutomation[] getRoutines() throws IOException, URISyntaxException { - String json = makeRequestAndReturnString(alexaServer + "/api/behaviors/automations"); + String json = makeRequestAndReturnString(alexaServer + "/api/behaviors/automations?limit=2000"); JsonAutomation[] result = parseJson(json, JsonAutomation[].class); return result; } @@ -1578,7 +1670,7 @@ public void setEnabledFlashBriefings(JsonFeed[] enabledFlashBriefing) throws IOE JsonEnabledFeeds enabled = new JsonEnabledFeeds(); enabled.enabledFeeds = enabledFlashBriefing; String json = gsonWithNullSerialization.toJson(enabled); - makeRequest("POST", alexaServer + "/api/content-skills/enabled-feeds", json, true, true, null); + makeRequest("POST", alexaServer + "/api/content-skills/enabled-feeds", json, true, true, null, 0); } public JsonNotificationSound[] getNotificationSounds(Device device) throws IOException, URISyntaxException { @@ -1593,12 +1685,22 @@ public JsonNotificationSound[] getNotificationSounds(Device device) throws IOExc return new JsonNotificationSound[0]; } + public JsonNotificationResponse[] notifications() throws IOException, URISyntaxException { + String response = makeRequestAndReturnString(alexaServer + "/api/notifications"); + JsonNotificationsResponse result = parseJson(response, JsonNotificationsResponse.class); + JsonNotificationResponse[] notifications = result.notifications; + if (notifications == null) { + return new JsonNotificationResponse[0]; + } + return notifications; + } + public JsonNotificationResponse notification(Device device, String type, @Nullable String label, @Nullable JsonNotificationSound sound) throws IOException, URISyntaxException { Date date = new Date(new Date().getTime()); long createdDate = date.getTime(); Date alarm = new Date(createdDate + 5000); // add 5 seconds, because amazon does not except calls for times in - // the past (compared with the server time) + // the past (compared with the server time) long alarmTime = alarm.getTime(); JsonNotificationRequest request = new JsonNotificationRequest(); @@ -1636,7 +1738,7 @@ public JsonNotificationResponse getNotificationState(JsonNotificationResponse no public List getMusicProviders() { String response; try { - Map headers = new HashMap(); + Map headers = new HashMap<>(); headers.put("Routines-Version", "1.1.218665"); response = makeRequestAndReturnString("GET", alexaServer + "/api/behaviors/entities?skillId=amzn1.ask.1p.music", null, true, headers); @@ -1654,7 +1756,8 @@ public List getMusicProviders() { public void playMusicVoiceCommand(Device device, String providerId, String voiceCommand) throws IOException, URISyntaxException { JsonPlaySearchPhraseOperationPayload payload = new JsonPlaySearchPhraseOperationPayload(); - payload.customerId = device.deviceOwnerCustomerId; + payload.customerId = (StringUtils.isEmpty(this.accountCustomerId) ? device.deviceOwnerCustomerId + : this.accountCustomerId); payload.locale = "ALEXA_CURRENT_LOCALE"; payload.musicProviderId = providerId; payload.searchPhrase = voiceCommand; @@ -1697,6 +1800,18 @@ public void playMusicVoiceCommand(Device device, String providerId, String voice startRoutineRequest.status = null; String postData = gson.toJson(startRoutineRequest); - makeRequest("POST", alexaServer + "/api/behaviors/preview", postData, true, true, null); + makeRequest("POST", alexaServer + "/api/behaviors/preview", postData, true, true, null, 3); + } + + public JsonEqualizer getEqualizer(Device device) throws IOException, URISyntaxException { + String json = makeRequestAndReturnString( + alexaServer + "/api/equalizer/" + device.serialNumber + "/" + device.deviceType); + return parseJson(json, JsonEqualizer.class); + } + + public void SetEqualizer(Device device, JsonEqualizer settings) throws IOException, URISyntaxException { + String postData = gson.toJson(settings); + makeRequest("POST", alexaServer + "/api/equalizer/" + device.serialNumber + "/" + device.deviceType, postData, + true, true, null, 0); } } diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/WebSocketConnection.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/WebSocketConnection.java index 949cfde646b55..302e7c90dcbaa 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/WebSocketConnection.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/WebSocketConnection.java @@ -82,7 +82,7 @@ public WebSocketConnection(String amazonSite, List sessionCookies, } String deviceSerial = ""; - List cookiesForWs = new ArrayList(); + List cookiesForWs = new ArrayList<>(); for (HttpCookie cookie : sessionCookies) { if (cookie.getName().equals("ubid-acbde")) { deviceSerial = cookie.getValue(); @@ -149,7 +149,18 @@ public void close() { Session session = this.session; this.session = null; if (session != null) { - session.close(); + try { + session.close(); + } catch (Exception e) { + logger.debug("Closing sessing failed {}", e); + } + } + try { + webSocketClient.stop(); + } catch (InterruptedException e) { + // Just ignore + } catch (Exception e) { + logger.debug("Stopping websocket failed {}", e); } webSocketClient.destroy(); } diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/channelhandler/ChannelHandler.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/channelhandler/ChannelHandler.java new file mode 100644 index 0000000000000..cd288e6dc3d46 --- /dev/null +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/channelhandler/ChannelHandler.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.amazonechocontrol.internal.channelhandler; + +import java.io.IOException; +import java.net.URISyntaxException; + +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.amazonechocontrol.internal.Connection; +import org.openhab.binding.amazonechocontrol.internal.jsons.JsonDevices.Device; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +/** + * The {@link ChannelHandler} is the base class for all channel handlers + * + * @author Michael Geramb - Initial contribution + */ +public abstract class ChannelHandler { + public abstract boolean tryHandleCommand(Device device, Connection connection, String channelId, Command command) + throws IOException, URISyntaxException; + + protected final IAmazonThingHandler thingHandler; + protected final Gson gson; + private final Logger logger; + + protected ChannelHandler(IAmazonThingHandler thingHandler, Gson gson) { + this.logger = LoggerFactory.getLogger(this.getClass()); + this.thingHandler = thingHandler; + this.gson = gson; + } + + protected @Nullable T tryParseJson(String json, Class type) { + try { + return gson.fromJson(json, type); + } catch (JsonSyntaxException e) { + logger.debug("Json parse error {}", e); + return null; + } + } + + protected @Nullable T parseJson(String json, Class type) throws JsonSyntaxException { + return gson.fromJson(json, type); + + } +} diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/channelhandler/ChannelHandlerAnnouncement.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/channelhandler/ChannelHandlerAnnouncement.java new file mode 100644 index 0000000000000..26425b44b21aa --- /dev/null +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/channelhandler/ChannelHandlerAnnouncement.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.amazonechocontrol.internal.channelhandler; + +import java.io.IOException; +import java.net.URISyntaxException; + +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.amazonechocontrol.internal.Connection; +import org.openhab.binding.amazonechocontrol.internal.jsons.JsonDevices.Device; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +/** + * The {@link ChannelHandlerAnnouncement} is responsible for the announcement channel + * + * @author Michael Geramb - Initial contribution + */ +public class ChannelHandlerAnnouncement extends ChannelHandler { + public static final String CHANNEL_NAME = "announcement"; + + public ChannelHandlerAnnouncement(IAmazonThingHandler thingHandler, Gson gson) { + super(thingHandler, gson); + } + + @Override + public boolean tryHandleCommand(Device device, Connection connection, String channelId, Command command) + throws IOException, URISyntaxException { + if (channelId.equals(CHANNEL_NAME)) { + if (command instanceof StringType) { + String commandValue = ((StringType) command).toFullString(); + String body = commandValue; + String title = null; + String speak = " "; // blank generates a beep + if (commandValue.startsWith("{") && commandValue.endsWith("}")) { + try { + AnnouncementRequestJson request = parseJson(commandValue, AnnouncementRequestJson.class); + if (request != null) { + title = request.title; + body = request.body; + if (body == null) { + body = ""; + } + if (request.sound == false) { + speak = ""; + } + } + } catch (JsonSyntaxException e) { + body = e.getLocalizedMessage(); + } + } + connection.sendAnnouncement(device, speak, body, title, 0, 0); + } + RefreshChannel(); + } + return false; + } + + void RefreshChannel() { + thingHandler.updateChannelState(CHANNEL_NAME, new StringType("")); + } + + class AnnouncementRequestJson { + public @Nullable Boolean sound; + public @Nullable String title; + public @Nullable String body; + } +} diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/channelhandler/IAmazonThingHandler.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/channelhandler/IAmazonThingHandler.java new file mode 100644 index 0000000000000..f0e412c9879ed --- /dev/null +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/channelhandler/IAmazonThingHandler.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.amazonechocontrol.internal.channelhandler; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.types.State; + +/** + * The {@link IAmazonThingHandler} is used from ChannelHandlers to communicate with the thing + * + * @author Michael Geramb - Initial contribution + */ +@NonNullByDefault +public interface IAmazonThingHandler { + void updateChannelState(String channelId, State state); +} diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/discovery/AmazonEchoDiscovery.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/discovery/AmazonEchoDiscovery.java index 437961fb7890a..d5dd3ca0b46ca 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/discovery/AmazonEchoDiscovery.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/discovery/AmazonEchoDiscovery.java @@ -15,8 +15,8 @@ import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.*; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; -import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledFuture; @@ -49,7 +49,7 @@ public class AmazonEchoDiscovery extends AbstractDiscoveryService implements Ext AccountHandler accountHandler; private final Logger logger = LoggerFactory.getLogger(AmazonEchoDiscovery.class); - private final HashSet discoverdFlashBriefings = new HashSet(); + private final HashSet discoverdFlashBriefings = new HashSet<>(); @Nullable ScheduledFuture startScanStateJob; @@ -68,7 +68,7 @@ public AmazonEchoDiscovery(AccountHandler accountHandler) { } public void activate() { - activate(new Hashtable()); + activate(new HashMap<>()); } @Override diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/discovery/SmartHomeDevicesDiscovery.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/discovery/SmartHomeDevicesDiscovery.java index 1840da3783f82..3c75693c73f3c 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/discovery/SmartHomeDevicesDiscovery.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/discovery/SmartHomeDevicesDiscovery.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ package org.openhab.binding.amazonechocontrol.internal.discovery; import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.*; @@ -13,6 +25,7 @@ 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.AbstractDiscoveryService; import org.eclipse.smarthome.config.discovery.DiscoveryResult; import org.eclipse.smarthome.config.discovery.DiscoveryResultBuilder; @@ -127,6 +140,13 @@ synchronized void setSmartHomeDevices(List deviceList) { return; } + Configuration config = accountHandler.getThing().getConfiguration(); + boolean discoverSmartHome = (boolean) config.getProperties().get("discoverSmartHome"); + + if (discoverSmartHome == false) { + return; + } + for (SmartHomeDevice smartHomeDevice : deviceList) { ThingUID bridgeThingUID = this.accountHandler.getThing().getUID(); ThingTypeUID thingTypeId = smartHomeDevice.groupDevices != null ? THING_TYPE_LIGHT_GROUP : THING_TYPE_LIGHT; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/AccountHandler.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/AccountHandler.java index 8ceef6b571503..fa03318850fd3 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/AccountHandler.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/AccountHandler.java @@ -16,6 +16,7 @@ import java.net.URISyntaxException; import java.net.URLEncoder; import java.net.UnknownHostException; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -54,10 +55,12 @@ import org.openhab.binding.amazonechocontrol.internal.jsons.JsonCommandPayloadPushActivity.Key; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonCommandPayloadPushDevice; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonCommandPayloadPushDevice.DopplerId; +import org.openhab.binding.amazonechocontrol.internal.jsons.JsonCommandPayloadPushNotificationChange; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonDeviceNotificationState.DeviceNotificationState; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonDevices.Device; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonFeed; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonMusicProvider; +import org.openhab.binding.amazonechocontrol.internal.jsons.JsonNotificationResponse; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonNotificationSound; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlaylists; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPushCommand; @@ -94,11 +97,12 @@ public class AccountHandler extends BaseBridgeHandler implements IWebSocketComma private String currentFlashBriefingJson = ""; private final HttpService httpService; private @Nullable AccountServlet accountServlet; - private final Gson gson = new Gson(); + private final Gson gson; int checkDataCounter; - public AccountHandler(Bridge bridge, HttpService httpService, Storage stateStorage) { + public AccountHandler(Bridge bridge, HttpService httpService, Storage stateStorage, Gson gson) { super(bridge); + this.gson = gson; this.httpService = httpService; this.stateStorage = stateStorage; } @@ -110,11 +114,11 @@ public void initialize() { synchronized (synchronizeConnection) { Connection connection = this.connection; if (connection == null) { - this.connection = new Connection(null); + this.connection = new Connection(null, gson); } } if (this.accountServlet == null) { - this.accountServlet = new AccountServlet(httpService, this.getThing().getUID().getId(), this); + this.accountServlet = new AccountServlet(httpService, this.getThing().getUID().getId(), this, gson); } updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "Wait for login"); @@ -163,7 +167,7 @@ public void addSmartHomeDeviceHandler(SmartHomeDeviceHandler smartHomeDeviceHand forceCheckData(); } - void forceCheckData() { + public void forceCheckData() { if (foceCheckDataJob == null) { foceCheckDataJob = scheduler.schedule(this::forceCheckDataHandler, 1000, TimeUnit.MILLISECONDS); } @@ -197,7 +201,7 @@ public void addFlashBriefingProfileHandler(FlashBriefingProfileHandler flashBrie flashBriefingProfileHandlers.add(flashBriefingProfileHandler); } Connection connection = this.connection; - if (connection != null) { + if (connection != null && connection.getIsLoggedIn()) { if (currentFlashBriefingJson.isEmpty()) { updateFlashBriefingProfiles(connection); } @@ -205,6 +209,16 @@ public void addFlashBriefingProfileHandler(FlashBriefingProfileHandler flashBrie } } + private void scheduleUpdate() { + checkDataCounter = 999; + } + + @Override + public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) { + super.childHandlerInitialized(childHandler, childThing); + scheduleUpdate(); + } + @Override public void handleRemoval() { cleanup(); @@ -338,7 +352,7 @@ public void setConnection(@Nullable Connection connection) { updateDeviceList(); updateFlashBriefingHandlers(); updateStatus(ThingStatus.ONLINE); - checkDataCounter = 0; + scheduleUpdate(); checkData(); } } @@ -391,6 +405,30 @@ private void checkData() { } } + private void refreshNotifications(@Nullable JsonCommandPayloadPushNotificationChange pushPayload) { + Connection currentConnection = this.connection; + if (currentConnection == null) { + return; + } + if (!currentConnection.getIsLoggedIn()) { + return; + } + JsonNotificationResponse[] notifications; + ZonedDateTime timeStamp = ZonedDateTime.now(); + try { + notifications = currentConnection.notifications(); + } catch (IOException | URISyntaxException e) { + logger.debug("refreshNotifications failed {}", e); + return; + } + ZonedDateTime timeStampNow = ZonedDateTime.now(); + + for (EchoHandler child : echoHandlers) { + child.updateNotifications(timeStamp, timeStampNow, pushPayload, notifications); + } + + } + private void refreshData() { synchronized (synchronizeConnection) { try { @@ -486,6 +524,9 @@ private void refreshData() { notificationSounds, musicProviders); } + // refresh notifications + refreshNotifications(null); + // update account state updateStatus(ThingStatus.ONLINE); @@ -564,7 +605,7 @@ public List updateDeviceList() { Connection currentConnection = connection; if (currentConnection == null) { - return new ArrayList(); + return new ArrayList<>(); } List devices = null; @@ -606,7 +647,7 @@ public List updateDeviceList() { if (devices != null) { return devices; } - return new ArrayList(); + return new ArrayList<>(); } public void setEnabledFlashBriefingsJson(String flashBriefingJson) { @@ -712,7 +753,9 @@ void handleWebsocketCommand(JsonPushCommand pushCommand) { TimeUnit.MILLISECONDS); break; case "PUSH_NOTIFICATION_CHANGE": - // Currently ignored + JsonCommandPayloadPushNotificationChange pushPayload = gson.fromJson(pushCommand.payload, + JsonCommandPayloadPushNotificationChange.class); + refreshNotifications(pushPayload); break; default: String payload = pushCommand.payload; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/DeviceHandler.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/DeviceHandler.java index 1bd2ff07b9720..f3b031ebda1b6 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/DeviceHandler.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/DeviceHandler.java @@ -33,9 +33,7 @@ import org.slf4j.LoggerFactory; /** - * * @author Lukas Knoeller - * */ public class DeviceHandler extends BaseThingHandler { @@ -64,7 +62,7 @@ public void initialize() { if (bridge != null) { AccountHandler account = (AccountHandler) bridge.getHandler(); if (account != null) { - // TODO + updateSmartHomeDevices(); } } } @@ -73,7 +71,7 @@ public void initialize() { public void handleCommand(ChannelUID channelUID, Command command) { logger.trace("Command {} received from channel '{}'", command, channelUID); if (command instanceof RefreshType) { - // updateSmartHomeDevices(); + updateSmartHomeDevices(); } } diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/EchoHandler.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/EchoHandler.java index e9eed8bf1a418..2509589adeb1c 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/EchoHandler.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/EchoHandler.java @@ -12,21 +12,71 @@ */ package org.openhab.binding.amazonechocontrol.internal.handler; -import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.*; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_AMAZON_MUSIC; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_AMAZON_MUSIC_PLAY_LIST_ID; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_AMAZON_MUSIC_TRACK_ID; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_ASCENDING_ALARM; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_BLUETOOTH; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_BLUETOOTH_DEVICE_NAME; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_BLUETOOTH_MAC; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_EQUALIZER_BASS; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_EQUALIZER_MIDRANGE; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_EQUALIZER_TREBLE; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_IMAGE_URL; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_LAST_VOICE_COMMAND; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_MEDIA_LENGTH; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_MEDIA_PROGRESS; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_MEDIA_PROGRESS_TIME; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_MUSIC_PROVIDER_ID; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_NEXT_ALARM; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_NEXT_MUSIC_ALARM; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_NEXT_REMINDER; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_NEXT_TIMER; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_NOTIFICATION_VOLUME; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_PLAYER; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_PLAY_ALARM_SOUND; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_PLAY_MUSIC_VOICE_COMMAND; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_PLAY_ON_DEVICE; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_PROVIDER_DISPLAY_NAME; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_RADIO; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_RADIO_STATION_ID; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_REMIND; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_SHUFFLE; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_START_COMMAND; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_START_ROUTINE; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_SUBTITLE1; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_SUBTITLE2; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_TEXT_TO_SPEECH; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_TEXT_TO_SPEECH_VOLUME; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_TITLE; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_VOLUME; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.DEVICE_PROPERTY_SERIAL_NUMBER; +import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.FLASH_BRIEFING_COMMAND_PREFIX; import java.io.IOException; import java.net.URISyntaxException; -import java.util.Hashtable; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +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 java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.measure.quantity.Time; import org.apache.commons.lang.StringUtils; 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.DecimalType; import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType; import org.eclipse.smarthome.core.library.types.NextPreviousType; @@ -45,18 +95,25 @@ 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.openhab.binding.amazonechocontrol.internal.Connection; +import org.openhab.binding.amazonechocontrol.internal.ConnectionException; import org.openhab.binding.amazonechocontrol.internal.HttpException; +import org.openhab.binding.amazonechocontrol.internal.channelhandler.ChannelHandler; +import org.openhab.binding.amazonechocontrol.internal.channelhandler.ChannelHandlerAnnouncement; +import org.openhab.binding.amazonechocontrol.internal.channelhandler.IAmazonThingHandler; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonActivities.Activity; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonActivities.Activity.Description; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonAscendingAlarm.AscendingAlarmModel; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonBluetoothStates; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonBluetoothStates.BluetoothState; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonBluetoothStates.PairedDevice; +import org.openhab.binding.amazonechocontrol.internal.jsons.JsonCommandPayloadPushNotificationChange; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonCommandPayloadPushVolumeChange; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonDeviceNotificationState.DeviceNotificationState; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonDevices.Device; +import org.openhab.binding.amazonechocontrol.internal.jsons.JsonEqualizer; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonMediaState; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonMediaState.QueueEntry; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonMusicProvider; @@ -81,11 +138,12 @@ * @author Michael Geramb - Initial contribution */ @NonNullByDefault -public class EchoHandler extends BaseThingHandler { +public class EchoHandler extends BaseThingHandler implements IAmazonThingHandler { private final Logger logger = LoggerFactory.getLogger(EchoHandler.class); - private Gson gson = new Gson(); + private Gson gson; private @Nullable Device device; + private Set capabilities = new HashSet<>(); private @Nullable AccountHandler account; private @Nullable ScheduledFuture updateStateJob; private @Nullable ScheduledFuture ignoreVolumeChange; @@ -100,6 +158,7 @@ public class EchoHandler extends BaseThingHandler { private boolean isPaused = false; private int lastKnownVolume = 25; private int textToSpeechVolume = 0; + private @Nullable JsonEqualizer lastKnownEqualizer = null; private @Nullable BluetoothState bluetoothState; private boolean disableUpdate = false; private boolean updateRemind = true; @@ -113,6 +172,7 @@ public class EchoHandler extends BaseThingHandler { private @Nullable JsonPlaylists playLists; private @Nullable JsonNotificationSound @Nullable [] alarmSounds; private @Nullable List musicProviders; + private List channelHandlers = new ArrayList<>(); private @Nullable JsonNotificationResponse currentNotification; private @Nullable ScheduledFuture currentNotifcationUpdateTimer; @@ -121,8 +181,10 @@ public class EchoHandler extends BaseThingHandler { long mediaStartMs; String lastSpokenText = ""; - public EchoHandler(Thing thing) { + public EchoHandler(Thing thing, Gson gson) { super(thing); + this.gson = gson; + channelHandlers.add(new ChannelHandlerAnnouncement(this, this.gson)); } @Override @@ -149,6 +211,10 @@ public boolean setDeviceAndUpdateThingState(AccountHandler accountHandler, @Null return false; } this.device = device; + String[] capabilities = device.capabilities; + if (capabilities != null) { + this.capabilities = Stream.of(capabilities).filter(Objects::nonNull).collect(Collectors.toSet()); + } if (!device.online) { updateStatus(ThingStatus.OFFLINE); return false; @@ -244,8 +310,14 @@ public void handleCommand(ChannelUID channelUID, Command command) { return; } - // Player commands String channelId = channelUID.getId(); + for (ChannelHandler channelHandler : channelHandlers) { + if (channelHandler.tryHandleCommand(device, connection, channelId, command)) { + return; + } + } + + // Player commands if (channelId.equals(CHANNEL_PLAYER)) { if (command == PlayPauseType.PAUSE || command == OnOffType.OFF) { connection.command(device, "{\"type\":\"PauseCommand\"}"); @@ -356,7 +428,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { + ",\"contentFocusClientId\":\"Default\"}"); } else { - Map parameters = new Hashtable(); + Map parameters = new HashMap<>(); parameters.put("value", volume); connection.executeSequenceCommand(device, "Alexa.DeviceControls.Volume", parameters); } @@ -366,6 +438,14 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } + // equalizer commands + if (channelId.equals(CHANNEL_EQUALIZER_BASS) || channelId.equals(CHANNEL_EQUALIZER_MIDRANGE) + || channelId.equals(CHANNEL_EQUALIZER_TREBLE)) { + if (handleEqualizerCommands(channelId, command, connection, device)) { + waitForUpdate = -1; + } + } + // shuffle command if (channelId.equals(CHANNEL_SHUFFLE)) { if (command instanceof OnOffType) { @@ -638,6 +718,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { }; if (command instanceof RefreshType) { waitForUpdate = 0; + account.forceCheckData(); } if (waitForUpdate == 0) { doRefresh.run(); @@ -651,6 +732,41 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } + private boolean handleEqualizerCommands(String channelId, Command command, Connection connection, Device device) + throws URISyntaxException { + if (command instanceof RefreshType) { + this.lastKnownEqualizer = null; + } + if (command instanceof DecimalType) { + DecimalType value = (DecimalType) command; + if (this.lastKnownEqualizer == null) { + updateEqualizerState(); + } + JsonEqualizer lastKnownEqualizer = this.lastKnownEqualizer; + if (lastKnownEqualizer != null) { + JsonEqualizer newEqualizerSetting = lastKnownEqualizer.createClone(); + if (channelId.equals(CHANNEL_EQUALIZER_BASS)) { + newEqualizerSetting.bass = value.intValue(); + } + if (channelId.equals(CHANNEL_EQUALIZER_MIDRANGE)) { + newEqualizerSetting.mid = value.intValue(); + } + if (channelId.equals(CHANNEL_EQUALIZER_TREBLE)) { + newEqualizerSetting.treble = value.intValue(); + } + try { + connection.SetEqualizer(device, newEqualizerSetting); + return true; + } catch (HttpException | IOException | ConnectionException e) { + logger.debug("Update equalizer failed {}", e); + this.lastKnownEqualizer = null; + } + + } + } + return false; + } + private void startTextToSpeech(Connection connection, Device device, String text) throws IOException, URISyntaxException { if (textToSpeechVolume != 0) { @@ -661,7 +777,12 @@ private void startTextToSpeech(Connection connection, Device device, String text } this.ignoreVolumeChange = scheduler.schedule(this::stopIgnoreVolumeChange, 2000, TimeUnit.MILLISECONDS); } - connection.textToSpeech(device, text, textToSpeechVolume, lastKnownVolume); + if (text.startsWith("") && text.endsWith("")) { + String bodyText = text.replaceAll("<[^>]+>", ""); + connection.sendAnnouncement(device, text, bodyText, null, textToSpeechVolume, lastKnownVolume); + } else { + connection.textToSpeech(device, text, textToSpeechVolume, lastKnownVolume); + } } private void stopCurrentNotification() { @@ -724,7 +845,7 @@ public void updateState(AccountHandler accountHandler, @Nullable Device device, @Nullable JsonNotificationSound @Nullable [] alarmSounds, @Nullable List musicProviders) { try { - this.logger.debug("Handle updateState {}", this.getThing().getUID().getAsString()); + this.logger.debug("Handle updateState {}", this.getThing().getUID()); if (deviceNotificationState != null) { noticationVolumeLevel = deviceNotificationState.volumeLevel; @@ -742,16 +863,16 @@ public void updateState(AccountHandler accountHandler, @Nullable Device device, this.musicProviders = musicProviders; } if (!setDeviceAndUpdateThingState(accountHandler, device, null)) { - this.logger.debug("Handle updateState {} aborted: Not online", this.getThing().getUID().getAsString()); + this.logger.debug("Handle updateState {} aborted: Not online", this.getThing().getUID()); return; } if (device == null) { - this.logger.debug("Handle updateState {} aborted: No device", this.getThing().getUID().getAsString()); + this.logger.debug("Handle updateState {} aborted: No device", this.getThing().getUID()); return; } if (this.disableUpdate) { - this.logger.debug("Handle updateState {} aborted: Disabled", this.getThing().getUID().getAsString()); + this.logger.debug("Handle updateState {} aborted: Disabled", this.getThing().getUID()); return; } Connection connection = this.findConnection(); @@ -759,6 +880,10 @@ public void updateState(AccountHandler accountHandler, @Nullable Device device, return; } + if (this.lastKnownEqualizer == null) { + updateEqualizerState(); + } + PlayerInfo playerInfo = null; Provider provider = null; InfoText infoText = null; @@ -790,6 +915,13 @@ public void updateState(AccountHandler accountHandler, @Nullable Device device, if (StringUtils.startsWith(musicProviderId, "TUNEIN")) { musicProviderId = "TUNEIN"; } + if (StringUtils.startsWithIgnoreCase(musicProviderId, "iHeartRadio")) { + musicProviderId = "I_HEART_RADIO"; + } + if (StringUtils.containsIgnoreCase(musicProviderId, "Apple") + && StringUtils.containsIgnoreCase(musicProviderId, "Music")) { + musicProviderId = "APPLE_MUSIC"; + } } } progress = playerInfo.progress; @@ -1052,13 +1184,51 @@ public void updateState(AccountHandler accountHandler, @Nullable Device device, } } catch (Exception e) { - this.logger.debug("Handle updateState {} failed: {}", this.getThing().getUID().getAsString(), e); + this.logger.debug("Handle updateState {} failed: {}", this.getThing().getUID(), e); disableUpdate = false; throw e; // Rethrow same exception } } + private void updateEqualizerState() { + if (!this.capabilities.contains("SOUND_SETTINGS")) { + return; + } + + Connection connection = findConnection(); + if (connection == null) { + return; + } + Device device = findDevice(); + if (device == null) { + return; + } + Integer bass; + Integer midrange; + Integer treble; + try { + JsonEqualizer equalizer = connection.getEqualizer(device); + bass = equalizer.bass; + midrange = equalizer.mid; + treble = equalizer.treble; + this.lastKnownEqualizer = equalizer; + } catch (IOException | URISyntaxException | HttpException | ConnectionException e) { + logger.debug("Get equalizer failes {}", e); + return; + } + if (bass != null) { + updateState(CHANNEL_EQUALIZER_BASS, new DecimalType(bass)); + } + if (midrange != null) { + updateState(CHANNEL_EQUALIZER_MIDRANGE, new DecimalType(midrange)); + } + if (treble != null) { + updateState(CHANNEL_EQUALIZER_TREBLE, new DecimalType(treble)); + } + + } + private void updateMediaProgress() { updateMediaProgress(false); } @@ -1092,11 +1262,17 @@ private void updateMediaProgress(boolean updateMediaLength) { } public void handlePushActivity(Activity pushActivity) { + if ("DISCARDED_NON_DEVICE_DIRECTED_INTENT".equals(pushActivity.activityStatus)) { + return; + } Description description = pushActivity.ParseDescription(); if (StringUtils.isEmpty(description.firstUtteranceId) || StringUtils.startsWithIgnoreCase(description.firstUtteranceId, "TextClient:")) { return; } + if (StringUtils.isEmpty(description.firstStreamId)) { + return; + } String spokenText = description.summary; if (spokenText != null && StringUtils.isNotEmpty(spokenText)) { // remove wake word @@ -1141,7 +1317,7 @@ public void handlePushCommand(String command, String payload) { } break; case "PUSH_EQUALIZER_STATE_CHANGE": - // Currently ignored + updateEqualizerState(); break; default: AccountHandler account = this.account; @@ -1152,4 +1328,72 @@ public void handlePushCommand(String command, String payload) { } } } + + public void updateNotifications(ZonedDateTime currentTime, ZonedDateTime now, + @Nullable JsonCommandPayloadPushNotificationChange pushPayload, JsonNotificationResponse[] notifications) { + Device device = this.device; + if (device == null) { + return; + } + + ZonedDateTime nextReminder = null; + ZonedDateTime nextAlarm = null; + ZonedDateTime nextMusicAlarm = null; + ZonedDateTime nextTimer = null; + for (JsonNotificationResponse notification : notifications) { + if (StringUtils.equals(notification.deviceSerialNumber, device.serialNumber)) { + // notification for this device + if (StringUtils.equals(notification.status, "ON")) { + if ("Reminder".equals(notification.type)) { + String offset = ZoneId.systemDefault().getRules().getOffset(Instant.now()).toString(); + ZonedDateTime alarmTime = ZonedDateTime + .parse(notification.originalDate + "T" + notification.originalTime + offset); + if (StringUtils.isNotBlank(notification.recurringPattern) && alarmTime.isBefore(now)) { + continue; // Ignore recurring entry if alarm time is before now + } + if (nextReminder == null || alarmTime.isBefore(nextReminder)) { + nextReminder = alarmTime; + } + } else if ("Timer".equals(notification.type)) { + // use remaining time + ZonedDateTime alarmTime = currentTime.plus(notification.remainingTime, ChronoUnit.MILLIS); + if (nextTimer == null || alarmTime.isBefore(nextTimer)) { + nextTimer = alarmTime; + } + } else if ("Alarm".equals(notification.type)) { + String offset = ZoneId.systemDefault().getRules().getOffset(Instant.now()).toString(); + ZonedDateTime alarmTime = ZonedDateTime + .parse(notification.originalDate + "T" + notification.originalTime + offset); + if (StringUtils.isNotBlank(notification.recurringPattern) && alarmTime.isBefore(now)) { + continue; // Ignore recurring entry if alarm time is before now + } + if (nextAlarm == null || alarmTime.isBefore(nextAlarm)) { + nextAlarm = alarmTime; + } + } else if ("MusicAlarm".equals(notification.type)) { + String offset = ZoneId.systemDefault().getRules().getOffset(Instant.now()).toString(); + ZonedDateTime alarmTime = ZonedDateTime + .parse(notification.originalDate + "T" + notification.originalTime + offset); + if (StringUtils.isNotBlank(notification.recurringPattern) && alarmTime.isBefore(now)) { + continue; // Ignore recurring entry if alarm time is before now + } + if (nextMusicAlarm == null || alarmTime.isBefore(nextMusicAlarm)) { + nextMusicAlarm = alarmTime; + } + } + } + } + } + + updateState(CHANNEL_NEXT_REMINDER, nextReminder == null ? UnDefType.UNDEF : new DateTimeType(nextReminder)); + updateState(CHANNEL_NEXT_ALARM, nextAlarm == null ? UnDefType.UNDEF : new DateTimeType(nextAlarm)); + updateState(CHANNEL_NEXT_MUSIC_ALARM, + nextMusicAlarm == null ? UnDefType.UNDEF : new DateTimeType(nextMusicAlarm)); + updateState(CHANNEL_NEXT_TIMER, nextTimer == null ? UnDefType.UNDEF : new DateTimeType(nextTimer)); + } + + @Override + public void updateChannelState(String channelId, State state) { + updateState(channelId, state); + } } diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/SmartHomeDeviceHandler.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/SmartHomeDeviceHandler.java index 4bb024a5bbda3..087b1579135a2 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/SmartHomeDeviceHandler.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/SmartHomeDeviceHandler.java @@ -15,6 +15,7 @@ import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.*; import java.io.IOException; +import java.math.BigDecimal; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; @@ -24,6 +25,7 @@ import org.apache.commons.lang.StringUtils; import org.eclipse.jdt.annotation.Nullable; +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.StringType; @@ -42,9 +44,7 @@ import org.slf4j.LoggerFactory; /** - * * @author Lukas Knoeller - * */ public class SmartHomeDeviceHandler extends BaseThingHandler { @@ -122,7 +122,12 @@ public void run() { } } }; - updateStateJob = scheduler.scheduleWithFixedDelay(runnable, 0, 30, TimeUnit.SECONDS); + + Configuration config = accountHandler.getThing().getConfiguration(); + + updateStateJob = scheduler.scheduleWithFixedDelay(runnable, 0, + ((BigDecimal) config.getProperties().get("pollingIntervalSmartHome")).longValue(), + TimeUnit.SECONDS); } } @@ -142,15 +147,11 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (accountHandler == null) { return; } - int waitForUpdate = -1; try { Map props = this.thing.getProperties(); String entityId = props.get(DEVICE_PROPERTY_LIGHT_ENTITY_ID); String channelId = channelUID.getId(); - if (command instanceof RefreshType) { - waitForUpdate = 0; - } if (channelId.equals(CHANNEL_LIGHT_STATE)) { if (command instanceof OnOffType) { connection = accountHandler.findConnection(); @@ -170,7 +171,6 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } } - waitForUpdate = 1; } } if (channelId.equals(CHANNEL_LIGHT_COLOR)) { @@ -218,18 +218,9 @@ public void handleCommand(ChannelUID channelUID, Command command) { ((PercentType) command).floatValue() / 100); } } - waitForUpdate = 1; } } - if (waitForUpdate < 0) { - return; - } - - if (command instanceof RefreshType) { - waitForUpdate = 0; - } - } catch (Exception e) { logger.warn("Handle command failed {}", e); } @@ -273,6 +264,16 @@ public boolean initialize(AccountHandler handler) { } public List updateSmartHomeDevices() { + + /* + * Configuration config = accountHandler.getThing().getConfiguration(); + * boolean discoverSmartHome = (boolean) config.getProperties().get("discoverSmartHome"); + * + * if (discoverSmartHome == false) { + * return new ArrayList(); + * } + */ + Connection currentConnection = connection; if (currentConnection == null) { return new ArrayList(); diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonActivities.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonActivities.java index 910b1f347cf54..b09660af70a1e 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonActivities.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonActivities.java @@ -29,7 +29,7 @@ public class JsonActivities { public @Nullable Activity @Nullable [] activities; - public class Activity { + public static class Activity { public @Nullable String activityStatus; public @Nullable Long creationTimestamp; public @Nullable String description; @@ -45,14 +45,14 @@ public class Activity { public @Nullable String utteranceId; public @Nullable Long version; - public class SourceDeviceId { + public static class SourceDeviceId { public @Nullable String deviceAccountId; public @Nullable String deviceType; public @Nullable String serialNumber; } - public class Description { + public static class Description { public @Nullable String summary; public @Nullable String firstUtteranceId; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAnnouncementContent.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAnnouncementContent.java index 1ce05d0cbfa52..ce2296773e417 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAnnouncementContent.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAnnouncementContent.java @@ -24,17 +24,17 @@ @NonNullByDefault public class JsonAnnouncementContent { - public @Nullable String locale; + public String locale = ""; public final Display display = new Display(); public final Speak speak = new Speak(); - public class Display { + public static class Display { public @Nullable String title; public @Nullable String body; } - public class Speak { + public static class Speak { public String type = "text"; public @Nullable String value; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAnnouncementTarget.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAnnouncementTarget.java index fcb8e884541f2..6e99bec84f1c6 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAnnouncementTarget.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAnnouncementTarget.java @@ -26,7 +26,7 @@ public class JsonAnnouncementTarget { public @Nullable String customerId; public @Nullable TargetDevice @Nullable [] devices; - public class TargetDevice { + public static class TargetDevice { public @Nullable String deviceSerialNumber; public @Nullable String deviceTypeId; } diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAscendingAlarm.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAscendingAlarm.java index 68ebe7d70d936..95c4d6c81a8a5 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAscendingAlarm.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAscendingAlarm.java @@ -25,7 +25,7 @@ public class JsonAscendingAlarm { public @Nullable AscendingAlarmModel @Nullable [] ascendingAlarmModelList; - public class AscendingAlarmModel { + public static class AscendingAlarmModel { public @Nullable Boolean ascendingAlarmEnabled; public @Nullable String deviceSerialNumber; public @Nullable String deviceType; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAutomation.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAutomation.java index 9f9b7cf99faf3..d3be585e7c7f4 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAutomation.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonAutomation.java @@ -32,13 +32,13 @@ public class JsonAutomation { public long creationTimeEpochMillis; public long lastUpdatedTimeEpochMillis; - public class Trigger { + public static class Trigger { public @Nullable Payload payload; public @Nullable String id; public @Nullable String type; } - public class Payload { + public static class Payload { public @Nullable String customerId; public @Nullable String utterance; public @Nullable String locale; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonBluetoothStates.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonBluetoothStates.java index 5361887d5afb2..4d2ba0adb07cc 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonBluetoothStates.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonBluetoothStates.java @@ -44,7 +44,7 @@ public class JsonBluetoothStates { public @Nullable BluetoothState @Nullable [] bluetoothStates; - public class PairedDevice { + public static class PairedDevice { public @Nullable String address; public boolean connected; public @Nullable String deviceClass; @@ -53,7 +53,7 @@ public class PairedDevice { } - public class BluetoothState { + public static class BluetoothState { public @Nullable String deviceSerialNumber; public @Nullable String deviceType; public @Nullable String friendlyName; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonBootstrapResult.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonBootstrapResult.java new file mode 100644 index 0000000000000..dffce100b83b8 --- /dev/null +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonBootstrapResult.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.amazonechocontrol.internal.jsons; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link JsonBluetoothStates} encapsulate the bootstrap result + * + * @author Michael Geramb - Initial contribution + */ +@NonNullByDefault +public class JsonBootstrapResult { + + public @Nullable Authentication authentication; + + public static class Authentication { + public boolean authenticated; + public @Nullable Boolean canAccessPrimeMusicContent; + public @Nullable String customerEmail; + public @Nullable String customerId; + public @Nullable String customerName; + } +} diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonColorTemperature.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonColorTemperature.java index 65ac12e075889..bdf8f94f004dd 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonColorTemperature.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonColorTemperature.java @@ -1,8 +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.amazonechocontrol.internal.jsons; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +/** + * @author Lukas Knoeller + */ @NonNullByDefault public class JsonColorTemperature { public @Nullable String temperatureName; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonColors.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonColors.java index 96d606174758f..129fe2988f37f 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonColors.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonColors.java @@ -1,8 +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.amazonechocontrol.internal.jsons; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +/** + * @author Lukas Knoeller + */ @NonNullByDefault public class JsonColors { public @Nullable String colorName; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonCommandPayloadPushActivity.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonCommandPayloadPushActivity.java index 9fc7800324326..c53128cf0966a 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonCommandPayloadPushActivity.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonCommandPayloadPushActivity.java @@ -28,7 +28,7 @@ public class JsonCommandPayloadPushActivity { public @Nullable Key key; - public class Key { + public static class Key { public @Nullable String entryId; public @Nullable String registeredUserId; } diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonCommandPayloadPushDevice.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonCommandPayloadPushDevice.java index f606635ab5e22..c7b42709229ec 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonCommandPayloadPushDevice.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonCommandPayloadPushDevice.java @@ -25,7 +25,7 @@ public class JsonCommandPayloadPushDevice { public @Nullable DopplerId dopplerId; - public class DopplerId { + public static class DopplerId { public @Nullable String deviceSerialNumber; public @Nullable String deviceType; } diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonDeviceNotificationState.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonDeviceNotificationState.java index 2f0a6ccb7701b..596c172a2048b 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonDeviceNotificationState.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonDeviceNotificationState.java @@ -25,7 +25,7 @@ public class JsonDeviceNotificationState { public @Nullable DeviceNotificationState @Nullable [] deviceNotificationStates; - public class DeviceNotificationState { + public static class DeviceNotificationState { public @Nullable String deviceSerialNumber; public @Nullable String deviceType; public @Nullable String softwareVersion; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonDevices.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonDevices.java index 16f00449aac06..2ff3a02ad65d1 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonDevices.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonDevices.java @@ -23,7 +23,7 @@ @NonNullByDefault public class JsonDevices { - public class Device { + public static class Device { public @Nullable String accountName; public @Nullable String serialNumber; public @Nullable String deviceOwnerCustomerId; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonExchangeTokenResponse.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonExchangeTokenResponse.java index e95a1df7a873e..48807bea68910 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonExchangeTokenResponse.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonExchangeTokenResponse.java @@ -26,15 +26,15 @@ public class JsonExchangeTokenResponse { public @Nullable Response response; - public class Response { + public static class Response { public @Nullable Tokens tokens; } - public class Tokens { + public static class Tokens { public @Nullable Map cookies; } - public class Cookie { + public static class Cookie { public @Nullable String Path; public @Nullable Boolean Secure; public @Nullable String Value; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonMediaState.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonMediaState.java index 69033b938c5e5..0d23fb92d1995 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonMediaState.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonMediaState.java @@ -47,7 +47,7 @@ public class JsonMediaState { // public long timeLastShuffled; parsing fails with some values, so do not use it public int volume; - public class QueueEntry { + public static class QueueEntry { public @Nullable String album; public @Nullable String albumAsin; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonNotificationResponse.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonNotificationResponse.java index 48f1c1915e91c..e339d176c5bad 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonNotificationResponse.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonNotificationResponse.java @@ -30,6 +30,10 @@ public class JsonNotificationResponse { public @Nullable String id; public @Nullable String status; public @Nullable String type; + public long remainingTime; + public @Nullable String recurringPattern; + public @Nullable String originalDate; + public @Nullable String originalTime; } /* diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonPlayerState.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonPlayerState.java index 79e4f508b6c97..f3013b518cc13 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonPlayerState.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonPlayerState.java @@ -24,7 +24,7 @@ public class JsonPlayerState { public @Nullable PlayerInfo playerInfo; - public class PlayerInfo { + public static class PlayerInfo { public @Nullable String state; public @Nullable InfoText infoText; public @Nullable InfoText miniInfoText; @@ -37,7 +37,7 @@ public class PlayerInfo { public @Nullable Progress progress; - public class InfoText { + public static class InfoText { public boolean multiLineMode; public @Nullable String subText1; public @Nullable String subText2; @@ -45,24 +45,24 @@ public class InfoText { } - public class Provider { + public static class Provider { public @Nullable String providerDisplayName; public @Nullable String providerName; } - public class Volume { + public static class Volume { public boolean muted; public int volume; } - public class MainArt { + public static class MainArt { public @Nullable String altText; public @Nullable String artType; public @Nullable String contentType; public @Nullable String url; } - public class Progress { + public static class Progress { public @Nullable Boolean allowScrubbing; public @Nullable Object locationInfo; public @Nullable Long mediaLength; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonPlaylists.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonPlaylists.java index 1e8b8bcdcdeee..d079c686df8c5 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonPlaylists.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonPlaylists.java @@ -27,7 +27,7 @@ public class JsonPlaylists { public @Nullable Map playlists; - public class PlayList { + public static class PlayList { public @Nullable String playlistId; public @Nullable String title; public int trackCount; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonRegisterAppRequest.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonRegisterAppRequest.java index 54b242a0cf0f1..8b2e2762a22d5 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonRegisterAppRequest.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonRegisterAppRequest.java @@ -38,7 +38,7 @@ public JsonRegisterAppRequest(String serial, String access_token, String frc, Js public UserContextMap user_context_map = new UserContextMap(); public String[] requested_token_type = { "bearer", "mac_dms", "website_cookies" }; - public class Cookies { + public static class Cookies { @Nullable public JsonWebSiteCookie @Nullable [] website_cookies; @Nullable @@ -46,7 +46,7 @@ public class Cookies { } - public class RegistrationData { + public static class RegistrationData { public String domain = "Device"; public String app_version = "2.2.223830.0"; public String device_type = "A2IVLV5VM2W81"; @@ -59,12 +59,12 @@ public class RegistrationData { public String software_version = "1"; } - public class AuthData { + public static class AuthData { @Nullable public String access_token; } - public class UserContextMap { + public static class UserContextMap { public String frc = ""; } } diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonRegisterAppResponse.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonRegisterAppResponse.java index 9c6539c066aca..b2a22649f54ea 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonRegisterAppResponse.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonRegisterAppResponse.java @@ -29,19 +29,21 @@ public class JsonRegisterAppResponse { @Nullable public String request_id; - public class Response { + public static class Response { @Nullable public Success success; } - public class Success { + public static class Success { @Nullable public Extensions extensions; @Nullable public Tokens tokens; + @Nullable + public String customer_id; } - public class Extensions { + public static class Extensions { @Nullable public DeviceInfo device_info; @Nullable @@ -50,7 +52,7 @@ public class Extensions { public String customer_id; } - public class DeviceInfo { + public static class DeviceInfo { @Nullable public String device_name; @Nullable @@ -60,7 +62,7 @@ public class DeviceInfo { } - public class CustomerInfo { + public static class CustomerInfo { @Nullable public String account_pool; @Nullable @@ -73,7 +75,7 @@ public class CustomerInfo { public String given_name; } - public class Tokens { + public static class Tokens { @Nullable public Object website_cookies; @Nullable @@ -82,14 +84,14 @@ public class Tokens { public Bearer bearer; } - public class MacDms { + public static class MacDms { @Nullable public String device_private_key; @Nullable public String adp_token; } - public class Bearer { + public static class Bearer { @Nullable public String access_token; @Nullable diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeDeviceAlias.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeDeviceAlias.java index 65cafc30fb12b..81d62666ea991 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeDeviceAlias.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeDeviceAlias.java @@ -1,10 +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.amazonechocontrol.internal.jsons; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; /** - * @author Lukas Knoeller - Initial contribution + * @author Lukas Knoeller */ @NonNullByDefault public class JsonSmartHomeDeviceAlias { @@ -15,4 +27,7 @@ public JsonSmartHomeDeviceAlias(String friendlyName, Boolean enabled) { this.friendlyName = friendlyName; this.enabled = enabled; } + + public JsonSmartHomeDeviceAlias() { + } } diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeDevices.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeDevices.java index d34f891edc678..441c4deaeb121 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeDevices.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeDevices.java @@ -16,12 +16,12 @@ import org.eclipse.jdt.annotation.Nullable; /** - * @author Lukas Knoeller - Contributor + * @author Lukas Knoeller */ @NonNullByDefault public class JsonSmartHomeDevices { - public class SmartHomeDevice { + public static class SmartHomeDevice { public @Nullable String applianceId; public @Nullable String manufacturerName; public @Nullable String friendlyDescription; @@ -36,7 +36,7 @@ public class SmartHomeDevice { public boolean colorTemperature = false; public SmartHomeDevice(String applianceId, String manufacturerName, String friendlyDescription, - String friendlyName, String reachability, String entityId, JsonSmartHomeDeviceAlias[] alias, + @Nullable String friendlyName, String reachability, String entityId, JsonSmartHomeDeviceAlias[] alias, SmartHomeDevice[] groupDevices) { this.applianceId = applianceId; this.manufacturerName = manufacturerName; @@ -47,6 +47,9 @@ public SmartHomeDevice(String applianceId, String manufacturerName, String frien this.alias = alias; this.groupDevices = groupDevices; } + + public SmartHomeDevice() { + } } public @Nullable SmartHomeDevice @Nullable [] smarthomeDevices; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeGroupIdentifiers.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeGroupIdentifiers.java index a20d85355bbae..bd766a06c3b0a 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeGroupIdentifiers.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeGroupIdentifiers.java @@ -1,12 +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.amazonechocontrol.internal.jsons; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +/** + * @author Lukas Knoeller + */ @NonNullByDefault public class JsonSmartHomeGroupIdentifiers { - public class SmartHomeGroupIdentifier { + public static class SmartHomeGroupIdentifier { public @Nullable String value; } diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeGroups.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeGroups.java index ade1d91152892..3f54db9051ecf 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeGroups.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeGroups.java @@ -1,13 +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.amazonechocontrol.internal.jsons; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.amazonechocontrol.internal.jsons.JsonSmartHomeGroupIdentifiers.SmartHomeGroupIdentifier; +/** + * @author Lukas Knoeller + */ @NonNullByDefault public class JsonSmartHomeGroups { - public class SmartHomeGroup { + public static class SmartHomeGroup { public @Nullable String applianceGroupName; public @Nullable Boolean isSpace; public @Nullable Boolean space; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonWakeWords.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonWakeWords.java index c8af33b41867c..c59a5bca171ab 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonWakeWords.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonWakeWords.java @@ -24,7 +24,7 @@ public class JsonWakeWords { public @Nullable WakeWord @Nullable [] wakeWords; - public class WakeWord { + public static class WakeWord { public @Nullable Boolean active; public @Nullable String deviceSerialNumber; public @Nullable String deviceType; 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 dcd9c3acc966d..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,8 @@ 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) { - final String value = 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))); } diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/statedescription/AmazonEchoDynamicStateDescriptionSmartHome.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/statedescription/AmazonEchoDynamicStateDescriptionSmartHome.java index a9e6d9d450cde..63cd4b88cd3b8 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/statedescription/AmazonEchoDynamicStateDescriptionSmartHome.java +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/statedescription/AmazonEchoDynamicStateDescriptionSmartHome.java @@ -1,3 +1,15 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ package org.openhab.binding.amazonechocontrol.internal.statedescription; import static org.openhab.binding.amazonechocontrol.internal.AmazonEchoControlBindingConstants.CHANNEL_TYPE_LIGHT_COLOR; diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.amazonechocontrol/src/main/resources/ESH-INF/binding/binding.xml index b6c76c2bd9f9a..27d95905a731b 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/resources/ESH-INF/binding/binding.xml +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/resources/ESH-INF/binding/binding.xml @@ -4,6 +4,6 @@ Amazon Echo Control Binding Binding to control Amazon Echo devices (Alexa). This binding enables openHAB to control the volume, playing state, bluetooth connection of your amazon echo devices or allow to use it as TTS device. - Michael Geramb + Michael Geramb, Lukas Knoeller diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/resources/ESH-INF/i18n/amazonechocontrol_de.properties b/bundles/org.openhab.binding.amazonechocontrol/src/main/resources/ESH-INF/i18n/amazonechocontrol_de.properties index e3b6d0b14de0a..cb836e9130991 100755 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/resources/ESH-INF/i18n/amazonechocontrol_de.properties +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/resources/ESH-INF/i18n/amazonechocontrol_de.properties @@ -6,6 +6,7 @@ binding.amazonechocontrol.description = Binding zum Steuern von Amazon Echo (Ale thing-type.amazonechocontrol.account.label = Amazon Konto thing-type.amazonechocontrol.account.description = Amazon Konto bei dem die Amazon Echo Geräte registriert sind. +thing-type.amazonechocontrol.account.config-description.pollingIntervalSmartHome.description = Definiert die Sekundenanzahl, wie häufig OpenHab den Status der Alexa-Lampen abfragt. thing-type.amazonechocontrol.echo.label = Amazon Echo thing-type.amazonechocontrol.echo.description = Amazon Echo Gerät (Amazon Echo, Amazon Echo Dot, Amazon Echo Plus...) @@ -34,6 +35,12 @@ thing-type.config.wha.echoshow.serialNumber.description = Die Seriennummer des G thing-type.amazonechocontrol.flashbriefingprofile.label = Tägliche Zusammenfassungsprofile thing-type.amazonechocontrol.flashbriefingprofile.description = Speichert und läd eine Tägliches Zusammenfassungskonfiguration +thing-type.amazonechocontrol.light.label = Amazon Echo Licht +thing-type.amazonechocontrol.light.description = Eine mit deinem Amazon Account verbundene Lampe + +thing-type.amazonechocontrol.lightGroup.label = Amazon Echo Lichtergruppe +thing-type.amazonechocontrol.lightGroup.description = Eine in deinem Amazon Account angelegte Gruppe von Lichtern + # channel types channel-type.amazonechocontrol.bluetoothDeviceName.label = Bluetooth Gerät channel-type.amazonechocontrol.bluetoothDeviceName.description = Verbundenes Bluetoothgerät @@ -56,6 +63,9 @@ channel-type.amazonechocontrol.providerDisplayName.description = Name des Musika channel-type.amazonechocontrol.bluetoothMAC.label = Bluetooth Verbindung channel-type.amazonechocontrol.bluetoothMAC.description = MAC-Adresse des verbundenen Bluetoothgerätes +channel-type.amazonechocontrol.announcement.label = Ankündigung +channel-type.amazonechocontrol.announcement.description = Zeigt die Ankündungsnachricht am Display (Nur schreiben). In der Binding Beschreibung ist im Tutorials Abschnitt eine Erklärung wie der Title gesetzt und der Sound deaktiviert wird. + channel-type.amazonechocontrol.textToSpeech.label = Sprich channel-type.amazonechocontrol.textToSpeech.description = Spricht den Text (Nur schreiben). Es kann reiner Text oder SSML verwendet werden: e.g. Ich will dir ein Geheimnis erzählen.Ich bin nicht wirklich ein Mensch. @@ -163,3 +173,35 @@ channel-type.amazonechocontrol.playCommand.option.GoodMorning = Guten Morgen channel-type.amazonechocontrol.playCommand.option.SingASong = Lied channel-type.amazonechocontrol.playCommand.option.TellStory = Geschichte channel-type.amazonechocontrol.playCommand.option.TellStory = Zusammenfassung + +channel-type-amazonechocontrol.lightState.label = Status der Lampe +channel-type-amazonechocontrol.lightState.description = Zustand (Ein/Aus) der Lampe + +channel-type-amazonechocontrol.lightColor.label = Farbe deiner Lampe +channel-type-amazonechocontrol.lightColor.description = Farbe der smarten Lampe +channel-type-amazonechocontrol.lightColor.option.red = Rot +channel-type-amazonechocontrol.lightColor.option.crimson = Purpur +channel-type-amazonechocontrol.lightColor.option.salmon = Lachs +channel-type-amazonechocontrol.lightColor.option.orange = Orange +channel-type-amazonechocontrol.lightColor.option.gold = Gold +channel-type-amazonechocontrol.lightColor.option.yellow = Gelb +channel-type-amazonechocontrol.lightColor.option.green = Grün +channel-type-amazonechocontrol.lightColor.option.turquoise = Türkis +channel-type-amazonechocontrol.lightColor.option.cyan = Cyan +channel-type-amazonechocontrol.lightColor.option.sky_blue = Himmelblau +channel-type-amazonechocontrol.lightColor.option.blue = Blau +channel-type-amazonechocontrol.lightColor.option.purple = Lila +channel-type-amazonechocontrol.lightColor.option.magenta = Magenta +channel-type-amazonechocontrol.lightColor.option.pink = Pink +channel-type-amazonechocontrol.lightColor.option.lavender = Lavendel + +channel-type-amazonechocontrol.whiteTemperature.label = Weißton der Lampe +channel-type-amazonechocontrol.whiteTemperature.description = Weißtemperatur der Lampe am Amazon Echo +channel-type-amazonechocontrol.whiteTemperature.option.warm_white = Warmweiß +channel-type-amazonechocontrol.whiteTemperature.option.soft_white = Sanftes Weiß +channel-type-amazonechocontrol.whiteTemperature.option.white = Weiß +channel-type-amazonechocontrol.whiteTemperature.option.daylight_white = Tageslicht +channel-type-amazonechocontrol.whiteTemperature.option.cool_white = Kaltweiß + +channel-type-amazonechocontrol.lightBrightness.label = Helligkeit +channel-type-amazonechocontrol.lightBrightness.description = Helligkeit der Lampe diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.amazonechocontrol/src/main/resources/ESH-INF/thing/thing-types.xml index 54b7acb0f7a42..e5b9607d00336 100644 --- a/bundles/org.openhab.binding.amazonechocontrol/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.amazonechocontrol/src/main/resources/ESH-INF/thing/thing-types.xml @@ -1,11 +1,18 @@ - Amazon Account where the amazon echo devices are registered. + + + 20 + Defines the time in seconds for openhab to pull the state of the bulbs. + + true + Defines if openhab should discover your bulbs connected to the Amazon Echo. + @@ -27,14 +34,17 @@ - + - + - + @@ -47,7 +57,8 @@ - + @@ -80,15 +91,19 @@ - + - + - + + @@ -100,7 +115,8 @@ - + @@ -133,15 +149,19 @@ - + - + - + + @@ -153,7 +173,8 @@ - + @@ -182,11 +203,13 @@ - + - + serialNumber @@ -210,6 +233,32 @@ + + + + + + Represents a light in your amazon account + + + + + + + + + + + + + Group of light bulbs in your amazon account + + + + + + + Switch @@ -267,7 +316,8 @@ Amazon Music play list id (Write only, no current state) - + String Id of the playlist which was started with openHAB @@ -365,6 +415,11 @@ Voice command as text. E.g. 'Yesterday from the Beatles' (Write only) + + String + + Display the announcement message on the display (Write only). See in the tutorial section of the binding description to learn how it's possible to set the title and turn off the sound. + String @@ -426,6 +481,7 @@ Ascending alarm up to the configured volume + DateTime @@ -450,4 +506,52 @@ Next timer + + Switch + + State (ON/OFF) of a light + + + String + + The color of a smart bulb + + + + + + + + + + + + + + + + + + + + + + String + + The temperature of white + + + + + + + + + + + + Dimmer + + The brightness of the light + diff --git a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/model/Sun.java b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/model/Sun.java index 891c5c4b9b824..708896475c03e 100644 --- a/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/model/Sun.java +++ b/bundles/org.openhab.binding.astro/src/main/java/org/openhab/binding/astro/internal/model/Sun.java @@ -193,6 +193,22 @@ public void setNight(Range night) { ranges.put(SunPhaseName.NIGHT, night); } + /** + * Sets the rise range. + */ + public void setRise(Range rise) { + super.setRise(rise); + ranges.put(SunPhaseName.SUN_RISE, rise); + } + + /** + * Sets the set range. + */ + public void setSet(Range set) { + super.setSet(set); + ranges.put(SunPhaseName.SUN_SET, set); + } + /** * Returns the sun position. */ diff --git a/bundles/org.openhab.binding.astro/src/test/java/org/openhab/binding/astro/internal/calc/SunCalcTest.java b/bundles/org.openhab.binding.astro/src/test/java/org/openhab/binding/astro/internal/calc/SunCalcTest.java new file mode 100644 index 0000000000000..c93c38be7ffa8 --- /dev/null +++ b/bundles/org.openhab.binding.astro/src/test/java/org/openhab/binding/astro/internal/calc/SunCalcTest.java @@ -0,0 +1,282 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.astro.internal.calc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.openhab.binding.astro.internal.model.Sun; +import org.openhab.binding.astro.internal.model.SunPhaseName; + +/*** + * Specific unit tests to check if {@link SunCalc} generates correct data for + * Amsterdam city on 27 February 2019. In particular the following cases are + * covered: + *
    + *
  • checks if generated data are the same (with some accuracy) as produced by + * haevens-above.com
  • + *
  • checks if the generated {@link Sun#getAllRanges()} are consistent with + * each other
  • + *
+ * + * @author Witold Markowski + * @see [astro] + * Sun Phase returns UNDEF + * @see Heavens Above Sun + */ +public class SunCalcTest { + + private final static Calendar FEB_27_2019 = new GregorianCalendar(2019, Calendar.FEBRUARY, 27); + private final static double AMSTERDAM_LATITUDE = 52.367607; + private final static double AMSTERDAM_LONGITUDE = 4.8978293; + private final static double AMSTERDAM_ALTITUDE = 0.0; + private final static int ACCURACY_IN_MILLIS = 3 * 60 * 1000; + + private SunCalc sunCalc; + + @Before + public void init() { + FEB_27_2019.setTimeZone(TimeZone.getTimeZone("Europe/Amsterdam")); + sunCalc = new SunCalc(); + } + + @Test + public void testGetSunInfoForOldDate() { + Calendar calendar = new GregorianCalendar(2019, Calendar.FEBRUARY, 27); + TimeZone.getAvailableIDs(); + calendar.setTimeZone(TimeZone.getTimeZone("Europe/Amsterdam")); + + Sun sun = sunCalc.getSunInfo(calendar, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + assertNotNull(sun.getNight()); + + assertNotNull(sun.getAstroDawn()); + assertNotNull(sun.getNauticDawn()); + assertNotNull(sun.getCivilDawn()); + + assertNotNull(sun.getRise()); + + assertNotNull(sun.getDaylight()); + assertNotNull(sun.getNoon()); + assertNotNull(sun.getSet()); + + assertNotNull(sun.getCivilDusk()); + assertNotNull(sun.getNauticDusk()); + assertNotNull(sun.getAstroDusk()); + assertNotNull(sun.getNight()); + + assertNotNull(sun.getMorningNight()); + assertNotNull(sun.getEveningNight()); + + // for an old date the phase is always null + assertNull(sun.getPhase().getName()); + } + + @Test + public void testGetSunInfoForAstronomicalDawnAccuracy() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + // expected result from haevens-above.com is 27 Feb 2019 05:39 till 06:18 + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 5, 39).getTimeInMillis(), + sun.getAstroDawn().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS); + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 6, 18).getTimeInMillis(), + sun.getAstroDawn().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS); + } + + @Test + public void testGetSunInfoForNauticDawnAccuracy() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + // expected result from haevens-above.com is 27 Feb 2019 06:18 till 06:58 + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 6, 18).getTimeInMillis(), + sun.getNauticDawn().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS); + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 6, 58).getTimeInMillis(), + sun.getNauticDawn().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS); + } + + @Test + public void testGetSunInfoForCivilDawnAccuracy() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + // expected result from haevens-above.com is 27 Feb 2019 06:58 till 07:32 + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 6, 58).getTimeInMillis(), + sun.getCivilDawn().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS); + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 7, 32).getTimeInMillis(), + sun.getCivilDawn().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS); + } + + @Test + public void testGetSunInfoForRiseAccuracy() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + // expected result from haevens-above.com is 27 Feb 2019 07:32 + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 7, 32).getTimeInMillis(), + sun.getRise().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS); + } + + @Test + public void testGetSunInfoForSunNoonAccuracy() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + // expected result from haevens-above.com is 27 Feb 2019 12:54 + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 12, 54).getTimeInMillis(), + sun.getNoon().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS); + } + + @Test + public void testGetSunInfoForSetAccuracy() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + // expected result from haevens-above.com is 27 Feb 2019 18:15 + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 18, 15).getTimeInMillis(), + sun.getSet().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS); + } + + @Test + public void testGetSunInfoForCivilDuskAccuracy() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + // expected result from haevens-above.com is 27 Feb 2019 18:15 till 18:50 + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 18, 15).getTimeInMillis(), + sun.getCivilDusk().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS); + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 18, 50).getTimeInMillis(), + sun.getCivilDusk().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS); + } + + @Test + public void testGetSunInfoForNauticDuskAccuracy() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + // expected result from haevens-above.com is 27 Feb 2019 18:50 till 19:29 + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 18, 50).getTimeInMillis(), + sun.getNauticDusk().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS); + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 19, 29).getTimeInMillis(), + sun.getNauticDusk().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS); + } + + @Test + public void testGetSunInfoForAstronomicalDuskAccuracy() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + // expected result from haevens-above.com is 27 Feb 2019 19:29 till 20:09 + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 19, 29).getTimeInMillis(), + sun.getAstroDusk().getStart().getTimeInMillis(), ACCURACY_IN_MILLIS); + assertEquals(new GregorianCalendar(2019, Calendar.FEBRUARY, 27, 20, 9).getTimeInMillis(), + sun.getAstroDusk().getEnd().getTimeInMillis(), ACCURACY_IN_MILLIS); + } + + @Test + @Ignore + public void testRangesForCoherenceBetweenNightEndAndAstroDawnStart() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + assertEquals(sun.getAllRanges().get(SunPhaseName.NIGHT).getEnd(), + sun.getAllRanges().get(SunPhaseName.ASTRO_DAWN).getStart()); + } + + @Test + public void testRangesForCoherenceBetweenMorningNightEndAndAstroDawnStart() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + assertEquals(sun.getAllRanges().get(SunPhaseName.MORNING_NIGHT).getEnd(), + sun.getAllRanges().get(SunPhaseName.ASTRO_DAWN).getStart()); + } + + @Test + public void testRangesForCoherenceBetweenAstroDownEndAndNauticDawnStart() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + assertEquals(sun.getAllRanges().get(SunPhaseName.ASTRO_DAWN).getEnd(), + sun.getAllRanges().get(SunPhaseName.NAUTIC_DAWN).getStart()); + } + + @Test + public void testRangesForCoherenceBetweenNauticDawnEndAndCivilDawnStart() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + assertEquals(sun.getAllRanges().get(SunPhaseName.NAUTIC_DAWN).getEnd(), + sun.getAllRanges().get(SunPhaseName.CIVIL_DAWN).getStart()); + } + + @Test + public void testRangesForCoherenceBetweenCivilDawnEndAndSunRiseStart() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + assertEquals(sun.getAllRanges().get(SunPhaseName.CIVIL_DAWN).getEnd(), + sun.getAllRanges().get(SunPhaseName.SUN_RISE).getStart()); + } + + @Test + public void testRangesForCoherenceBetweenSunRiseEndAndDaylightStart() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + assertEquals(sun.getAllRanges().get(SunPhaseName.SUN_RISE).getEnd(), + sun.getAllRanges().get(SunPhaseName.DAYLIGHT).getStart()); + } + + @Test + public void testRangesForCoherenceBetweenDaylightEndAndSunSetStart() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + assertEquals(sun.getAllRanges().get(SunPhaseName.DAYLIGHT).getEnd(), + sun.getAllRanges().get(SunPhaseName.SUN_SET).getStart()); + } + + @Test + public void testRangesForCoherenceBetweenSunSetEndAndCivilDuskStart() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + assertEquals(sun.getAllRanges().get(SunPhaseName.SUN_SET).getEnd(), + sun.getAllRanges().get(SunPhaseName.CIVIL_DUSK).getStart()); + } + + @Test + public void testRangesForCoherenceBetweenCivilDuskEndAndNauticDuskStart() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + assertEquals(sun.getAllRanges().get(SunPhaseName.CIVIL_DUSK).getEnd(), + sun.getAllRanges().get(SunPhaseName.NAUTIC_DUSK).getStart()); + } + + @Test + public void testRangesForCoherenceBetweenNauticDuskEndAndAstroDuskStart() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + assertEquals(sun.getAllRanges().get(SunPhaseName.NAUTIC_DUSK).getEnd(), + sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK).getStart()); + } + + @Test + public void testRangesForCoherenceBetweenAstroDuskEndAndNightStart() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + assertEquals(sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK).getEnd(), + sun.getAllRanges().get(SunPhaseName.NIGHT).getStart()); + } + + @Test + public void testRangesForCoherenceBetweenAstroDuskEndAndEveningNightStart() { + Sun sun = sunCalc.getSunInfo(FEB_27_2019, AMSTERDAM_LATITUDE, AMSTERDAM_LONGITUDE, AMSTERDAM_ALTITUDE); + + assertEquals(sun.getAllRanges().get(SunPhaseName.ASTRO_DUSK).getEnd(), + sun.getAllRanges().get(SunPhaseName.EVENING_NIGHT).getStart()); + } +} diff --git a/bundles/org.openhab.binding.astro/src/test/java/org/openhab/binding/astro/internal/model/SunTest.java b/bundles/org.openhab.binding.astro/src/test/java/org/openhab/binding/astro/internal/model/SunTest.java new file mode 100644 index 0000000000000..c966029d7c448 --- /dev/null +++ b/bundles/org.openhab.binding.astro/src/test/java/org/openhab/binding/astro/internal/model/SunTest.java @@ -0,0 +1,163 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.astro.internal.model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.UnDefType; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.astro.internal.util.PropertyUtils; + +/*** + * A set of standard unit test of {@link Sun} class. In particular it checks if + * {@link Sun#getAllRanges()} contains a correct {@link SunPhaseName}. + * + * @author Witold Markowski + * @see [astro] + * Sun Phase returns UNDEF + */ +public class SunTest { + + private Sun sun; + + @Before + public void init() { + sun = new Sun(); + } + + @Test + public void testConstructor() throws Exception { + assertNotNull(sun.getPhase()); + assertEquals(UnDefType.UNDEF, PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), null, sun)); + } + + @Test + public void testGetStateWhenNullPhaseName() throws Exception { + sun.getPhase().setName(null); + + assertEquals(UnDefType.UNDEF, PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), null, sun)); + } + + @Test + public void testGetStateWhenNotNullPhaseName() throws Exception { + sun.getPhase().setName(SunPhaseName.DAYLIGHT); + + assertEquals(new StringType("DAYLIGHT"), + PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), null, sun)); + } + + @Test(expected = NullPointerException.class) + public void testGetStateWhenNullPhase() throws Exception { + sun.setPhase(null); + + assertNull(sun.getPhase()); + assertEquals(UnDefType.UNDEF, PropertyUtils.getState(new ChannelUID("astro:sun:home:phase#name"), null, sun)); + } + + @Test + public void testGetAllRangesForNight() { + sun.setNight(new Range()); + + assertTrue(sun.getAllRanges().containsKey(SunPhaseName.NIGHT)); + } + + @Test + public void testGetAllRangesForMorningNight() { + sun.setMorningNight(new Range()); + + assertTrue(sun.getAllRanges().containsKey(SunPhaseName.MORNING_NIGHT)); + } + + @Test + public void testGetAllRangesForAstroDawn() { + sun.setAstroDawn(new Range()); + + assertTrue(sun.getAllRanges().containsKey(SunPhaseName.ASTRO_DAWN)); + } + + @Test + public void testGetAllRangesForNauticDawn() { + sun.setNauticDawn(new Range()); + + assertTrue(sun.getAllRanges().containsKey(SunPhaseName.NAUTIC_DAWN)); + } + + @Test + public void testGetAllRangesForCivilDawn() { + sun.setCivilDawn(new Range()); + + assertTrue(sun.getAllRanges().containsKey(SunPhaseName.CIVIL_DAWN)); + } + + @Test + public void testGetAllRangesForRise() { + sun.setRise(new Range()); + + assertTrue(sun.getAllRanges().containsKey(SunPhaseName.SUN_RISE)); + } + + @Test + public void testGetAllRangesForDaylight() { + sun.setDaylight(new Range()); + + assertTrue(sun.getAllRanges().containsKey(SunPhaseName.DAYLIGHT)); + } + + @Test + public void testGetAllRangesForNoon() { + sun.setNoon(new Range()); + + assertTrue(sun.getAllRanges().containsKey(SunPhaseName.NOON)); + } + + @Test + public void testGetAllRangesForSet() { + sun.setSet(new Range()); + + assertTrue(sun.getAllRanges().containsKey(SunPhaseName.SUN_SET)); + } + + @Test + public void testGetAllRangesForCivilDusk() { + sun.setCivilDusk(new Range()); + + assertTrue(sun.getAllRanges().containsKey(SunPhaseName.CIVIL_DUSK)); + } + + @Test + public void testGetAllRangesForNauticDusk() { + sun.setNauticDusk(new Range()); + + assertTrue(sun.getAllRanges().containsKey(SunPhaseName.NAUTIC_DUSK)); + } + + @Test + public void testGetAllRangesForAstroDusk() { + sun.setAstroDusk(new Range()); + + assertTrue(sun.getAllRanges().containsKey(SunPhaseName.ASTRO_DUSK)); + } + + @Test + public void testGetAllRangesForEveningNight() { + sun.setEveningNight(new Range()); + + assertTrue(sun.getAllRanges().containsKey(SunPhaseName.EVENING_NIGHT)); + } +} diff --git a/bundles/org.openhab.binding.avmfritz/pom.xml b/bundles/org.openhab.binding.avmfritz/pom.xml index 7fed52f88efaf..481597298aa46 100644 --- a/bundles/org.openhab.binding.avmfritz/pom.xml +++ b/bundles/org.openhab.binding.avmfritz/pom.xml @@ -33,9 +33,9 @@ provided - javax.activation - activation - 1.1.1 + org.apache.servicemix.specs + org.apache.servicemix.specs.activation-api-1.1 + 2.9.0 provided diff --git a/bundles/org.openhab.binding.bluetooth.bluez/lib/tinyb-0.5.1.jar b/bundles/org.openhab.binding.bluetooth.bluez/lib/tinyb-0.5.1.jar new file mode 100644 index 0000000000000..dca6d7ad8bd83 Binary files /dev/null and b/bundles/org.openhab.binding.bluetooth.bluez/lib/tinyb-0.5.1.jar differ diff --git a/bundles/org.openhab.binding.bluetooth.bluez/pom.xml b/bundles/org.openhab.binding.bluetooth.bluez/pom.xml index f21776a482aaa..8d92f24bb8a47 100644 --- a/bundles/org.openhab.binding.bluetooth.bluez/pom.xml +++ b/bundles/org.openhab.binding.bluetooth.bluez/pom.xml @@ -20,11 +20,6 @@ ${project.version} provided - - org.sputnikdev - bluetooth-manager-tinyb - 1.3.3 - provided - + diff --git a/bundles/org.openhab.binding.bluetooth.bluez/src/main/feature/feature.xml b/bundles/org.openhab.binding.bluetooth.bluez/src/main/feature/feature.xml index 297798a45403b..9c1f255ac4d0a 100644 --- a/bundles/org.openhab.binding.bluetooth.bluez/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.bluetooth.bluez/src/main/feature/feature.xml @@ -5,8 +5,6 @@ openhab-runtime-base openhab-transport-serial - wrap - wrap:mvn:org.sputnikdev/bluetooth-manager-tinyb/1.3.3$Bundle-Name=Tinyb&Bundle-SymbolicName=org.sputnikdev.bluetooth-manager-tinyb&Bundle-Version=1.3.3 mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth/${project.version} mvn:org.openhab.addons.bundles/org.openhab.binding.bluetooth.bluez/${project.version} diff --git a/bundles/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 index 38d8953c4e37a..54bf41a03014c 100644 --- a/bundles/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 @@ -88,8 +88,12 @@ public BlueZBridgeHandler(Bridge bridge) { @Override public void initialize() { + BluetoothManager manager; try { - BluetoothManager.getBluetoothManager(); + manager = BluetoothManager.getBluetoothManager(); + if (manager == null) { + throw new IllegalStateException("Received null BlueZ manager"); + } } catch (UnsatisfiedLinkError e) { throw new IllegalStateException("BlueZ JNI connection cannot be established.", e); } catch (RuntimeException e) { @@ -116,9 +120,14 @@ public void initialize() { } logger.debug("Creating BlueZ adapter with address '{}'", address); - for (tinyb.BluetoothAdapter a : BluetoothManager.getBluetoothManager().getAdapters()) { - if (a.getAddress().equals(address.toString())) { - adapter = a; + + for (tinyb.BluetoothAdapter adapter : manager.getAdapters()) { + if (adapter == null) { + logger.warn("got null adapter from bluetooth manager"); + continue; + } + if (adapter.getAddress().equals(address.toString())) { + this.adapter = adapter; updateStatus(ThingStatus.ONLINE); startDiscovery(); discoveryJob = scheduler.scheduleWithFixedDelay(this::refreshDevices, 0, 10, TimeUnit.SECONDS); diff --git a/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/package-info.java b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/package-info.java new file mode 100644 index 0000000000000..857b851de19e0 --- /dev/null +++ b/bundles/org.openhab.binding.bluetooth.bluez/src/main/java/org/openhab/binding/bluetooth/bluez/package-info.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 + */ +@org.osgi.annotation.bundle.Header(name = org.osgi.framework.Constants.BUNDLE_NATIVECODE, value = "lib/armv6hf/libjavatinyb.so;lib/armv6hf/libtinyb.so;processor=arm;osname=linux") +package org.openhab.binding.bluetooth.bluez; + +/** + * Additional information for BlueZ package + * + * @author Jan N. Klug - Initial contribution + * + */ \ No newline at end of file diff --git a/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/lib/armv6hf/libjavatinyb.so b/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/lib/armv6hf/libjavatinyb.so new file mode 100644 index 0000000000000..b1b5da17fc90b Binary files /dev/null and b/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/lib/armv6hf/libjavatinyb.so differ diff --git a/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/lib/armv6hf/libtinyb.so b/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/lib/armv6hf/libtinyb.so new file mode 100644 index 0000000000000..bf444a7f101d5 Binary files /dev/null and b/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/lib/armv6hf/libtinyb.so differ diff --git a/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/lib/x86-64/libjavatinyb.so b/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/lib/x86-64/libjavatinyb.so new file mode 100644 index 0000000000000..308abed357c2a Binary files /dev/null and b/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/lib/x86-64/libjavatinyb.so differ diff --git a/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/lib/x86-64/libtinyb.so b/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/lib/x86-64/libtinyb.so new file mode 100644 index 0000000000000..430f3b6c44495 Binary files /dev/null and b/bundles/org.openhab.binding.bluetooth.bluez/src/main/resources/lib/x86-64/libtinyb.so differ diff --git a/bundles/org.openhab.binding.boschindego/pom.xml b/bundles/org.openhab.binding.boschindego/pom.xml index 82d82726c5d49..af10b3acb4cfa 100644 --- a/bundles/org.openhab.binding.boschindego/pom.xml +++ b/bundles/org.openhab.binding.boschindego/pom.xml @@ -33,8 +33,8 @@ provided - de.zazaz.iot.bosch.indego - bosch-indego-controller-lib + org.openhab.osgiify + de.zazaz.iot.bosch.indego.bosch-indego-controller-lib 0.8 provided diff --git a/bundles/org.openhab.binding.boschindego/src/main/feature/feature.xml b/bundles/org.openhab.binding.boschindego/src/main/feature/feature.xml index 13cb6434cc123..a25418aadaef1 100644 --- a/bundles/org.openhab.binding.boschindego/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.boschindego/src/main/feature/feature.xml @@ -4,13 +4,12 @@ openhab-runtime-base - wrap 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 mvn:org.apache.httpcomponents/httpcore-osgi/4.4.9 mvn:org.apache.httpcomponents/httpclient-osgi/4.5.5 - wrap:mvn:de.zazaz.iot.bosch.indego/bosch-indego-controller-lib/0.8$Bundle-Name=Bosch%20Indego;Bundle-SymbolicName=de.zazaz.iot.bosch.indego.bosch-indego-controller-lib&Bundle-Version=0.8 + mvn:org.openhab.osgiify/de.zazaz.iot.bosch.indego.bosch-indego-controller-lib/0.8 mvn:org.openhab.addons.bundles/org.openhab.binding.boschindego/${project.version} diff --git a/bundles/org.openhab.binding.buienradar/README.md b/bundles/org.openhab.binding.buienradar/README.md index 05781207aef14..03d0133d1cc2d 100644 --- a/bundles/org.openhab.binding.buienradar/README.md +++ b/bundles/org.openhab.binding.buienradar/README.md @@ -1,12 +1,17 @@ # Buienradar Binding -The Buienradar Binding periodically (5 minute intervals) retrieves rainfall predictions from the Dutch buienradar.nl webservice API. +The Buienradar Binding periodically (5 minute intervals) retrieves rainfall predictions from the Dutch +[buienradar.nl webservice API.](https://www.buienradar.nl/overbuienradar/gratis-weerdata). -This allows us to warn of upcoming rainfal when there are still open windows, or prevents watering the outside plants needlessly. +Using the binding, we can + +* warn of upcoming rainfall when there are open windows or doors +* prevent watering the outside plants needlessly, +* warn when we are about to leave the house. ## 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. +The binding 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 @@ -50,3 +55,20 @@ Number RAIN_115MIN "Rain 115 min." (Rain) {channel="buienradar:rain_forecast:hom Number RAIN_120MIN "Rain 120 min." (Rain) {channel="buienradar:rain_forecast:home:forecast_120" } ``` +## Example data visualisation + +In this example we use the 'Discrete' plugin of Grafana to visualize the predictions. Mappings are made according to precipitation intensity (light, medium, heavy) and those categories are given appropriate colors. + +![Z-Way Binding](doc/img/grafana-dashboard.png) + +The mappings are as follows: + +* 0 – 0.01: None (rgba(204, 204, 204, 0)) +* 0.01 – 1: Very light (#badff4) +* 1 – 5: Light (#6ed0e0) +* 5 – 20: Medium (#1f78c1) +* 20 – 50: Heavy (#ef843c) +* 50 – 80: Very heavy (#e24d42) +* 80 – 100: Extremely heavy (#890f02) + + diff --git a/bundles/org.openhab.binding.buienradar/doc/img/grafana-dashboard.png b/bundles/org.openhab.binding.buienradar/doc/img/grafana-dashboard.png new file mode 100644 index 0000000000000..466102e9690c3 Binary files /dev/null and b/bundles/org.openhab.binding.buienradar/doc/img/grafana-dashboard.png differ 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 index a009b6ea1c1a9..c3df364683531 100644 --- 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 @@ -29,6 +29,7 @@ public class BuienradarBindingConstants { // List of all Thing Type UIDs public static final ThingTypeUID THING_TYPE_RAIN_FORECAST = new ThingTypeUID(BINDING_ID, "rain_forecast"); + public static final String ACTUAL_DATETIME = "actual_datetime"; public static final String FORECAST_0 = "forecast_0"; public static final String FORECAST_5 = "forecast_5"; public static final String FORECAST_10 = "forecast_10"; 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 index 65e46acc5beb2..601b0c74876fd 100644 --- 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 @@ -28,4 +28,15 @@ public class BuienradarConfiguration { * Refresh interval for retrieving results from buienradar. */ public Integer refreshIntervalMinutes; + + /** + * Number of retries to try to retrieve buienradar results. + */ + public Integer retries; + + /** + * Exponential backoff base value for retries in seconds. For example, when this is 2 seconds, will retry at 2, 4, + * 8, 16, 32, 64 seconds. + */ + public Integer exponentialBackoffRetryBaseInSeconds; } 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 index 4431c42c54ac8..02dd02ff7e617 100644 --- 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 @@ -20,15 +20,20 @@ import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Locale; +import java.util.Optional; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; -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.DateTimeType; import org.eclipse.smarthome.core.library.types.PointType; import org.eclipse.smarthome.core.library.types.QuantityType; +import org.eclipse.smarthome.core.library.unit.SmartHomeUnits; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; @@ -41,8 +46,6 @@ 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. @@ -58,10 +61,14 @@ public class BuienradarHandler extends BaseThingHandler { private @NonNullByDefault({}) ScheduledFuture listenableFuture; + /** + * Prevents race-condition access to listenableFuture. + */ + private final Lock listenableFutureLock = new ReentrantLock(); + private @NonNullByDefault({}) PointType location; - private static final Unit MILLIMETRE_PER_HOUR = Units.METRE.divide(1000).divide(Units.HOUR) - .asType(Speed.class); + private @NonNullByDefault({}) BuienradarConfiguration config; public BuienradarHandler(Thing thing) { super(thing); @@ -75,7 +82,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { @SuppressWarnings("null") @Override public void initialize() { - BuienradarConfiguration config = getConfigAs(BuienradarConfiguration.class); + this.config = getConfigAs(BuienradarConfiguration.class); boolean configValid = true; if (StringUtils.trimToNull(config.location) == null) { @@ -96,29 +103,59 @@ public void initialize() { if (configValid) { updateStatus(ThingStatus.UNKNOWN); } - if (listenableFuture == null || listenableFuture.isCancelled()) { - listenableFuture = scheduler.scheduleWithFixedDelay(this::refresh, 0L, config.refreshIntervalMinutes, - MINUTES); + try { + listenableFutureLock.lock(); + if (listenableFuture == null || listenableFuture.isCancelled()) { + listenableFuture = scheduler.scheduleWithFixedDelay(() -> refresh(), 0L, config.refreshIntervalMinutes, + MINUTES); + } + } finally { + listenableFutureLock.unlock(); } } private void refresh() { + refresh(config.retries, ZonedDateTime.now().plusMinutes(config.refreshIntervalMinutes), + config.exponentialBackoffRetryBaseInSeconds); + } + + private void refresh(int tries, ZonedDateTime nextRefresh, int retryInSeconds) { + if (nextRefresh.isBefore(ZonedDateTime.now())) { + // The next refresh is already running, stop retries. + return; + } + if (tries <= 0) { + // We are out of tries, stop retrying. + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + return; + } try { @SuppressWarnings("null") - final List predictions = client.getPredictions(location); + final Optional> predictionsOpt = client.getPredictions(location); + if (!predictionsOpt.isPresent()) { + // Did not get a result, retry the retrieval. + logger.warn("Did not get a result from buienradar. Retrying. {} tries remaining, waiting {} seconds.", + tries, retryInSeconds); + scheduler.schedule(() -> refresh(tries - 1, nextRefresh, retryInSeconds * 2), retryInSeconds, + TimeUnit.SECONDS); + return; + } + final List predictions = predictionsOpt.get(); + if (!predictions.isEmpty()) { + final ZonedDateTime actual = predictions.get(0).getActualDateTime(); + updateState(BuienradarBindingConstants.ACTUAL_DATETIME, new DateTimeType(actual)); + } 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)); + + final long minutesFromNow = prediction.getActualDateTime().until(prediction.getDateTimeOfPrediction(), + ChronoUnit.MINUTES); + logger.debug("Forecast for {} at {} made at {} is {}", minutesFromNow, + prediction.getDateTimeOfPrediction(), prediction.getActualDateTime(), intensity); + if (minutesFromNow >= 0 && minutesFromNow <= 115) { + final String label = String.format(Locale.ENGLISH, "forecast_%d", minutesFromNow); + + updateState(label, new QuantityType(intensity, SmartHomeUnits.MILLIMETRE_PER_HOUR)); } } @@ -133,9 +170,14 @@ private void refresh() { @SuppressWarnings("null") @Override public void dispose() { - if (listenableFuture != null && !listenableFuture.isCancelled()) { - listenableFuture.cancel(true); - listenableFuture = null; + try { + listenableFutureLock.lock(); + if (listenableFuture != null && !listenableFuture.isCancelled()) { + listenableFuture.cancel(true); + listenableFuture = null; + } + } finally { + listenableFutureLock.unlock(); } } } 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 index cc4c71845f055..6efff28380790 100644 --- 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 @@ -20,15 +20,15 @@ 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.Optional; 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.eclipse.smarthome.io.net.http.HttpUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -101,10 +101,13 @@ public static ZonedDateTime parseDateTime(String timeStr, ZonedDateTime now) thr * * @param line The line to parse, such as 100|23:00 * @param now The reference time to determine which instant to match to. + * @param actual The date time of the 'actual' prediction if known. If None is given, it is assumed this is the + * first row of the results. * @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 { + public static Prediction parseLine(String line, ZonedDateTime now, Optional actual) + throws BuienradarParseException { final String[] lineElements = line.trim().split("\\|"); if (lineElements.length != 2) { throw new BuienradarParseException( @@ -120,29 +123,44 @@ public final BigDecimal getIntensity() { } @Override - public final ZonedDateTime getDateTime() { + public ZonedDateTime getDateTimeOfPrediction() { return dateTime; } + + @Override + public ZonedDateTime getActualDateTime() { + return actual.orElseGet(this::getDateTimeOfPrediction); + } }; } @Override - public List getPredictions(PointType location) throws IOException { - final String address = String.format(Locale.ENGLISH, BASE_ADDRESS + "?lat=%.8f&lon=%.8f", + public Optional> getPredictions(PointType location) throws IOException { + final String address = String.format(Locale.ENGLISH, BASE_ADDRESS + "?lat=%.2f&lon=%.2f", location.getLatitude().doubleValue(), location.getLongitude().doubleValue()); - final String result = HttpUtil.executeUrl("GET", address, TIMEOUT_MS); + + final String result; + try { + result = HttpUtil.executeUrl("GET", address, TIMEOUT_MS); + } catch (IOException e) { + logger.warn("IO Exception when trying to retrieve Buienradar results", e); + return Optional.empty(); + } if (result.trim().isEmpty()) { logger.warn(String.format("Buienradar API at URI %s return empty result", address)); - return Collections.emptyList(); + return Optional.empty(); } final List predictions = new ArrayList(24); final List errors = new LinkedList(); logger.debug("Returned result from buienradar: {}", result); final String[] lines = result.split("\n"); + Optional actual = Optional.empty(); for (String line : lines) { try { - predictions.add(parseLine(line, ZonedDateTime.now())); + final Prediction prediction = parseLine(line, ZonedDateTime.now(), actual); + actual = Optional.of(prediction.getActualDateTime()); + predictions.add(prediction); } catch (BuienradarParseException e) { errors.add(e.getMessage()); } @@ -150,7 +168,7 @@ public List getPredictions(PointType location) throws IOException { if (!errors.isEmpty()) { logger.warn("Could not parse all results: " + errors.stream().collect(Collectors.joining(", "))); } - return predictions; + return Optional.of(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 index 7764566ac687b..8aabe8d33c7de 100644 --- 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 @@ -32,5 +32,10 @@ public interface Prediction { /** * Date-time of prediction. */ - ZonedDateTime getDateTime(); + ZonedDateTime getDateTimeOfPrediction(); + + /** + * Date-time of when the prediction was made. + */ + ZonedDateTime getActualDateTime(); } 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 index d3a23f5dd82b5..18f3ea978281a 100644 --- 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 @@ -14,6 +14,7 @@ import java.io.IOException; import java.util.List; +import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.PointType; @@ -25,5 +26,6 @@ */ @NonNullByDefault public interface PredictionAPI { - List getPredictions(PointType location) throws IOException; + + Optional> getPredictions(PointType location) throws IOException; } 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 index effd19fd2f2a1..0ff4e018c8c02 100644 --- 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 @@ -11,6 +11,7 @@ two hours, in 5 minute increments using the buienradar.nl webservice. + @@ -50,10 +51,29 @@ Refresh interval in minutes 5 + + + + Number of retries to try to retrieve buienradar results + 4 + + + + + Exponential back-off base value for retries in seconds. For example, when this is 2 seconds, will retry at 2, 4, 8, 16, 32, 64 seconds. + 5 + + + DateTime + + The actual date and time when the prediction was made." + + + Number:Speed 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 index 2290d02d1ef14..f60a5e329d85b 100644 --- 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 @@ -17,6 +17,7 @@ import java.math.BigDecimal; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.util.Optional; import org.junit.Test; import org.openhab.binding.buienradar.internal.buienradarapi.BuienradarParseException; @@ -26,6 +27,9 @@ 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 ACTUAL = ZonedDateTime.of(2019, 3, 10, 20, 35, 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")); @@ -71,8 +75,9 @@ public void testParseDateTimeTomorrow() throws BuienradarParseException { @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()); + final Prediction parsed = BuienradarPredictionAPI.parseLine("000|19:35", NOW, Optional.of(ACTUAL)); + assertEquals(ZonedDateTime.of(2019, 3, 11, 19, 35, 0, 0, ZoneId.of("Europe/Amsterdam")), + parsed.getDateTimeOfPrediction()); assertEquals(BigDecimal.valueOf(0, 2), parsed.getIntensity()); } diff --git a/bundles/org.openhab.binding.denonmarantz/pom.xml b/bundles/org.openhab.binding.denonmarantz/pom.xml index c42085c14a42a..c84e3469d47b4 100644 --- a/bundles/org.openhab.binding.denonmarantz/pom.xml +++ b/bundles/org.openhab.binding.denonmarantz/pom.xml @@ -34,9 +34,9 @@ provided - javax.activation - activation - 1.1.1 + org.apache.servicemix.specs + org.apache.servicemix.specs.activation-api-1.1 + 2.9.0 provided diff --git a/bundles/org.openhab.binding.dlinksmarthome/pom.xml b/bundles/org.openhab.binding.dlinksmarthome/pom.xml index 242f0fe90ddad..235a072239bff 100644 --- a/bundles/org.openhab.binding.dlinksmarthome/pom.xml +++ b/bundles/org.openhab.binding.dlinksmarthome/pom.xml @@ -34,9 +34,9 @@ provided - javax.activation - activation - 1.1.1 + org.apache.servicemix.specs + org.apache.servicemix.specs.activation-api-1.1 + 2.9.0 provided diff --git a/bundles/org.openhab.binding.dmx/README.md b/bundles/org.openhab.binding.dmx/README.md index daa0deeee8eda..5c60b9774c6b9 100644 --- a/bundles/org.openhab.binding.dmx/README.md +++ b/bundles/org.openhab.binding.dmx/README.md @@ -194,7 +194,7 @@ The next `ON` command uses these values instead of the default (or configuration |-----------------|---------------------|----------------------|----------------------------------------------------| |brightness |dimmer, tunablewhite |Switch, Dimmer | controls the brightness | |color |color |Switch, Dimmer, Color | allows to set the color and brightness | -|colortemperature |tunablewhite |Number | allows to set the color temperature | +|color_temperature|tunablewhite |Number | allows to set the color temperature | |brightness_r |color |Switch, Dimmer | controls the brightness of the red channel | |brightness_g |color |Switch, Dimmer | controls the brightness of the green channel | |brightness_b |color |Switch, Dimmer | controls the brightness of the blue channel | diff --git a/bundles/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 index 8d2eb503fb781..9e2291b8028e1 100644 --- a/bundles/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 @@ -18,7 +18,7 @@ */ public class EnOceanChannelRollershutterConfig { - public Integer shutTime; + public int shutTime; public EnOceanChannelRollershutterConfig() { shutTime = 255; diff --git a/bundles/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 index 39173b6620bec..0da5eb17c2132 100644 --- a/bundles/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 @@ -96,16 +96,24 @@ protected State convertToStateImpl(String channelId, String channelTypeId, State currentState = getCurrentStateFunc.apply(channelId); if (currentState != null) { - int direction = getDB_1() == MoveUp ? -1 : 1; int duration = ((getDB_3Value() << 8) + getDB_2Value()) / 10; // => Time in DB3 and DB2 is given // in ms - PercentType current = currentState.as(PercentType.class); - if (config != null && current != null) { + if (config != null) { EnOceanChannelRollershutterConfig c = config.as(EnOceanChannelRollershutterConfig.class); - if (c.shutTime != -1 && c.shutTime != 0) { - return new PercentType(Math.min(100, (Math.max(0, current.intValue() - + direction * ((duration * PercentType.HUNDRED.intValue()) / c.shutTime))))); + if (duration == c.shutTime) { + return getDB_1() == MoveUp ? PercentType.ZERO : PercentType.HUNDRED; + } else { + PercentType current = PercentType.ZERO; + if (currentState instanceof PercentType) { + current = currentState.as(PercentType.class); + } + + int direction = getDB_1() == MoveUp ? -1 : 1; + if (c.shutTime != -1 && c.shutTime != 0) { + return new PercentType(Math.min(100, (Math.max(0, current.intValue() + + direction * ((duration * PercentType.HUNDRED.intValue()) / c.shutTime))))); + } } } } diff --git a/bundles/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 index 3ea9ba2ce78ad..e36fc16cd1d16 100644 --- a/bundles/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 @@ -257,7 +257,7 @@ public enum EEPType { CHANNEL_MOTIONDETECTION, CHANNEL_BATTERY_VOLTAGE), OCCUPANCYSENSOR_A5_07_02(RORG._4BS, 0x07, 0x02, false, A5_07_02.class, THING_TYPE_OCCUPANCYSENSOR, CHANNEL_MOTIONDETECTION, CHANNEL_BATTERY_VOLTAGE), - OCCUPANCYSENSOR_A5_07_03(RORG._4BS, 0x07, 0x02, false, A5_07_03.class, THING_TYPE_OCCUPANCYSENSOR, + OCCUPANCYSENSOR_A5_07_03(RORG._4BS, 0x07, 0x03, false, A5_07_03.class, THING_TYPE_OCCUPANCYSENSOR, CHANNEL_ILLUMINATION, CHANNEL_MOTIONDETECTION, CHANNEL_BATTERY_VOLTAGE), LightTemperatureOccupancySensor_A5_08_01(RORG._4BS, 0x08, 0x01, false, A5_08_01.class, 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 index 2cddf477f78a1..542dd1780a6b1 100644 --- 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 @@ -154,7 +154,7 @@ public void espPacketReceived(ESP3Packet packet) { EEP eep = EEPFactory.buildEEP(receivingEEPType, (ERP1Message) packet); logger.debug("ESP Packet payload {} for {} received", HexUtils.bytesToHex(packet.getPayload()), - config.enoceanId); + HexUtils.bytesToHex(msg.getSenderId())); if (eep.isValid()) { byte[] senderId = msg.getSenderId(); diff --git a/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/Rollershutter.xml b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/Rollershutter.xml index 541d58f82ded1..89aae59187e69 100644 --- a/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/Rollershutter.xml +++ b/bundles/org.openhab.binding.enocean/src/main/resources/ESH-INF/thing/Rollershutter.xml @@ -48,6 +48,7 @@ + 4 true diff --git a/bundles/org.openhab.binding.globalcache/README.md b/bundles/org.openhab.binding.globalcache/README.md index b2d905edd7ce4..4004bc3e05228 100644 --- a/bundles/org.openhab.binding.globalcache/README.md +++ b/bundles/org.openhab.binding.globalcache/README.md @@ -135,7 +135,7 @@ A *Contact Closure channel* activates the contact closure (relay) on the iTach o For example, the following item links to the module 1, connector 1 channel on an iTach CC device. ``` -Contact MyRelay "My Relay [%s]" (gRelays) { channel="globalcache:itachCC:000C1E039BCF:cc-m1#c1" } +Switch MyRelay "My Relay [%s]" (gRelays) { channel="globalcache:itachCC:000C1E039BCF:cc-m1#c1" } ``` The item definition for an iTach Flex WiFi device would look like this. diff --git a/bundles/org.openhab.binding.globalcache/src/main/resources/ESH-INF/thing/channel-types.xml b/bundles/org.openhab.binding.globalcache/src/main/resources/ESH-INF/thing/channel-types.xml index 103e1eac1b866..9bbe57a5bf5a3 100644 --- a/bundles/org.openhab.binding.globalcache/src/main/resources/ESH-INF/thing/channel-types.xml +++ b/bundles/org.openhab.binding.globalcache/src/main/resources/ESH-INF/thing/channel-types.xml @@ -12,7 +12,7 @@ - Contact + Switch Transmits contact closure command on module:connector diff --git a/bundles/org.openhab.binding.innogysmarthome/pom.xml b/bundles/org.openhab.binding.innogysmarthome/pom.xml index 366501912c7ca..289ff4e26c46a 100644 --- a/bundles/org.openhab.binding.innogysmarthome/pom.xml +++ b/bundles/org.openhab.binding.innogysmarthome/pom.xml @@ -33,8 +33,8 @@ provided - com.google.http-client - google-http-client-gson + org.openhab.osgiify + com.google.http-client.google-http-client-gson 1.27.0 provided diff --git a/bundles/org.openhab.binding.innogysmarthome/src/main/feature/feature.xml b/bundles/org.openhab.binding.innogysmarthome/src/main/feature/feature.xml index c2480a7983ee1..77b5edaceaced 100644 --- a/bundles/org.openhab.binding.innogysmarthome/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.innogysmarthome/src/main/feature/feature.xml @@ -4,14 +4,13 @@ openhab-runtime-base - wrap mvn:org.apache.httpcomponents/httpcore-osgi/4.4.4 mvn:org.apache.httpcomponents/httpclient-osgi/4.5.2 mvn:com.google.guava/guava/20.0 mvn:commons-codec/commons-codec/1.10 mvn:com.google.oauth-client/google-oauth-client/1.27.0 mvn:com.google.http-client/google-http-client/1.27.0 - wrap:mvn:com.google.http-client/google-http-client-gson/1.27.0$Bundle-Name=GSON%202%20Extensions&Bundle-SymbolicName=com.google.http-client.google-http-client-gson&Bundle-Version=1.27.0 + mvn:org.openhab.osgiify/com.google.http-client.google-http-client-gson/1.27.0 mvn:org.openhab.addons.bundles/org.openhab.binding.innogysmarthome/${project.version} diff --git a/bundles/org.openhab.binding.ipp/pom.xml b/bundles/org.openhab.binding.ipp/pom.xml index af9380935729e..e2eb11d1f912f 100644 --- a/bundles/org.openhab.binding.ipp/pom.xml +++ b/bundles/org.openhab.binding.ipp/pom.xml @@ -15,8 +15,8 @@ - org.cups4j - cups4j + org.openhab.osgiify + org.cups4j.cups4j 0.7.6 provided diff --git a/bundles/org.openhab.binding.ipp/src/main/feature/feature.xml b/bundles/org.openhab.binding.ipp/src/main/feature/feature.xml index 5fa38a8fed4fe..f61734a2a0a26 100644 --- a/bundles/org.openhab.binding.ipp/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.ipp/src/main/feature/feature.xml @@ -5,8 +5,7 @@ openhab-runtime-base openhab-transport-mdns - wrap - wrap:mvn:org.cups4j/cups4j/0.7.6$Bundle-Name=Cups4J&Bundle-SymbolicName=org.cups4j.cups4j&Bundle-Version=0.7.6 + mvn:org.openhab.osgiify/org.cups4j.cups4j/0.7.6 mvn:org.apache.httpcomponents/httpcore-osgi/4.4.4 mvn:org.apache.httpcomponents/httpclient-osgi/4.5.2 mvn:org.openhab.addons.bundles/org.openhab.binding.ipp/${project.version} 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 b94a0c42b39b6..cbd393731f4f4 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,7 +16,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; -import java.io.Reader; import java.util.TooManyListenersException; import org.slf4j.Logger; @@ -79,26 +78,25 @@ public synchronized void openConnection() { serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); - 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()); + 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()); } + } 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/README.md b/bundles/org.openhab.binding.keba/README.md index 9d91b67597578..84c623b2d0f9d 100644 --- a/bundles/org.openhab.binding.keba/README.md +++ b/bundles/org.openhab.binding.keba/README.md @@ -42,6 +42,7 @@ All devices support the following channels: | sessionrfidtag | String | yes | RFID tag used for the last charging session | | sessionrfidclass | String | yes | RFID tag class used for the last charging session | | sessionid | Number | yes | session ID of the last charging session | +| setenergylimit | Number | no | set an energy limit for an already running or the next charging session| ## Example @@ -76,6 +77,7 @@ Number KebaSessionEnergy {channel="keba:kecontact:1:sessionconsumption"} Number KebaTotalEnergy {channel="keba:kecontact:1:totalconsumption"} Switch KebaInputSwitch {channel="keba:kecontact:1:input"} Switch KebaOutputSwitch {channel="keba:kecontact:1:output"} +Number KebaSetEnergyLimit {channel="keba:kecontact:1:setenergylimit"} ``` demo.sitemap: @@ -96,6 +98,7 @@ sitemap demo label="Main Menu" Text item=KebaFailSafeCurrent label="Failsafe supply current [%.0f mA]" Text item=KebaSessionEnergy label="Energy during current session [%.0f Wh]" Text item=KebaTotalEnergy label="Energy during all sessions [%.0f Wh]" + Switch item=KebaSetEnergyLimit label="Set charge energy limit" mappings=[0="off", 20000="20kWh"] } } ``` 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 68d663bb77310..c10fe983e9a43 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 @@ -69,6 +69,7 @@ public class KebaBindingConstants { public static final String CHANNEL_SESSION_RFID_TAG = "sessionrfidtag"; public static final String CHANNEL_SESSION_RFID_CLASS = "sessionrfidclass"; public static final String CHANNEL_SESSION_SESSION_ID = "sessionid"; + public static final String CHANNEL_SETENERGY = "setenergylimit"; 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 bcf684c616042..123db4d738dde 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 @@ -467,6 +467,12 @@ protected void onData(ByteBuffer byteBuffer) { updateState(CHANNEL_SESSION_SESSION_ID, newState); break; } + case "Setenergy": { + int state = entry.getValue().getAsInt() / 10; + State newState = new DecimalType(state); + updateState(CHANNEL_SETENERGY, newState); + break; + } } } } catch (JsonParseException e) { @@ -548,6 +554,14 @@ public void handleCommand(ChannelUID channelUID, Command command) { } break; } + case CHANNEL_SETENERGY: { + if (command instanceof DecimalType) { + transceiver.send( + "setenergy " + String.valueOf( + Math.min(Math.max(0, ((DecimalType) command).intValue()*10), 999999999)), this); + } + break; + } } } } 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 dea303d475a7e..2d8c5f3919f5f 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 @@ -53,6 +53,7 @@ + @@ -245,4 +246,10 @@ Session ID of the last charging session + + Number + + An energy limit for an already running or the next charging session. + + diff --git a/bundles/org.openhab.binding.knx/README.md b/bundles/org.openhab.binding.knx/README.md index 1f963dd5fa890..fd2ec6128d57a 100644 --- a/bundles/org.openhab.binding.knx/README.md +++ b/bundles/org.openhab.binding.knx/README.md @@ -183,22 +183,50 @@ Each configuration parameter has a `mainGA` where commands are written to and op The `dpt` element is optional. If ommitted, the corresponding default value will be used (see the channel descriptions above). -## Examples - -### Full Example +## Examples +The following two templates are sufficient for almost all purposes. Only add parameters to the Bridge and Thing configuration if you know exactly what functionality it is needed for. +### Type ROUTER mode configuration Template +knx.things: +```xtend +Bridge knx:ip:bridge [ + type="ROUTER", + autoReconnectPeriod=60 //optional, do not set <30 sec. +] { + Thing device knx_device "knx_device_name" @ "knx_device_group_in_paperui" [ + //readInterval=3600 //optional, only used if reading values are present + ] { + //Items configurations + } +} +``` +### Type TUNNEL mode configuration Template knx.things: - ```xtend Bridge knx:ip:bridge [ + type="TUNNEL", + ipAddress="192.168.0.111", + autoReconnectPeriod=60 //optional, do not set <30 sec. +] { + Thing device knx_device "knx_device_name" @ "knx_device_group_in_paperui" [ + //readInterval=3600 //optional, only used if reading values are present + ] { + //Items configurations + } +} +``` +### Full Example +```xtend +//TUNNEL +Bridge knx:ip:bridge [ + type="TUNNEL", ipAddress="192.168.0.10", portNumber=3671, - localIp="192.168.0.11", - type="TUNNEL", + localIp="192.168.0.11", readingPause=50, responseTimeout=10, readRetriesLimit=3, - autoReconnectPeriod=1, + autoReconnectPeriod=60, localSourceAddr="0.0.0" ] { Thing device generic [ @@ -216,6 +244,19 @@ Bridge knx:ip:bridge [ Type datetime : demoDatetime "Alarm" [ ga="5/5/42" ] } } + +//ROUTER +Bridge knx:ip:bridge [ + type="ROUTER", + ipAddress="224.0.23.12", + portNumber=3671, + localIp="192.168.0.11", + readingPause=50, + responseTimeout=10, + readRetriesLimit=3, + autoReconnectPeriod=60, + localSourceAddr="0.0.0" +] {} ``` knx.items: @@ -258,7 +299,7 @@ Bridge knx:serial:bridge [ readingPause=50, responseTimeout=10, readRetriesLimit=3, - autoReconnectPeriod=1 + autoReconnectPeriod=60 ] { Thing device generic { Type switch-control : controlSwitch "Control Switch" [ ga="3/3/10+<3/3/11" ] // '<' signs are allowed but will be ignored for control Channels diff --git a/bundles/org.openhab.binding.knx/pom.xml b/bundles/org.openhab.binding.knx/pom.xml index 87f34a22b8446..1c122758d11b9 100644 --- a/bundles/org.openhab.binding.knx/pom.xml +++ b/bundles/org.openhab.binding.knx/pom.xml @@ -15,8 +15,8 @@ - com.github.calimero - calimero-core + org.openhab.osgiify + com.github.calimero.calimero-core 2.4 provided @@ -27,8 +27,8 @@ - com.github.calimero - calimero-device + org.openhab.osgiify + com.github.calimero.calimero-device 2.4 provided @@ -39,8 +39,8 @@ - com.github.calimero - calimero-rxtx + org.openhab.osgiify + com.github.calimero.calimero-rxtx 2.4 provided diff --git a/bundles/org.openhab.binding.knx/src/main/feature/feature.xml b/bundles/org.openhab.binding.knx/src/main/feature/feature.xml index e790a77310d4e..a38f9be53b9e0 100644 --- a/bundles/org.openhab.binding.knx/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.knx/src/main/feature/feature.xml @@ -5,10 +5,9 @@ openhab-runtime-base openhab-transport-serial - wrap - wrap:mvn:com.github.calimero/calimero-core/2.4$Bundle-Name=Calimero%20Core&Bundle-SymbolicName=com.github.calimero.calimero-core&Bundle-Version=2.4 - wrap:mvn:com.github.calimero/calimero-device/2.4$Bundle-Name=Calimero%20KNX%20Device&Bundle-SymbolicName=com.github.calimero.calimero-device&Bundle-Version=2.4 - wrap:mvn:com.github.calimero/calimero-rxtx/2.4$Bundle-Name=Calimero%20rxtx%20Adapter&Bundle-SymbolicName=com.github.calimero.calimero-rxtx&Bundle-Version=2.4 + mvn:org.openhab.osgiify/com.github.calimero.calimero-core/2.4 + mvn:org.openhab.osgiify/com.github.calimero.calimero-device/2.4 + mvn:org.openhab.osgiify/com.github.calimero.calimero-rxtx/2.4 mvn:org.openhab.addons.bundles/org.openhab.binding.knx/${project.version} diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/AbstractKNXClient.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/AbstractKNXClient.java index ae9e33dd075aa..97e73ed91009c 100644 --- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/AbstractKNXClient.java +++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/AbstractKNXClient.java @@ -142,17 +142,17 @@ public void initialize() { private boolean scheduleReconnectJob() { if (autoReconnectPeriod > 0) { - connectJob = knxScheduler.scheduleWithFixedDelay(() -> connect(), 0, autoReconnectPeriod, TimeUnit.SECONDS); + connectJob = knxScheduler.schedule(this::connect, autoReconnectPeriod, TimeUnit.SECONDS); return true; } else { return false; } } - private void cancelReconnectJob(boolean kill) { + private void cancelReconnectJob() { ScheduledFuture currentReconnectJob = connectJob; if (currentReconnectJob != null) { - currentReconnectJob.cancel(kill); + currentReconnectJob.cancel(true); connectJob = null; } } @@ -200,11 +200,12 @@ private synchronized boolean connect() { TimeUnit.MILLISECONDS); statusUpdateCallback.updateStatus(ThingStatus.ONLINE); - cancelReconnectJob(false); + connectJob = null; return true; } catch (KNXException | InterruptedException e) { logger.debug("Error connecting to the bus: {}", e.getMessage(), e); disconnect(e); + scheduleReconnectJob(); return false; } } @@ -219,6 +220,7 @@ private void disconnect(@Nullable Exception e) { } } + @SuppressWarnings("null") private void releaseConnection() { logger.debug("Bridge {} is disconnecting from the KNX bus", thingUID); readDatapoints.clear(); @@ -277,29 +279,28 @@ private void readNextQueuedDatapoint() { return; } ReadDatapoint datapoint = readDatapoints.poll(); - if (datapoint != null) { - datapoint.incrementRetries(); - try { - logger.trace("Sending a Group Read Request telegram for {}", datapoint.getDatapoint().getMainAddress()); - processCommunicator.read(datapoint.getDatapoint()); - } catch (KNXException e) { - if (datapoint.getRetries() < datapoint.getLimit()) { - readDatapoints.add(datapoint); - logger.debug("Could not read value for datapoint {}: {}. Going to retry.", - datapoint.getDatapoint().getMainAddress(), e.getMessage()); - } else { - logger.warn("Giving up reading datapoint {}, the number of maximum retries ({}) is reached.", - datapoint.getDatapoint().getMainAddress(), datapoint.getLimit()); - } - } catch (InterruptedException e) { - logger.debug("Interrupted sending KNX read request"); - return; + + datapoint.incrementRetries(); + try { + logger.trace("Sending a Group Read Request telegram for {}", datapoint.getDatapoint().getMainAddress()); + processCommunicator.read(datapoint.getDatapoint()); + } catch (KNXException e) { + if (datapoint.getRetries() < datapoint.getLimit()) { + readDatapoints.add(datapoint); + logger.debug("Could not read value for datapoint {}: {}. Going to retry.", + datapoint.getDatapoint().getMainAddress(), e.getMessage()); + } else { + logger.warn("Giving up reading datapoint {}, the number of maximum retries ({}) is reached.", + datapoint.getDatapoint().getMainAddress(), datapoint.getLimit()); } + } catch (InterruptedException e) { + logger.debug("Interrupted sending KNX read request"); + return; } } public void dispose() { - cancelReconnectJob(true); + cancelReconnectJob(); disconnect(null); } diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/config/BridgeConfiguration.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/config/BridgeConfiguration.java index 0605b3d8728da..164fd6abbe5c9 100644 --- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/config/BridgeConfiguration.java +++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/config/BridgeConfiguration.java @@ -23,13 +23,12 @@ * */ public class BridgeConfiguration { - - private BigDecimal autoReconnectPeriod; + private int autoReconnectPeriod; private BigDecimal readingPause; private BigDecimal readRetriesLimit; private BigDecimal responseTimeout; - public BigDecimal getAutoReconnectPeriod() { + public int getAutoReconnectPeriod() { return autoReconnectPeriod; } @@ -45,4 +44,7 @@ public BigDecimal getResponseTimeout() { return responseTimeout; } + public void setAutoReconnectPeriod(int period) { + autoReconnectPeriod = period; + } } diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/IPBridgeThingHandler.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/IPBridgeThingHandler.java index 82a0404a8037f..038ebad9459e0 100644 --- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/IPBridgeThingHandler.java +++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/IPBridgeThingHandler.java @@ -27,6 +27,8 @@ import org.openhab.binding.knx.internal.client.KNXClient; import org.openhab.binding.knx.internal.client.NoOpClient; import org.openhab.binding.knx.internal.config.IPBridgeConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The {@link IPBridgeThingHandler} is responsible for handling commands, which are @@ -39,13 +41,12 @@ */ @NonNullByDefault public class IPBridgeThingHandler extends KNXBridgeBaseThingHandler { - private static final String MODE_ROUTER = "ROUTER"; private static final String MODE_TUNNEL = "TUNNEL"; - @Nullable - private IPClient client; + private final Logger logger = LoggerFactory.getLogger(IPBridgeThingHandler.class); + private @Nullable IPClient client; private final NetworkAddressService networkAddressService; public IPBridgeThingHandler(Bridge bridge, NetworkAddressService networkAddressService) { @@ -56,6 +57,13 @@ public IPBridgeThingHandler(Bridge bridge, NetworkAddressService networkAddressS @Override public void initialize() { IPBridgeConfiguration config = getConfigAs(IPBridgeConfiguration.class); + int autoReconnectPeriod = config.getAutoReconnectPeriod(); + if (autoReconnectPeriod != 0 && autoReconnectPeriod < 30) { + logger.info("autoReconnectPeriod set to {}s, allowed range is 0 (never) or >30", thing.getUID(), + autoReconnectPeriod); + autoReconnectPeriod = 30; + config.setAutoReconnectPeriod(autoReconnectPeriod); + } String localSource = config.getLocalSourceAddr(); String connectionTypeString = config.getType(); int port = config.getPortNumber().intValue(); @@ -91,9 +99,9 @@ public void initialize() { } updateStatus(ThingStatus.UNKNOWN); - client = new IPClient(ipConnectionType, ip, localSource, port, localEndPoint, useNAT, - config.getAutoReconnectPeriod().intValue(), thing.getUID(), config.getResponseTimeout().intValue(), - config.getReadingPause().intValue(), config.getReadRetriesLimit().intValue(), getScheduler(), this); + client = new IPClient(ipConnectionType, ip, localSource, port, localEndPoint, useNAT, autoReconnectPeriod, + thing.getUID(), config.getResponseTimeout().intValue(), config.getReadingPause().intValue(), + config.getReadRetriesLimit().intValue(), getScheduler(), this); client.initialize(); } diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/SerialBridgeThingHandler.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/SerialBridgeThingHandler.java index 7bf87845c9200..a8a8156e90949 100644 --- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/SerialBridgeThingHandler.java +++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/SerialBridgeThingHandler.java @@ -36,7 +36,7 @@ public class SerialBridgeThingHandler extends KNXBridgeBaseThingHandler { public SerialBridgeThingHandler(Bridge bridge) { super(bridge); SerialBridgeConfiguration config = getConfigAs(SerialBridgeConfiguration.class); - client = new SerialClient(config.getAutoReconnectPeriod().intValue(), thing.getUID(), + client = new SerialClient(config.getAutoReconnectPeriod(), thing.getUID(), config.getResponseTimeout().intValue(), config.getReadingPause().intValue(), config.getReadRetriesLimit().intValue(), getScheduler(), config.getSerialPort(), this); } diff --git a/bundles/org.openhab.binding.knx/src/main/resources/ESH-INF/thing/ip.xml b/bundles/org.openhab.binding.knx/src/main/resources/ESH-INF/thing/ip.xml index 9d78057509c40..3a0932b8768fe 100644 --- a/bundles/org.openhab.binding.knx/src/main/resources/ESH-INF/thing/ip.xml +++ b/bundles/org.openhab.binding.knx/src/main/resources/ESH-INF/thing/ip.xml @@ -60,9 +60,9 @@ Limits the read retries while initialization from the KNX bus 3 - + - Seconds between connection retries when KNX link has been lost, 0 means never retry + Seconds between connection retries when KNX link has been lost, 0 means never retry, minimum 30s 60 diff --git a/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/protocol/KodiClientSocket.java b/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/protocol/KodiClientSocket.java index ef063ba0a70b3..0f4e37086779f 100644 --- a/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/protocol/KodiClientSocket.java +++ b/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/protocol/KodiClientSocket.java @@ -21,6 +21,7 @@ import org.eclipse.jetty.websocket.api.Session; 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; @@ -160,6 +161,12 @@ public void onClose(int statusCode, String reason) { }); } } + + @OnWebSocketError + public void onError(Throwable error) { + onClose(0, error.getMessage()); + } + } private void sendMessage(String str) throws IOException { diff --git a/bundles/org.openhab.binding.konnected/src/main/java/org/openhab/binding/konnected/internal/KonnectedHandlerFactory.java b/bundles/org.openhab.binding.konnected/src/main/java/org/openhab/binding/konnected/internal/KonnectedHandlerFactory.java index 4306699d187da..7b4cdc2d7da1c 100644 --- a/bundles/org.openhab.binding.konnected/src/main/java/org/openhab/binding/konnected/internal/KonnectedHandlerFactory.java +++ b/bundles/org.openhab.binding.konnected/src/main/java/org/openhab/binding/konnected/internal/KonnectedHandlerFactory.java @@ -18,6 +18,8 @@ import java.util.Dictionary; import java.util.Set; +import javax.servlet.ServletException; + import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.net.HttpServiceUtil; import org.eclipse.smarthome.core.net.NetworkAddressService; @@ -33,6 +35,7 @@ import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.service.http.HttpService; +import org.osgi.service.http.NamespaceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,6 +49,8 @@ public class KonnectedHandlerFactory extends BaseThingHandlerFactory { private final Logger logger = LoggerFactory.getLogger(KonnectedHandlerFactory.class); private static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_MODULE); + private static final String alias = "/" + BINDING_ID; + private HttpService httpService; private String callbackUrl = null; private NetworkAddressService networkAddressService; @@ -61,25 +66,26 @@ protected void activate(ComponentContext componentContext) { super.activate(componentContext); Dictionary properties = componentContext.getProperties(); callbackUrl = (String) properties.get("callbackUrl"); - KonnectedHTTPServlet servlet = registerWebHookServlet(); - this.servlet = servlet; + try { + this.servlet = registerWebHookServlet(); + } catch (KonnectedWebHookFail e) { + logger.error("Failed registering Konnected servlet - binding is not functional!", e); + } } @Override protected void deactivate(ComponentContext componentContext) { super.deactivate(componentContext); - servlet.deactivate(); + httpService.unregister(alias); } @Override protected @Nullable ThingHandler createHandler(Thing thing) { KonnectedHandler thingHandler = new KonnectedHandler(thing, '/' + BINDING_ID, createCallbackUrl(), createCallbackPort()); - logger.debug("Adding thinghandler for thing {} to webhook.", thing.getUID().getId()); - try { + if (servlet != null) { + logger.debug("Adding thinghandler for thing {} to webhook.", thing.getUID().getId()); servlet.add(thingHandler); - } catch (KonnectedWebHookFail e) { - logger.trace("there was an error adding the thing handler to the servlet: {}", e.getMessage()); } return thingHandler; } @@ -94,11 +100,14 @@ protected void removeHandler(ThingHandler thingHandler) { super.removeHandler(thingHandler); } - private KonnectedHTTPServlet registerWebHookServlet() { - KonnectedHTTPServlet servlet = null; - String configCallBack = '/' + BINDING_ID; - servlet = new KonnectedHTTPServlet(httpService, configCallBack); - return servlet; + private KonnectedHTTPServlet registerWebHookServlet() throws KonnectedWebHookFail { + KonnectedHTTPServlet servlet = new KonnectedHTTPServlet(); + try { + httpService.registerServlet(alias, servlet, null, httpService.createDefaultHttpContext()); + return servlet; + } catch (ServletException | NamespaceException e) { + throw new KonnectedWebHookFail("Could not start Konnected Webhook servlet: " + e.getMessage(), e); + } } @Reference diff --git a/bundles/org.openhab.binding.konnected/src/main/java/org/openhab/binding/konnected/internal/servlet/KonnectedHTTPServlet.java b/bundles/org.openhab.binding.konnected/src/main/java/org/openhab/binding/konnected/internal/servlet/KonnectedHTTPServlet.java index a183d97c17b90..966cfc52a2cb3 100644 --- a/bundles/org.openhab.binding.konnected/src/main/java/org/openhab/binding/konnected/internal/servlet/KonnectedHTTPServlet.java +++ b/bundles/org.openhab.binding.konnected/src/main/java/org/openhab/binding/konnected/internal/servlet/KonnectedHTTPServlet.java @@ -16,15 +16,12 @@ import java.util.HashMap; import java.util.Scanner; -import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.openhab.binding.konnected.internal.gson.KonnectedModuleGson; import org.openhab.binding.konnected.internal.handler.KonnectedHandler; -import org.osgi.service.http.HttpService; -import org.osgi.service.http.NamespaceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,57 +41,20 @@ public class KonnectedHTTPServlet extends HttpServlet { private static final String CHARSET = "utf-8"; private final Gson gson = new Gson(); - private final HttpService httpService; - - private final String path; private HashMap konnectedThingHandlers = new HashMap<>(); - public KonnectedHTTPServlet(HttpService httpService, String id) { - this.httpService = httpService; - this.path = id; + public KonnectedHTTPServlet() { } - public void add(KonnectedHandler thingHandler) throws KonnectedWebHookFail { + public void add(KonnectedHandler thingHandler) { logger.trace("Adding KonnectedHandler[{}] to KonnectedHTTPServlet.", thingHandler.getThing().getUID()); - - if (konnectedThingHandlers.size() == 0) { - this.activate(); - } - konnectedThingHandlers.put(thingHandler.getThing().getUID().getAsString(), thingHandler); } public void remove(KonnectedHandler thingHandler) { - logger.trace("Removing KonnectedHandler [{}] from KonnectedHTTP Servlet. ", thingHandler.getThing().getUID()); + logger.trace("Removing KonnectedHandler [{}] from KonnectedHTTPServlet. ", thingHandler.getThing().getUID()); konnectedThingHandlers.remove(thingHandler.getThing().getUID().getAsString()); - - if (konnectedThingHandlers.size() == 0) { - this.deactivate(); - } - } - - /** - * Activation callback. - * - * @param config Service config. - **/ - public void activate() throws KonnectedWebHookFail { - try { - logger.debug("Trying to Start Webhook at {}.", path); - httpService.registerServlet(path, this, null, httpService.createDefaultHttpContext()); - logger.debug("Started Konnected Webhook servlet at {}", path); - } catch (ServletException | NamespaceException e) { - throw new KonnectedWebHookFail("Could not start Konnected Webhook servlet: " + e.getMessage(), e); - } - } - - /** - * Webhook Deactivation callback. - */ - public void deactivate() { - httpService.unregister(path); - logger.debug("Konnected webhook servlet stopped"); } @Override @@ -140,7 +100,4 @@ private void setHeaders(HttpServletResponse response) { response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); } - public String getPath() { - return path; - } } diff --git a/bundles/org.openhab.binding.lgwebos/README.md b/bundles/org.openhab.binding.lgwebos/README.md index e7e1819371d2f..74a52303ce2fe 100644 --- a/bundles/org.openhab.binding.lgwebos/README.md +++ b/bundles/org.openhab.binding.lgwebos/README.md @@ -47,7 +47,7 @@ Please note that at least one channel must be bound to an item before the bindin | power | Switch | Current power setting. TV can only be powered off, not on. | RW | | mute | Switch | Current mute setting. | RW | | volume | Dimmer | Current volume setting. Setting and reporting absolute percent values only works when using internal speakers. When connected to an external amp, the volume should be controlled using increase and decrease commands. | RW | -| channel | Number | Current channel number. | RW | +| channel | String | Current channel number. | RW | | channelName | String | Current channel name. | R | | toast | String | Displays a short message on the TV screen. See also rules section. | W | | mediaPlayer | Player | Media control player | W | @@ -76,9 +76,9 @@ Switch LG_TV0_Power "TV Power" { autoupdate="false", channel="lgwe Switch LG_TV0_Mute "TV Mute" { channel="lgwebos:WebOSTV:3aab9eea-953b-4272-bdbd-f0cd0ecf4a46:mute"} Dimmer LG_TV0_Volume "Volume [%S]" { channel="lgwebos:WebOSTV:3aab9eea-953b-4272-bdbd-f0cd0ecf4a46:volume" } Number LG_TV0_VolDummy "VolumeUpDown" -Number LG_TV0_ChannelNo "Channel [%d]" { channel="lgwebos:WebOSTV:3aab9eea-953b-4272-bdbd-f0cd0ecf4a46:channel" } +String LG_TV0_Channel "Channel [%d]" { channel="lgwebos:WebOSTV:3aab9eea-953b-4272-bdbd-f0cd0ecf4a46:channel" } Number LG_TV0_ChannelDummy "ChannelUpDown" -String LG_TV0_Channel "Channel [%S]" { channel="lgwebos:WebOSTV:3aab9eea-953b-4272-bdbd-f0cd0ecf4a46:channelName"} +String LG_TV0_ChannelName "Channel [%S]" { channel="lgwebos:WebOSTV:3aab9eea-953b-4272-bdbd-f0cd0ecf4a46:channelName"} String LG_TV0_Toast { channel="lgwebos:WebOSTV:3aab9eea-953b-4272-bdbd-f0cd0ecf4a46:toast"} Switch LG_TV0_Stop "Stop" { autoupdate="false", channel="lgwebos:WebOSTV:3aab9eea-953b-4272-bdbd-f0cd0ecf4a46:mediaStop" } String LG_TV0_Application "Application [%s]" { channel="lgwebos:WebOSTV:3aab9eea-953b-4272-bdbd-f0cd0ecf4a46:appLauncher"} @@ -98,9 +98,9 @@ sitemap demo label="Main Menu" Switch item=LG_TV0_Mute Text item=LG_TV0_Volume Switch item=LG_TV0_VolDummy icon="soundvolume" label="Volume" mappings=[1="▲", 0="▼"] - Text item=LG_TV0_ChannelNo - Switch item=LG_TV0_ChannelDummy icon="television" label="Channel" mappings=[1="▲", 0="▼"] Text item=LG_TV0_Channel + Switch item=LG_TV0_ChannelDummy icon="television" label="Channel" mappings=[1="▲", 0="▼"] + Text item=LG_TV0_ChannelName Default item=LG_TV0_Player Text item=LG_TV0_Application Selection item=LG_TV0_Application mappings=[ @@ -147,10 +147,15 @@ end rule "ChannelUpDown" when Item LG_TV0_ChannelDummy received command then - var currentChannel = LG_TV0_ChannelNo.state as DecimalType + val actions = getActions("lgwebos","lgwebos:WebOSTV:3aab9eea-953b-4272-bdbd-f0cd0ecf4a46") + if(null === actions) { + logInfo("actions", "Actions not found, check thing ID") + return + } + switch receivedCommand{ - case 0: LG_TV0_ChannelNo.sendCommand(currentChannel - 1) - case 1: LG_TV0_ChannelNo.sendCommand(currentChannel + 1) + case 0: actions.decreaseChannel() + case 1: actions.increaseChannel() } end ``` diff --git a/bundles/org.openhab.binding.lgwebos/pom.xml b/bundles/org.openhab.binding.lgwebos/pom.xml index 59e6a34519d6a..0611a0341a039 100644 --- a/bundles/org.openhab.binding.lgwebos/pom.xml +++ b/bundles/org.openhab.binding.lgwebos/pom.xml @@ -27,8 +27,8 @@ provided - org.java-websocket - java-websocket + org.openhab.osgiify + org.java-websocket.java-websocket 1.3.2 provided diff --git a/bundles/org.openhab.binding.lgwebos/src/main/feature/feature.xml b/bundles/org.openhab.binding.lgwebos/src/main/feature/feature.xml index c4a5bf5bc85f6..b38e6a708ae15 100644 --- a/bundles/org.openhab.binding.lgwebos/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.lgwebos/src/main/feature/feature.xml @@ -4,12 +4,11 @@ openhab-runtime-base - wrap mvn:org.apache.httpcomponents/httpcore-osgi/4.4.4 mvn:org.apache.httpcomponents/httpclient-osgi/4.5.2 mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.xpp3/1.1.4c_7 mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.json/20140107_1 - wrap:mvn:org.java-websocket/java-websocket/1.3.2$Bundle-Name=Java%20Websocket%20Client&Bundle-SymbolicName=org.java-websocket.java-websocket&Bundle-Version=1.3.2 + mvn:org.openhab.osgiify/org.java-websocket.java-websocket/1.3.2 mvn:org.openhab.addons.bundles/org.openhab.binding.lgwebos/${project.version} diff --git a/bundles/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 index a106f3cef2b23..2972702ba3837 100644 --- a/bundles/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 @@ -20,7 +20,7 @@ import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.library.types.StringType; import org.eclipse.smarthome.core.types.Command; import org.openhab.binding.lgwebos.internal.handler.LGWebOSHandler; import org.slf4j.Logger; @@ -121,7 +121,7 @@ public void onSuccess(@Nullable ChannelInfo channelInfo) { if (channelInfo == null) { return; } - handler.postUpdate(channelId, new DecimalType(channelInfo.getNumber())); + handler.postUpdate(channelId, new StringType(channelInfo.getNumber())); } })); } else { diff --git a/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/thing/thing-types.xml index 82a51ca67a5d5..2aa85d71c9e63 100644 --- a/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.lgwebos/src/main/resources/ESH-INF/thing/thing-types.xml @@ -44,10 +44,9 @@ - Number + String Current Channel - String diff --git a/bundles/org.openhab.binding.mcp23017/pom.xml b/bundles/org.openhab.binding.mcp23017/pom.xml index d5e92972a6605..16e031aa3ece0 100644 --- a/bundles/org.openhab.binding.mcp23017/pom.xml +++ b/bundles/org.openhab.binding.mcp23017/pom.xml @@ -21,8 +21,8 @@ provided - com.pi4j - pi4j-gpio-extension + org.openhab.osgiify + com.pi4j.pi4j-gpio-extension 1.2 provided diff --git a/bundles/org.openhab.binding.mcp23017/src/main/feature/feature.xml b/bundles/org.openhab.binding.mcp23017/src/main/feature/feature.xml index f643940b10590..f4c2ad6f8a390 100644 --- a/bundles/org.openhab.binding.mcp23017/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.mcp23017/src/main/feature/feature.xml @@ -4,9 +4,8 @@ openhab-runtime-base - wrap mvn:com.pi4j/pi4j-core/1.2 - wrap:mvn:com.pi4j/pi4j-gpio-extension/1.2$Bundle-Name=Pi4J%20::%20Java%20Library%20(Extension)&Bundle-SymbolicName=com.pi4j.pi4j-gpio-extension&Bundle-Version=1.2 + mvn:org.openhab.osgiify/com.pi4j.pi4j-gpio-extension/1.2 mvn:org.openhab.addons.bundles/org.openhab.binding.mcp23017/${project.version} diff --git a/bundles/org.openhab.binding.milight/README.md b/bundles/org.openhab.binding.milight/README.md index 953bd3d201262..284ee01333d89 100644 --- a/bundles/org.openhab.binding.milight/README.md +++ b/bundles/org.openhab.binding.milight/README.md @@ -19,7 +19,7 @@ RGB/White from 2014 and the new generation RGB/White from 2016 as well as RGB/Co | RGB/Cold,Warmwhite | | ✓ | ✓ | Please note that LD382, LD382A, LD686 RGB stripes and bulbs are supported by the -[WifiLed Binding](https://github.com/openhab/openhab2-addons/blob/master/addons/binding/org.openhab.binding.wifiled/README.md). +[WifiLed Binding](https://www.openhab.org/addons/bindings/wifiled/). ## Discovery diff --git a/bundles/org.openhab.binding.minecraft/pom.xml b/bundles/org.openhab.binding.minecraft/pom.xml index cce7fe4d9cdd3..4f422e5cc82ac 100644 --- a/bundles/org.openhab.binding.minecraft/pom.xml +++ b/bundles/org.openhab.binding.minecraft/pom.xml @@ -21,8 +21,8 @@ provided - com.firebase - tubesock + org.openhab.osgiify + com.firebase.tubesock 0.0.1 provided diff --git a/bundles/org.openhab.binding.minecraft/src/main/feature/feature.xml b/bundles/org.openhab.binding.minecraft/src/main/feature/feature.xml index 111ab213f2320..2349095af24ad 100644 --- a/bundles/org.openhab.binding.minecraft/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.minecraft/src/main/feature/feature.xml @@ -5,9 +5,8 @@ openhab-runtime-base openhab-transport-mdns - wrap mvn:io.reactivex/rxjava/1.1.7 - wrap:mvn:com.firebase/tubesock/0.0.1$Bundle-Name=TubeSock&Bundle-SymbolicName=com.firebase.tubesock&Bundle-Version=0.0.1 + mvn:org.openhab.osgiify/com.firebase.tubesock/0.0.1 mvn:org.openhab.addons.bundles/org.openhab.binding.minecraft/${project.version} diff --git a/bundles/org.openhab.binding.netatmo/pom.xml b/bundles/org.openhab.binding.netatmo/pom.xml index 88a5e4d90e147..956261f9ff3a8 100644 --- a/bundles/org.openhab.binding.netatmo/pom.xml +++ b/bundles/org.openhab.binding.netatmo/pom.xml @@ -15,26 +15,26 @@ - org.json - json + org.openhab.osgiify + org.json.json 20131018 provided - com.squareup.okhttp - okhttp + org.openhab.osgiify + com.squareup.okhttp.okhttp 2.3.0 provided - com.squareup.okio - okio + org.openhab.osgiify + com.squareup.okio.okio-1.3.0 1.3.0 provided - com.squareup.retrofit - retrofit + org.openhab.osgiify + com.squareup.retrofit.retrofit 1.9.0 provided diff --git a/bundles/org.openhab.binding.netatmo/src/main/feature/feature.xml b/bundles/org.openhab.binding.netatmo/src/main/feature/feature.xml index 515469ba1b68b..e13aba74cc1a2 100644 --- a/bundles/org.openhab.binding.netatmo/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.netatmo/src/main/feature/feature.xml @@ -4,11 +4,10 @@ openhab-runtime-base - wrap - wrap:mvn:org.json/json/20131018$Bundle-Name=JSON%20in%20Java&Bundle-SymbolicName=org.json.json&Bundle-Version=20131018 - wrap:mvn:com.squareup.okhttp/okhttp/2.3.0$Bundle-Name=OkHttp&Bundle-SymbolicName=com.squareup.okhttp&Bundle-Version=2.3.0 - wrap:mvn:com.squareup.okio/okio/1.3.0$Bundle-Name=Okio&Bundle-SymbolicName=com.squareup.okio&Bundle-Version=1.3.0 - wrap:mvn:com.squareup.retrofit/retrofit/1.9.0$Bundle-Name=Retrofit&Bundle-SymbolicName=com.squareup.retrofit&Bundle-Version=1.9.0 + mvn:org.openhab.osgiify/org.json.json/20131018 + mvn:org.openhab.osgiify/com.squareup.okhttp.okhttp/2.3.0 + mvn:org.openhab.osgiify/com.squareup.okio.okio-1.3.0/1.3.0 + mvn:org.openhab.osgiify/com.squareup.retrofit.retrofit/1.9.0 mvn:org.apache.oltu.oauth2/org.apache.oltu.oauth2.client/1.0.0 mvn:org.apache.oltu.oauth2/org.apache.oltu.oauth2.common/1.0.0 mvn:org.openhab.addons.bundles/org.openhab.binding.netatmo/${project.version} diff --git a/bundles/org.openhab.binding.network/pom.xml b/bundles/org.openhab.binding.network/pom.xml index e64e7ce53955c..464429cd866d9 100644 --- a/bundles/org.openhab.binding.network/pom.xml +++ b/bundles/org.openhab.binding.network/pom.xml @@ -15,14 +15,14 @@ - fr.bmartel - jspeedtest + org.openhab.osgiify + fr.bmartel.jspeedtest 1.32.1 provided - fr.bmartel - http-endec + org.openhab.osgiify + fr.bmartel.http-endec 1.04 provided diff --git a/bundles/org.openhab.binding.network/src/main/feature/feature.xml b/bundles/org.openhab.binding.network/src/main/feature/feature.xml index a54db3044e819..9e59c06a301f0 100644 --- a/bundles/org.openhab.binding.network/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.network/src/main/feature/feature.xml @@ -5,10 +5,9 @@ openhab-runtime-base openhab-core-model-script - wrap - mvn:commons-net/commons-net/3.6 - wrap:mvn:fr.bmartel/jspeedtest/1.32.1$Bundle-Name=Speed%20Test%20library%20for%20Java&Bundle-SymbolicName=fr.bmartel.jspeedtest&Bundle-Version=1.32.1 - wrap:mvn:fr.bmartel/http-endec/1.04$Bundle-Name=http-endec&Bundle-SymbolicName=fr.bmartel.http-endec&Bundle-Version=1.04 + mvn:commons-net/commons-net/3.6 + mvn:org.openhab.osgiify/fr.bmartel.jspeedtest/1.32.1 + mvn:org.openhab.osgiify/fr.bmartel.http-endec/1.04 mvn:org.openhab.addons.bundles/org.openhab.binding.network/${project.version} diff --git a/bundles/org.openhab.binding.network/src/test/java/org/openhab/binding/network/internal/dhcp/DHCPTest.java b/bundles/org.openhab.binding.network/src/test/java/org/openhab/binding/network/internal/dhcp/DHCPTest.java index 64ae230462a34..19b4d802e61fb 100644 --- a/bundles/org.openhab.binding.network/src/test/java/org/openhab/binding/network/internal/dhcp/DHCPTest.java +++ b/bundles/org.openhab.binding.network/src/test/java/org/openhab/binding/network/internal/dhcp/DHCPTest.java @@ -14,6 +14,7 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeTrue; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; @@ -34,7 +35,9 @@ public class DHCPTest { public void testService() throws SocketException { String testIP = "10.1.2.3"; IPRequestReceivedCallback dhcpListener = mock(IPRequestReceivedCallback.class); - assertThat(DHCPListenService.instance, is(nullValue())); + + // if this is not the case this test is not very useful, we don't always have the static field under control. + assumeTrue(DHCPListenService.instance == null); DHCPListenService.register(testIP, dhcpListener); assertThat(DHCPListenService.instance, is(notNullValue())); DHCPListenService.unregister(testIP); diff --git a/bundles/org.openhab.binding.onkyo/README.md b/bundles/org.openhab.binding.onkyo/README.md index d085a1fc4712d..e9d409d85b4cb 100644 --- a/bundles/org.openhab.binding.onkyo/README.md +++ b/bundles/org.openhab.binding.onkyo/README.md @@ -42,6 +42,7 @@ At the moment only the following models are supported: - TX-NR818 - TX-NR828 - TX-NR838 +- TX-NR3007 ## Binding Configuration diff --git a/bundles/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 index 36c6d285f172a..d59376366931e 100644 --- a/bundles/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 @@ -59,6 +59,7 @@ public class OnkyoBindingConstants { public static final String ONKYO_TYPE_TXNR818 = "TX-NR818"; public static final String ONKYO_TYPE_TXNR828 = "TX-NR828"; public static final String ONKYO_TYPE_TXNR838 = "TX-NR838"; + public static final String ONKYO_TYPE_TXNR3007 = "TX-NR3007"; // Extend this set with all successfully tested models public static final Set SUPPORTED_DEVICE_MODELS = Stream @@ -66,7 +67,8 @@ public class OnkyoBindingConstants { ONKYO_TYPE_TXNR535, ONKYO_TYPE_TXNR555, ONKYO_TYPE_TXNR575, ONKYO_TYPE_TXNR575E, ONKYO_TYPE_TXNR616, ONKYO_TYPE_TXNR626, ONKYO_TYPE_TXNR636, ONKYO_TYPE_TXNR646, ONKYO_TYPE_TXNR656, ONKYO_TYPE_TXNR676, ONKYO_TYPE_TXNR686, ONKYO_TYPE_TXNR708, ONKYO_TYPE_TXNR717, ONKYO_TYPE_TXNR727, ONKYO_TYPE_TXNR737, - ONKYO_TYPE_TXNR747, ONKYO_TYPE_TXNR757, ONKYO_TYPE_TXNR818, ONKYO_TYPE_TXNR828, ONKYO_TYPE_TXNR838) + ONKYO_TYPE_TXNR747, ONKYO_TYPE_TXNR757, ONKYO_TYPE_TXNR818, ONKYO_TYPE_TXNR828, ONKYO_TYPE_TXNR838, + ONKYO_TYPE_TXNR3007) .collect(Collectors.toSet()); // List of all Thing Type UIDs @@ -99,6 +101,7 @@ public class OnkyoBindingConstants { public static final ThingTypeUID THING_TYPE_TXNR818 = new ThingTypeUID(BINDING_ID, ONKYO_TYPE_TXNR818); public static final ThingTypeUID THING_TYPE_TXNR828 = new ThingTypeUID(BINDING_ID, ONKYO_TYPE_TXNR828); public static final ThingTypeUID THING_TYPE_TXNR838 = new ThingTypeUID(BINDING_ID, ONKYO_TYPE_TXNR838); + public static final ThingTypeUID THING_TYPE_TXNR3007 = new ThingTypeUID(BINDING_ID, ONKYO_TYPE_TXNR3007); public static final Set SUPPORTED_THING_TYPES_UIDS = Stream .of(THING_TYPE_ONKYOAV, THING_TYPE_ONKYO_UNSUPPORTED, THING_TYPE_HTRC560, THING_TYPE_TXNR414, THING_TYPE_TXNR474, @@ -106,7 +109,7 @@ public class OnkyoBindingConstants { THING_TYPE_TXNR575E, THING_TYPE_TXNR616, THING_TYPE_TXNR626, THING_TYPE_TXNR636, THING_TYPE_TXNR646, THING_TYPE_TXNR656, THING_TYPE_TXNR676, THING_TYPE_TXNR686, THING_TYPE_TXNR708, THING_TYPE_TXNR717, THING_TYPE_TXNR727, THING_TYPE_TXNR737, THING_TYPE_TXNR747, THING_TYPE_TXNR757, THING_TYPE_TXNR818, - THING_TYPE_TXNR828, THING_TYPE_TXNR838) + THING_TYPE_TXNR828, THING_TYPE_TXNR838, THING_TYPE_TXNR3007) .collect(Collectors.toSet()); // List of thing parameters names diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/ht-rc560.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/ht-rc560.xml index 3fe73bb4b94d9..fb74846def4ca 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/ht-rc560.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/ht-rc560.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/onkyoAVR.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/onkyoAVR.xml index 9ba8731f82f9c..711136acfba98 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/onkyoAVR.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/onkyoAVR.xml @@ -6,7 +6,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr3007.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr3007.xml new file mode 100644 index 0000000000000..624190a219219 --- /dev/null +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr3007.xml @@ -0,0 +1,21 @@ + + + + + + Network enabled Onkyo AV Receiver + + + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr414.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr414.xml index ab665c0664396..480fc1a2fc210 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr414.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr414.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr474.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr474.xml index 2bd79cb618b8b..cb1f5d2b57d6c 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr474.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr474.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr509.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr509.xml index 0bab81f5fcdf5..b573c4ed36f4e 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr509.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr509.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr515.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr515.xml index f0bbe10f9d324..5e42e4ea03fe4 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr515.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr515.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr525.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr525.xml index e58f788156359..1902e8b787642 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr525.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr525.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr535.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr535.xml index eabd6fd0d7602..1894b6d3cb0f4 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr535.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr535.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr545.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr545.xml index de968774158c8..271dad2781909 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr545.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr545.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr555.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr555.xml index cba6625e9ea35..8071b87451b5d 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr555.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr555.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr575.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr575.xml index 12d44b95248a2..f17ca0ff264a3 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr575.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr575.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr575e.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr575e.xml index 19e1193032ede..c24b3ef17964e 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr575e.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr575e.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr616.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr616.xml index 5fb6d183f95e8..4ddca4779a923 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr616.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr616.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr626.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr626.xml index 85cb05989a60a..254551da3fa42 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr626.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr626.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr636.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr636.xml index 494d937009727..af65f7a1ca0a7 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr636.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr636.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr646.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr646.xml index 95290d658ed74..aacc14266186b 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr646.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr646.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr656.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr656.xml index 82b83089b0ea6..9d05b5dad9ffa 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr656.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr656.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr676.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr676.xml index 06ac5e4674a45..9f36ee2c5784e 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr676.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr676.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr686.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr686.xml index 3c5060a8364ad..25208ddb0e74f 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr686.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr686.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr708.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr708.xml index a7c55d6841852..11a4303a63ad5 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr708.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr708.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr717.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr717.xml index 2ba2cf9953113..89da3644832b6 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr717.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr717.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr727.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr727.xml index 7966d64b9d921..e51c3b6603a86 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr727.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr727.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr737.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr737.xml index 2cb73dd602abe..edbf382442e3e 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr737.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr737.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr747.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr747.xml index 9bc44b17d958c..7561a9427ff34 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr747.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr747.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr818.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr818.xml index bbf544a80087f..dcb1e889fe000 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr818.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr818.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr828.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr828.xml index 3fb2bf53df5f6..ef5323e048ab3 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr828.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr828.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr838.xml b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr838.xml index 438d2d82cbeda..77a56cbc1ec5e 100644 --- a/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr838.xml +++ b/bundles/org.openhab.binding.onkyo/src/main/resources/ESH-INF/thing/tx-nr838.xml @@ -5,7 +5,7 @@ - Network enabled Onkyo AV Receivers + Network enabled Onkyo AV Receiver diff --git a/bundles/org.openhab.binding.pushbullet/.classpath b/bundles/org.openhab.binding.pushbullet/.classpath new file mode 100644 index 0000000000000..a5d95095ccaaf --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bundles/.project b/bundles/org.openhab.binding.pushbullet/.project similarity index 62% rename from bundles/.project rename to bundles/org.openhab.binding.pushbullet/.project index b4bb9f9000def..89f11546d5550 100644 --- a/bundles/.project +++ b/bundles/org.openhab.binding.pushbullet/.project @@ -1,10 +1,15 @@ - org.openhab.addons.reactor.bundles + org.openhab.binding.pushbullet + + org.eclipse.jdt.core.javabuilder + + + org.eclipse.m2e.core.maven2Builder @@ -12,6 +17,7 @@ + org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature diff --git a/bundles/org.openhab.binding.pushbullet/NOTICE b/bundles/org.openhab.binding.pushbullet/NOTICE new file mode 100644 index 0000000000000..4c20ef446c1e4 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/NOTICE @@ -0,0 +1,13 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab2-addons diff --git a/bundles/org.openhab.binding.pushbullet/README.md b/bundles/org.openhab.binding.pushbullet/README.md new file mode 100644 index 0000000000000..57799d9593021 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/README.md @@ -0,0 +1,158 @@ +# Pushbullet Binding + +The Pushbullet binding allows you to notify iOS, Android & Windows 10 Phone & Desktop devices of a message using the Pushbullet API web service. + +## Supported Things + +This binding supports a generic "bot" which is a representation of the client. + +## Discovery + +This binding provides no discovery. +The desired bots must be configured manually or via a things file. + +## Binding Configuration + +The binding has no configuration options itself, all configuration is done at 'Things' level. + +## Thing Configuration + +### Bot (`bot`) + +The bot thing is used to send messages to other recipients. +It has the following parameters: + +| Config | Description | Required | Advanced | +|------------|------------------------------------------------------------------|----------|----------| +| token | Pushbullet [API token](#obtaining-an-api-key) to send to devices | Yes | False | +| name | Explicit Name, for later use when the bot can receive messages | No | True | +| apiUrlBase | Address of own Pushbullet server, for testing purposes | No | True | + +```java +Thing pushbullet:bot:r2d2 "R2D2" @ "Somewhere" [ token = "verysecretwonttellyou" ] + +``` + +## Channels + +| Channel ID | Channel Description | Supported item type | Advanced | +|------------|-------------------------------------------------|----------------------|----------| +| recipient | for later use when the bot can receive messages | String | False | +| title | for later use when the bot can receive messages | String | False | +| message | for later use when the bot can receive messages | String | False | + +## Rule Action + +This binding includes rule actions for sending notes. +Two different actions available: + +* `sendPushbulletNote(String recipient, String messsage)` +* `sendPushbulletNote(String recipient, String title, String messsage)` + +Since there is a separate rule action instance for each `bot` thing, this needs to be retrieved through `getActions(scope, thingUID)`. +The first parameter always has to be `pushbullet` and the second is the full Thing UID of the bot that should be used. +Once this action instance is retrieved, you can invoke the action method on it. + +Examples: + +``` +val actions = getActions("pushbullet", "pushbullet:bot:r2d2") +val result = actions.sendPushbulletNote("someone@example.com", "R2D2 talks here...", "This is the pushed note.") +``` + +## Full Example + +_Provide a full usage example based on textual configuration files (*.things, *.items, *.sitemap)._ + +pushbullet.things: + +```java +Thing pushbullet:bot:r2d2 "R2D2" @ "Somewhere" [ token = "verysecretwonttellyou" ] + +``` + +pushbullet.items + +```java +Switch Pushbullet_R2D2_Button "Pushbullet Action bot R2D2" +``` + +pushbullet.sitemap + +```java +sitemap pushbullet label="Pushbullet" +{ + Switch item=Pushbullet_R2D2_Button +} +``` + +pushbullet.rules + +```java +rule "Pushbullet R2D2 changed" +when + Item Pushbullet_R2D2_Button changed +then + logInfo(filename, "Button R2D2 changed - OH2...") + + if (Pushbullet_R2D2_Button.state == ON) + { + sendCommand(Pushbullet_R2D2_Button, OFF) + + val actions = getActions("pushbullet", "pushbullet:bot:r2d2") + logInfo(filename, "Actions for 'R2D2' are: " + actions) + + if (actions != null) + { + val result = actions.sendPushbulletNote("someone@example.com", "Title R2D2 OH2", "This has been sent by the new R2D2 bot") + logInfo(filename, "Result of send action is: " + result) + } + } +end +``` + +## Creating an account for your bot(s) + +The pushbullet accounts are bound to either Google or Facebook accounts. + +- Create a bot account with either Facebook or Google +- Go to "" +- Chose to either "Sign up with Google" or "Sign up with Facebook". +- Complete the signup process as guided by the pushbullet web site. +- Continue with "Obtaining an API key". + +## Obtaining an API key + +The API keys are bound to the pushbullet account. + +- Go to the pushbullet site. +- Log in with either your personal account or the one you created for your bot. +- Go to "" +- Click on "Create Access Token". +- Copy the token created on the site. + +You must at least provide an API token (Private or Alias Key from Pushbullet.com) and a message in some manner before a message can be pushed. +All other parameters are optional. +If you use an alias key, the parameters (device, icon, sound, vibration) are overwritten by the alias setting on pushbullet. + +## Rate limits + +As of 2019, free accounts have a limit of 100 pushes per month. +This action does not evaluate the rate limiting headers though. + +## Translation + +This project is being translated on transifex. +If you want to help, please join the project at the URL: + +- https://www.transifex.com/hakan42/openhab-binding-pushbullet/dashboard/ + +## Libraries + +This action has been written without using libraries as jpushbullet or jpushbullet2. +Both of those libraries use various libraries themselves which makes integrating them into openHAB a challenge. + +## pushbullet API + +- +- diff --git a/bundles/org.openhab.binding.pushbullet/pom.xml b/bundles/org.openhab.binding.pushbullet/pom.xml new file mode 100644 index 0000000000000..f7d2623e8c3f2 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/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.pushbullet + + openHAB Add-ons :: Bundles :: Pushbullet Binding + + + + javax.mail + mail + 1.4.7 + provided + + + + diff --git a/bundles/org.openhab.binding.pushbullet/src/main/feature/feature.xml b/bundles/org.openhab.binding.pushbullet/src/main/feature/feature.xml new file mode 100644 index 0000000000000..f0dfc37f14ad8 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${project.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.pushbullet/${project.version} + + diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletBindingConstants.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletBindingConstants.java new file mode 100644 index 0000000000000..d60d350de3423 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletBindingConstants.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.pushbullet.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ThingTypeUID; + +import java.util.Collections; +import java.util.Set; + +/** + * The {@link PushbulletBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Hakan Tandogan - Initial contribution + */ +@NonNullByDefault +public class PushbulletBindingConstants { + + private static final String BINDING_ID = "pushbullet"; + + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_BOT = new ThingTypeUID(BINDING_ID, "bot"); + + public static final Set SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_BOT); + + // List of all Channel ids + public static final String RECIPIENT = "recipient"; + public static final String TITLE = "title"; + public static final String MESSAGE = "message"; + + // Binding logic constants + public static final String API_METHOD_PUSHES = "pushes"; + + public static final int TIMEOUT = 30 * 1000; // 30 seconds +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletConfiguration.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletConfiguration.java new file mode 100644 index 0000000000000..784e4553b99fa --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletConfiguration.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.binding.pushbullet.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link PushbulletConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Hakan Tandogan - Initial contribution + */ +@NonNullByDefault +public class PushbulletConfiguration { + + private @Nullable String name; + + private String token = "invalid"; + + private String apiUrlBase = "invalid"; + + public @Nullable String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getApiUrlBase() { + return apiUrlBase; + } + + public void setApiUrlBase(String apiUrlBase) { + this.apiUrlBase = apiUrlBase; + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletHandlerFactory.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletHandlerFactory.java new file mode 100644 index 0000000000000..4b73f6c093613 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/PushbulletHandlerFactory.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.pushbullet.internal; + +import static org.openhab.binding.pushbullet.internal.PushbulletBindingConstants.*; + +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.openhab.binding.pushbullet.internal.handler.PushbulletHandler; +import org.osgi.service.component.annotations.Component; + +/** + * The {@link PushbulletHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Hakan Tandogan - Initial contribution + */ +@NonNullByDefault +@Component(configurationPid = "binding.pushbullet", service = ThingHandlerFactory.class) +public class PushbulletHandlerFactory extends BaseThingHandlerFactory { + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (THING_TYPE_BOT.equals(thingTypeUID)) { + return new PushbulletHandler(thing); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/PushbulletActions.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/PushbulletActions.java new file mode 100644 index 0000000000000..c8716bae1cdf1 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/action/PushbulletActions.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.pushbullet.internal.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.pushbullet.internal.handler.PushbulletHandler; +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 PushbulletActions} class defines rule actions for sending notifications + * + * @author Hakan Tandogan - Initial contribution + */ +@ThingActionsScope(name = "pushbullet") +@NonNullByDefault +public class PushbulletActions implements ThingActions { + + private final Logger logger = LoggerFactory.getLogger(PushbulletActions.class); + + private @Nullable PushbulletHandler handler; + + @Override public void setThingHandler(@Nullable ThingHandler handler) { + this.handler = (PushbulletHandler) handler; + } + + @Override public @Nullable ThingHandler getThingHandler() { + return this.handler; + } + + @RuleAction(label = "@text/actionSendPushbulletNoteLabel", description = "@text/actionSendPushbulletNoteDesc") + public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendPushbulletNote( + @ActionInput(name = "recipient", label = "@text/actionSendPushbulletNoteInputRecipientLabel", description = "@text/actionSendPushbulletNoteInputRecipientDesc") @Nullable String recipient, + @ActionInput(name = "title", label = "@text/actionSendPushbulletNoteInputTitleLabel", description = "@text/actionSendPushbulletNoteInputTitleDesc") @Nullable String title, + @ActionInput(name = "message", label = "@text/actionSendPushbulletNoteInputMessageLabel", description = "@text/actionSendPushbulletNoteInputMessageDesc") @Nullable String message) { + logger.trace("sendPushbulletNote '{}', '{}', '{}'", recipient, title, message); + + // Use local variable so the SAT check can do proper flow analysis + PushbulletHandler localHandler = handler; + + if (localHandler == null) { + logger.warn("Pushbullet service Handler is null!"); + return false; + } + + return localHandler.sendPush(recipient, title, message, "note"); + } + + public static boolean sendPushbulletNote(@Nullable ThingActions actions, @Nullable String recipient, + @Nullable String title, @Nullable String message) { + if (actions instanceof PushbulletActions) { + return ((PushbulletActions) actions).sendPushbulletNote(recipient, title, message); + } else { + throw new IllegalArgumentException("Instance is not a PushbulletActions class ( " + actions + " )"); + } + } + + @RuleAction(label = "@text/actionSendPushbulletNoteLabel", description = "@text/actionSendPushbulletNoteDesc") + public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendPushbulletNote( + @ActionInput(name = "recipient", label = "@text/actionSendPushbulletNoteInputRecipientLabel", description = "@text/actionSendPushbulletNoteInputRecipientDesc") @Nullable String recipient, + @ActionInput(name = "message", label = "@text/actionSendPushbulletNoteInputMessageLabel", description = "@text/actionSendPushbulletNoteInputMessageDesc") @Nullable String message) { + logger.trace("sendPushbulletNote '{}', '{}'", recipient, message); + + // Use local variable so the SAT check can do proper flow analysis + PushbulletHandler localHandler = handler; + + if (localHandler == null) { + logger.warn("Pushbullet service Handler is null!"); + return false; + } + + return localHandler.sendPush(recipient, message, "note"); + } + + public static boolean sendPushbulletNote(@Nullable ThingActions actions, @Nullable String recipient, + @Nullable String message) { + if (actions instanceof PushbulletActions) { + return ((PushbulletActions) actions).sendPushbulletNote(recipient, message); + } else { + throw new IllegalArgumentException("Instance is not a PushbulletActions class ( " + actions + " )"); + } + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java new file mode 100644 index 0000000000000..45120307b7e64 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/handler/PushbulletHandler.java @@ -0,0 +1,229 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.pushbullet.internal.handler; + +import static org.openhab.binding.pushbullet.internal.PushbulletBindingConstants.*; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; +import org.eclipse.smarthome.io.net.http.HttpUtil; +import org.openhab.binding.pushbullet.internal.action.PushbulletActions; +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.types.Command; +import org.openhab.binding.pushbullet.internal.PushbulletConfiguration; +import org.openhab.binding.pushbullet.internal.model.Push; +import org.openhab.binding.pushbullet.internal.model.PushResponse; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.Version; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Collections; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; + +/** + * The {@link PushbulletHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Hakan Tandogan - Initial contribution + */ +@NonNullByDefault +public class PushbulletHandler extends BaseThingHandler { + + private final Logger logger = LoggerFactory.getLogger(PushbulletHandler.class); + + private final Gson gson = new GsonBuilder().create(); + + private static final Version VERSION = FrameworkUtil.getBundle(PushbulletHandler.class).getVersion(); + + private static final Pattern CHANNEL_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]+$"); + + private @Nullable PushbulletConfiguration config; + + public PushbulletHandler(Thing thing) { + super(thing); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("About to handle {} on {}", command, channelUID); + + // Future improvement: If recipient is already set, send a push on a command channel change + // check reconnect channel of the unifi binding for that + + logger.debug("The Pushbullet binding is a read-only binding and cannot handle command '{}'.", command); + } + + @Override + public void initialize() { + logger.debug("Start initializing!"); + config = getConfigAs(PushbulletConfiguration.class); + + // Name and Token are both "required", so set the Thing immediately ONLINE. + updateStatus(ThingStatus.ONLINE); + + logger.debug("Finished initializing!"); + } + + @Override + public Collection> getServices() { + return Collections.singleton(PushbulletActions.class); + } + + public boolean sendPush(@Nullable String recipient, @Nullable String message, String type) { + return sendPush(recipient, "", message, type); + } + + public boolean sendPush(@Nullable String recipient, @Nullable String title, @Nullable String message, String type) { + boolean result = false; + + logger.debug("sendPush is called for "); + logger.debug("Thing {}", thing); + logger.debug("Thing Label: '{}'", thing.getLabel()); + + PushbulletConfiguration configuration = getConfigAs(PushbulletConfiguration.class); + logger.debug("CFG {}", configuration); + + Properties headers = prepareRequestHeaders(configuration); + + String request = prepareMessageBody(recipient, title, message, type); + + try (InputStream stream = new ByteArrayInputStream(request.getBytes(StandardCharsets.UTF_8))) { + + String pushAPI = configuration.getApiUrlBase() + "/" + API_METHOD_PUSHES; + + String responseString = HttpUtil.executeUrl(HttpMethod.POST.asString(), pushAPI, headers, stream, + MimeTypes.Type.APPLICATION_JSON.asString(), TIMEOUT); + + logger.debug("Got Response: {}", responseString); + PushResponse response = gson.fromJson(responseString, PushResponse.class); + + logger.debug("Unpacked Response: {}", response); + + stream.close(); + + if ((null != response) && (null == response.getPushError())) { + result = true; + } + } + catch (IOException e) { + logger.warn("IO problems pushing note: {}", e.getMessage()); + } + + return result; + } + + /** + * helper method to populate the request headers + * + * @param configuration + * @return + */ + private Properties prepareRequestHeaders(PushbulletConfiguration configuration) { + Properties headers = new Properties(); + headers.put(HttpHeader.USER_AGENT, "openHAB / Pushbullet binding " + VERSION); + headers.put(HttpHeader.CONTENT_TYPE, MimeTypes.Type.APPLICATION_JSON.asString()); + headers.put("Access-Token", configuration.getToken()); + + logger.debug("Headers: {}", headers); + + return headers; + } + + /** + * helper method to create a message body from data to be transferred. + * + * @param recipient + * @param title + * @param message + * @param type + * + * @return the message as a String to be posted + */ + private String prepareMessageBody(@Nullable String recipient, @Nullable String title, @Nullable String message, + String type) { + logger.debug("Recipient is '{}'", recipient); + logger.debug("Title is '{}'", title); + logger.debug("Message is '{}'", message); + + Push push = new Push(); + push.setTitle(title); + push.setBody(message); + push.setType(type); + + if (recipient != null) { + if (isValidEmail(recipient)) { + logger.debug("Recipient is an email address"); + push.setEmail(recipient); + } else if (isValidChannel(recipient)) { + logger.debug("Recipient is a channel tag"); + push.setChannel(recipient); + } else { + logger.warn("Invalid recipient: {}", recipient); + logger.warn("Message will be broadcast to all user's devices."); + } + } + + logger.debug("Push: {}", push); + + String request = gson.toJson(push); + logger.debug("Packed Request: {}", request); + + return request; + } + + /** + * helper method checking if channel tag is valid. + * + * @param channel + * @return + */ + private static boolean isValidChannel(String channel) { + Matcher m = CHANNEL_PATTERN.matcher(channel); + return m.matches(); + } + + /** + * helper method checking if email address is valid. + * + * @param email + * @return + */ + private static boolean isValidEmail(String email) { + try { + InternetAddress emailAddr = new InternetAddress(email); + emailAddr.validate(); + return true; + } catch (AddressException e) { + return false; + } + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/Push.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/Push.java new file mode 100644 index 0000000000000..e32ca0c711359 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/Push.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.pushbullet.internal.model; + +import com.google.gson.annotations.SerializedName; + +/** + * This class represents the push request sent to the API. + * + * @author Hakan Tandogan - Initial contribution + * @author Hakan Tandogan - Migrated from openHAB 1 action with the same name + */ +public class Push { + + @SerializedName("title") + private String title; + + @SerializedName("body") + private String body; + + @SerializedName("type") + private String type; + + @SerializedName("email") + private String email; + + @SerializedName("channel_tag") + private String channelTag; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getChannel() { + return channelTag; + } + + public void setChannel(String channelTag) { + this.channelTag = channelTag; + } + + @Override + public String toString() { + return "Push {" + "title='" + title + '\'' + ", body='" + body + '\'' + ", type='" + type + '\'' + ", email='" + + email + '\'' + ", channelTag='" + channelTag + '\'' + '}'; + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushError.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushError.java new file mode 100644 index 0000000000000..85f963d62daa7 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushError.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.pushbullet.internal.model; + +import com.google.gson.annotations.SerializedName; + +/** + * This class represents errors in the response fetched from the API. + * + * @author Hakan Tandogan - Initial contribution + * @author Hakan Tandogan - Migrated from openHAB 1 action with the same name + */ +public class PushError { + + @SerializedName("type") + private String type; + + @SerializedName("message") + private String message; + + @SerializedName("param") + private String param; + + @SerializedName("cat") + private String cat; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getParam() { + return param; + } + + public void setParam(String param) { + this.param = param; + } + + public String getCat() { + return cat; + } + + public void setCat(String cat) { + this.cat = cat; + } + + @Override + public String toString() { + return "PushError {" + "type='" + type + '\'' + ", message='" + message + '\'' + ", param='" + param + '\'' + + ", cat='" + cat + '\'' + '}'; + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushResponse.java b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushResponse.java new file mode 100644 index 0000000000000..7183d3b66f26f --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/java/org/openhab/binding/pushbullet/internal/model/PushResponse.java @@ -0,0 +1,211 @@ +/** + * Copyright (c) 2010-2019 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.pushbullet.internal.model; + +import com.google.gson.annotations.SerializedName; + +/** + * This class represents the answer to pushes provided by the API. + * + * @author Hakan Tandogan - Initial contribution + * @author Hakan Tandogan - Migrated from openHAB 1 action with the same name + */ +public class PushResponse { + + @SerializedName("active") + private String active; + + @SerializedName("iden") + private String iden; + + @SerializedName("type") + private String type; + + @SerializedName("dismissed") + private Boolean dismissed; + + @SerializedName("direction") + private String direction; + + @SerializedName("sender_iden") + private String senderIdentifier; + + @SerializedName("sender_email") + private String senderEmail; + + @SerializedName("sender_email_normalized") + private String senderEmailNormalized; + + @SerializedName("sender_name") + private String senderName; + + @SerializedName("receiver_iden") + private String receiverIdentifier; + + @SerializedName("receiver_email") + private String receiverEmail; + + @SerializedName("receiver_email_normalized") + private String receiverEmailNormalized; + + @SerializedName("title") + private String title; + + @SerializedName("body") + private String body; + + @SerializedName("error_code") + private String errorCode; + + @SerializedName("error") + private PushError pushError; + + public String getActive() { + return active; + } + + public void setActive(String active) { + this.active = active; + } + + public String getIden() { + return iden; + } + + public void setIden(String iden) { + this.iden = iden; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Boolean getDismissed() { + return dismissed; + } + + public void setDismissed(Boolean dismissed) { + this.dismissed = dismissed; + } + + public String getDirection() { + return direction; + } + + public void setDirection(String direction) { + this.direction = direction; + } + + public String getSenderIdentifier() { + return senderIdentifier; + } + + public void setSenderIdentifier(String senderIdentifier) { + this.senderIdentifier = senderIdentifier; + } + + public String getSenderEmail() { + return senderEmail; + } + + public void setSenderEmail(String senderEmail) { + this.senderEmail = senderEmail; + } + + public String getSenderEmailNormalized() { + return senderEmailNormalized; + } + + public void setSenderEmailNormalized(String senderEmailNormalized) { + this.senderEmailNormalized = senderEmailNormalized; + } + + public String getSenderName() { + return senderName; + } + + public void setSenderName(String senderName) { + this.senderName = senderName; + } + + public String getReceiverIdentifier() { + return receiverIdentifier; + } + + public void setReceiverIdentifier(String receiverIdentifier) { + this.receiverIdentifier = receiverIdentifier; + } + + public String getReceiverEmail() { + return receiverEmail; + } + + public void setReceiverEmail(String receiverEmail) { + this.receiverEmail = receiverEmail; + } + + public String getReceiverEmailNormalized() { + return receiverEmailNormalized; + } + + public void setReceiverEmailNormalized(String receiverEmailNormalized) { + this.receiverEmailNormalized = receiverEmailNormalized; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public PushError getPushError() { + return pushError; + } + + public void setPushError(PushError pushError) { + this.pushError = pushError; + } + + @Override + public String toString() { + return "PushResponse {" + "active='" + active + '\'' + ", iden='" + iden + '\'' + ", type='" + type + '\'' + + ", dismissed=" + dismissed + ", direction='" + direction + '\'' + ", senderIdentifier='" + + senderIdentifier + '\'' + ", senderEmail='" + senderEmail + '\'' + ", senderEmailNormalized='" + + senderEmailNormalized + '\'' + ", senderName='" + senderName + '\'' + ", receiverIdentifier='" + + receiverIdentifier + '\'' + ", receiverEmail='" + receiverEmail + '\'' + ", receiverEmailNormalized='" + + receiverEmailNormalized + '\'' + ", title='" + title + '\'' + ", body='" + body + '\'' + + ", errorCode='" + errorCode + '\'' + ", pushError=" + pushError + '}'; + } +} diff --git a/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/binding/binding.xml b/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/binding/binding.xml new file mode 100644 index 0000000000000..18f0f94dfce51 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/binding/binding.xml @@ -0,0 +1,9 @@ + + + + @text/binding.pushbullet.name + @text/binding.pushbullet.description + Hakan Tandoğan + + diff --git a/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/i18n/pushbullet_en.properties b/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/i18n/pushbullet_en.properties new file mode 100644 index 0000000000000..df79c02a7897b --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/i18n/pushbullet_en.properties @@ -0,0 +1,17 @@ +# binding +binding.pushbullet.name = Pushbullet Binding +binding.pushbullet.description = The Pushbullet binding allows you to send messages to other users of the Pushbullet service. + +# action +actionSendPushbulletNoteLabel = publish an Pushbullet message +actionSendPushbulletNoteDesc = Publishes a Title to the given Pushbullet Recipient. + +actionSendPushbulletNoteInputRecipientLabel = Pushbullet Recipient +actionSendPushbulletNoteInputRecipientDesc = The Recipient to publish a Title to. +actionSendPushbulletNoteInputTitleLabel = Title +actionSendPushbulletNoteInputTitleDesc = The Title to publish +actionSendPushbulletNoteInputMessageLabel = Message +actionSendPushbulletNoteInputMessageDesc = The Message to publish + +# error texts +offline.conf-error-httpresponseexception = The pushbullet server reported an error, possibly an expired token. Check on web site diff --git a/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/thing/thing-types.xml new file mode 100644 index 0000000000000..ba9c559eecfb7 --- /dev/null +++ b/bundles/org.openhab.binding.pushbullet/src/main/resources/ESH-INF/thing/thing-types.xml @@ -0,0 +1,58 @@ + + + + + + Bot to send messages with. + + + + + + + + + + + Explicit Name of Bot, if wanted + true + + + + + Token as obtained from the server + + + + + The Pushbullet API Server to use, for local testing + https://api.pushbullet.com/v2 + true + + + + + + + + String + + Mail address or Channel Name + + + + String + + Title of the message + + + + String + + The text that is to be sent + + + diff --git a/bundles/org.openhab.binding.rfxcom/README.md b/bundles/org.openhab.binding.rfxcom/README.md index 8bb2a990a7b60..49d0043be4231 100644 --- a/bundles/org.openhab.binding.rfxcom/README.md +++ b/bundles/org.openhab.binding.rfxcom/README.md @@ -154,11 +154,13 @@ This binding currently supports following channel types: |-----------------|---------------|------------------------------------------------------------------------------------| | chimesound | Number | Id of the chime sound | | command | Switch | Command channel. | -| commandId | String | Id of the command. | + | commandId | Number | Id of the command (between 0 and 255). | +| commandString | String | Id of the command. | | contact | Contact | Contact channel. | | datetime | DateTime | DateTime channel. | | dimminglevel | Dimmer | Dimming level channel. | | forecast | String | Weather forecast from device: NO\_INFO\_AVAILABLE/SUNNY/PARTLY\_CLOUDY/CLOUDY/RAIN | +| tempcontrol | Rollershutter | Global control for temperature also setting ON, OFF, UP, DOWN | | humidity | Number | Relative humidity level in percentages. | | humiditystatus | String | Current humidity status: NORMAL/COMFORT/DRY/WET | | instantamp | Number | Instant current in Amperes. | @@ -964,10 +966,31 @@ A Thermostat3 device. #### Channels -| Name | Channel Type | Item Type | Remarks | -|-------------|-------------------------------------|-----------|----------| -| command | [command](#channels) | Switch | | -| signalLevel | [system.signal-strength](#channels) | Number | | +| Name | Channel Type | Item Type | Remarks | +|-------------------|-------------------------------------|---------------|----------| +| command | [command](#channels) | Switch | | +| command2nd | [command](#channels) | Switch | | +| control\* | [tempcontrol](#channels) | Rollershutter | | +| commandString\*\* | [commandString](#channels) | String | | +| signalLevel | [system.signal-strength](#channels) | Number | | + +\* `control` supports: + +* UP +* DOWN +* STOP + +\*\* `commandString` supports: + +* OFF +* ON +* UP +* DOWN +* RUN_UP +* RUN_DOWN +* SECOND_ON +* SECOND_OFF +* STOP #### Configuration Options @@ -982,6 +1005,8 @@ A Thermostat3 device. * MERTIK\_\_G6R\_H4TD\_\_G6R\_H4T16 - Mertik (G6R H4TD or G6R H4T16) * MERTIK\_\_G6R\_H4S\_TRANSMIT\_ONLY - Mertik (G6R H4S \- transmit only) +#### Examples + ### undecoded - RFXCOM Undecoded RF Messages diff --git a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/DeviceMessageListener.java b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/DeviceMessageListener.java index d5a0f44e0af06..abf7577124c3b 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/DeviceMessageListener.java +++ b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/DeviceMessageListener.java @@ -28,9 +28,9 @@ public interface DeviceMessageListener { * This method is called whenever the message is received from the bridge. * * @param bridge - * The RFXCom bridge where message is received. + * The RFXCom bridge where message is received. * @param message - * The message which received. + * The message which received. */ void onDeviceMessageReceived(ThingUID bridge, RFXComDeviceMessage message) throws RFXComException; } diff --git a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/RFXComBindingConstants.java b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/RFXComBindingConstants.java index 558eeceb85573..8a323630875d5 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/RFXComBindingConstants.java +++ b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/RFXComBindingConstants.java @@ -67,8 +67,11 @@ public class RFXComBindingConstants { public static final String CHANNEL_VENETIAN_BLIND = "venetianBlind"; public static final String CHANNEL_SUN_WIND_DETECTOR = "sunWindDetector"; public static final String CHANNEL_COMMAND = "command"; + public static final String CHANNEL_COMMAND_SECOND = "command2nd"; + public static final String CHANNEL_CONTROL = "control"; public static final String CHANNEL_PROGRAM = "program"; public static final String CHANNEL_COMMAND_ID = "commandId"; + public static final String CHANNEL_COMMAND_STRING = "commandString"; public static final String CHANNEL_MOOD = "mood"; public static final String CHANNEL_SIGNAL_LEVEL = "signalLevel"; public static final String CHANNEL_DIMMING_LEVEL = "dimmingLevel"; diff --git a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/connector/RFXComConnectorInterface.java b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/connector/RFXComConnectorInterface.java index f9c28f3194f2e..1550aff9bbad1 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/connector/RFXComConnectorInterface.java +++ b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/connector/RFXComConnectorInterface.java @@ -27,8 +27,8 @@ public interface RFXComConnectorInterface { * Procedure for connecting to RFXCOM controller. * * @param device - * Controller connection parameters (e.g. serial port name or IP - * address). + * Controller connection parameters (e.g. serial port name or IP + * address). */ public void connect(RFXComBridgeConfiguration device) throws Exception; @@ -42,7 +42,7 @@ public interface RFXComConnectorInterface { * Procedure for send raw data to RFXCOM controller. * * @param data - * raw bytes. + * raw bytes. */ public void sendMessage(byte[] data) throws IOException; @@ -50,7 +50,7 @@ public interface RFXComConnectorInterface { * Procedure for register event listener. * * @param listener - * Event listener instance to handle events. + * Event listener instance to handle events. */ public void addEventListener(RFXComEventListener listener); @@ -58,7 +58,7 @@ public interface RFXComConnectorInterface { * Procedure for remove event listener. * * @param listener - * Event listener instance to remove. + * Event listener instance to remove. */ public void removeEventListener(RFXComEventListener listener); diff --git a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/connector/RFXComEventListener.java b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/connector/RFXComEventListener.java index d27092328465f..abf1f9b305298 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/connector/RFXComEventListener.java +++ b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/connector/RFXComEventListener.java @@ -23,7 +23,7 @@ public interface RFXComEventListener { * Procedure for receive raw data from RFXCOM controller. * * @param data - * Received raw data. + * Received raw data. */ void packetReceived(byte[] data); @@ -31,7 +31,7 @@ public interface RFXComEventListener { * Procedure for receiving information fatal error. * * @param error - * Error occurred. + * Error occurred. */ void errorOccurred(String error); diff --git a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/handler/RFXComHandler.java b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/handler/RFXComHandler.java index 16a7f75ce23ee..772b072e535ad 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/handler/RFXComHandler.java +++ b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/handler/RFXComHandler.java @@ -141,13 +141,17 @@ public void onDeviceMessageReceived(ThingUID bridge, RFXComDeviceMessage message updateStatus(ThingStatus.ONLINE); for (Channel channel : getThing().getChannels()) { - String channelId = channel.getUID().getId(); + ChannelUID uid = channel.getUID(); + String channelId = uid.getId(); try { if (channelId.equals(CHANNEL_LOW_BATTERY)) { - updateState(channelId, isLowBattery(message.convertToState(CHANNEL_BATTERY_LEVEL))); + updateState(uid, isLowBattery(message.convertToState(CHANNEL_BATTERY_LEVEL))); } else { - updateState(channelId, message.convertToState(channelId)); + State state = message.convertToState(channelId); + if (state != null) { + updateState(uid, state); + } } } catch (RFXComException e) { logger.trace("{} does not handle {}", channelId, message); diff --git a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComLighting2Message.java b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComLighting2Message.java index 317c0abec96c3..0ddfcb9b8c0e4 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComLighting2Message.java +++ b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComLighting2Message.java @@ -152,7 +152,7 @@ public String getDeviceId() { * Convert a 0-15 scale value to a percent type. * * @param pt - * percent type to convert + * percent type to convert * @return converted value 0-15 */ public static int getDimLevelFromPercentType(PercentType pt) { @@ -164,7 +164,7 @@ public static int getDimLevelFromPercentType(PercentType pt) { * Convert a 0-15 scale value to a percent type. * * @param value - * percent type to convert + * percent type to convert * @return converted value 0-15 */ public static PercentType getPercentTypeFromDimLevel(int value) { diff --git a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComLighting5Message.java b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComLighting5Message.java index f7c5e6f41d7a5..4cd72438128ae 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComLighting5Message.java +++ b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComLighting5Message.java @@ -73,10 +73,10 @@ public byte toByte() { /** * Note: for the lighting5 commands, some command are only supported for certain sub types and * command-bytes might even have a different meaning for another sub type. - *

+ * * If no sub types are specified for a command, its supported by all sub types. * An example is the command OFF which is represented by the byte 0x00 for all subtypes. - *

+ * * Otherwise the list of sub types after the command-bytes indicates the sub types * which support this command with this byte. * Example byte value 0x03 means GROUP_ON for IT and some others while it means MOOD1 for LIGHTWAVERF @@ -206,7 +206,7 @@ public String getDeviceId() { * Convert a 0-31 scale value to a percent type. * * @param pt - * percent type to convert + * percent type to convert * @return converted value 0-31 */ public static int getDimLevelFromPercentType(PercentType pt) { @@ -218,7 +218,7 @@ public static int getDimLevelFromPercentType(PercentType pt) { * Convert a 0-31 scale value to a percent type. * * @param value - * percent type to convert + * percent type to convert * @return converted value 0-31 */ public static PercentType getPercentTypeFromDimLevel(int value) { diff --git a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComMessage.java b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComMessage.java index c0a1b50145eeb..727875d7b030f 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComMessage.java +++ b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComMessage.java @@ -28,7 +28,7 @@ public interface RFXComMessage { * Procedure for encode raw data. * * @param data - * Raw data. + * Raw data. */ void encodeMessage(byte[] data) throws RFXComException; diff --git a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComThermostat3Message.java b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComThermostat3Message.java index a8a92fff5c157..fba3b2f7dcb21 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComThermostat3Message.java +++ b/bundles/org.openhab.binding.rfxcom/src/main/java/org/openhab/binding/rfxcom/internal/messages/RFXComThermostat3Message.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.rfxcom.internal.messages; -import static org.openhab.binding.rfxcom.internal.RFXComBindingConstants.CHANNEL_COMMAND; +import static org.openhab.binding.rfxcom.internal.RFXComBindingConstants.*; import static org.openhab.binding.rfxcom.internal.messages.ByteEnumUtil.fromByte; import static org.openhab.binding.rfxcom.internal.messages.RFXComThermostat3Message.SubType.*; @@ -20,10 +20,13 @@ import java.util.List; 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.library.types.StopMoveType; +import org.eclipse.smarthome.core.library.types.StringType; import org.eclipse.smarthome.core.library.types.UpDownType; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.Type; +import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.rfxcom.internal.exceptions.RFXComException; import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedChannelException; import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedValueException; @@ -98,7 +101,6 @@ public static Commands fromByte(int input, SubType subType) throws RFXComUnsuppo public SubType subType; private int unitId; public Commands command; - private byte commandId; public RFXComThermostat3Message() { super(PacketType.THERMOSTAT3); @@ -115,7 +117,7 @@ public String toString() { str += super.toString(); str += ", Sub type = " + subType; str += ", Device Id = " + getDeviceId(); - str += ", Command = " + command + "(" + commandId + ")"; + str += ", Command = " + command; str += ", Signal level = " + signalLevel; return str; @@ -132,8 +134,7 @@ public void encodeMessage(byte[] data) throws RFXComException { subType = fromByte(SubType.class, super.subType); unitId = (data[4] & 0xFF) << 16 | (data[5] & 0xFF) << 8 | (data[6] & 0xFF); - commandId = data[7]; - command = Commands.fromByte(commandId, subType); + command = Commands.fromByte(data[7], subType); signalLevel = (byte) ((data[8] & 0xF0) >> 4); } @@ -159,19 +160,51 @@ public State convertToState(String channelId) throws RFXComUnsupportedChannelExc switch (channelId) { case CHANNEL_COMMAND: switch (command) { + case RUN_DOWN: case OFF: - case SECOND_OFF: return OnOffType.OFF; case ON: + case RUN_UP: + case UP: + return OnOffType.ON; case SECOND_ON: + case SECOND_OFF: + return null; + default: + return UnDefType.UNDEF; + + } + case CHANNEL_CONTROL: + switch (command) { + case ON: return OnOffType.ON; case UP: + case RUN_UP: return UpDownType.UP; + case OFF: + return OnOffType.OFF; case DOWN: + case RUN_DOWN: return UpDownType.DOWN; + case SECOND_ON: + case SECOND_OFF: + case STOP: + return null; default: throw new RFXComUnsupportedChannelException("Can't convert " + command + " for " + channelId); } + case CHANNEL_COMMAND_SECOND: + switch (command) { + case SECOND_OFF: + return OnOffType.OFF; + case SECOND_ON: + return OnOffType.ON; + default: + return null; + } + case CHANNEL_COMMAND_STRING: + return command == null ? UnDefType.UNDEF : StringType.valueOf(command.toString()); + default: return super.convertToState(channelId); } @@ -183,15 +216,35 @@ public void convertFromState(String channelId, Type type) throws RFXComUnsupport case CHANNEL_COMMAND: if (type instanceof OnOffType) { command = (type == OnOffType.ON ? Commands.ON : Commands.OFF); - } else if (type instanceof UpDownType) { + } else { + throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type); + } + break; + + case CHANNEL_COMMAND_SECOND: + if (type instanceof OnOffType) { + command = (type == OnOffType.ON ? Commands.SECOND_ON : Commands.SECOND_OFF); + } else { + throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type); + } + break; + + case CHANNEL_CONTROL: + if (type instanceof UpDownType) { command = (type == UpDownType.UP ? Commands.UP : Commands.DOWN); - } else if (type instanceof OpenClosedType) { - command = (type == OpenClosedType.CLOSED ? Commands.ON : Commands.OFF); + } else if (type == StopMoveType.STOP) { + command = Commands.STOP; + } else if (type instanceof PercentType) { + command = ((PercentType) type).as(UpDownType.class) == UpDownType.UP ? Commands.UP : Commands.DOWN; } else { throw new RFXComUnsupportedChannelException("Channel " + channelId + " does not accept " + type); } break; + case CHANNEL_COMMAND_STRING: + command = Commands.valueOf(type.toString().toUpperCase()); + break; + default: throw new RFXComUnsupportedChannelException("Channel " + channelId + " is not relevant here"); } diff --git a/bundles/org.openhab.binding.rfxcom/src/main/resources/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.rfxcom/src/main/resources/ESH-INF/thing/channels.xml index 69bb04bcb93ed..4f0d943d90dd0 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/resources/ESH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.rfxcom/src/main/resources/ESH-INF/thing/channels.xml @@ -26,10 +26,16 @@ Number - Command channel, ID of the channel + Command channel, ID of the command + + String + + Command channel, Name of the command + + Contact @@ -70,6 +76,12 @@ + + Rollershutter + + Requested temperature setting, UP, DOWN, STOP + + Switch diff --git a/bundles/org.openhab.binding.rfxcom/src/main/resources/ESH-INF/thing/thermostat3.xml b/bundles/org.openhab.binding.rfxcom/src/main/resources/ESH-INF/thing/thermostat3.xml index bd7d32eb0e6a5..0f7a1b1204054 100644 --- a/bundles/org.openhab.binding.rfxcom/src/main/resources/ESH-INF/thing/thermostat3.xml +++ b/bundles/org.openhab.binding.rfxcom/src/main/resources/ESH-INF/thing/thermostat3.xml @@ -16,6 +16,9 @@ + + + diff --git a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComChimeMessageTest.java b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComChimeMessageTest.java index b7be8e059cec5..4b4d93d88651d 100644 --- a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComChimeMessageTest.java +++ b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComChimeMessageTest.java @@ -19,7 +19,6 @@ import org.openhab.binding.rfxcom.internal.exceptions.RFXComException; import org.openhab.binding.rfxcom.internal.messages.RFXComChimeMessage.SubType; - /** * Test for RFXCom-binding * diff --git a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComDateTimeMessageTest.java b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComDateTimeMessageTest.java index b05e3c8a9390b..41f7e4ea2aeaf 100644 --- a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComDateTimeMessageTest.java +++ b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComDateTimeMessageTest.java @@ -37,7 +37,8 @@ public void testSomeMessages() throws RFXComException { assertEquals("Date time", "2003-04-29T13:21:10", msg.dateTime); assertEquals("Signal Level", 2, RFXComTestHelper.getActualIntValue(msg, CHANNEL_SIGNAL_LEVEL)); - assertEquals("Converted value", DateTimeType.valueOf("2003-04-29T13:21:10"), msg.convertToState(CHANNEL_DATE_TIME)); + assertEquals("Converted value", DateTimeType.valueOf("2003-04-29T13:21:10"), + msg.convertToState(CHANNEL_DATE_TIME)); byte[] decoded = msg.decodeMessage(); diff --git a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComHomeConfortTest.java b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComHomeConfortTest.java index ef37065da798d..fe0cc721f8bc6 100644 --- a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComHomeConfortTest.java +++ b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComHomeConfortTest.java @@ -21,7 +21,6 @@ import org.openhab.binding.rfxcom.internal.messages.RFXComHomeConfortMessage.Commands; import org.openhab.binding.rfxcom.internal.messages.RFXComHomeConfortMessage.SubType; - /** * Test for RFXCom-binding * @@ -29,12 +28,11 @@ * @author Mike Jagdis - added message handling and real test */ public class RFXComHomeConfortTest { - private void testMessage(SubType subType, Commands command, String deviceId, String data) - throws RFXComException { + private void testMessage(SubType subType, Commands command, String deviceId, String data) throws RFXComException { RFXComHomeConfortMessage message = (RFXComHomeConfortMessage) RFXComMessageFactory.createMessage(HOME_CONFORT); message.setSubType(subType); - message.command = command; + message.command = command; message.setDeviceId(deviceId); assertArrayEquals(HexUtils.hexToBytes(data), message.decodeMessage()); diff --git a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComInterfaceControlMessageTest.java b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComInterfaceControlMessageTest.java index 990afef1d5911..8be29f545d45a 100644 --- a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComInterfaceControlMessageTest.java +++ b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComInterfaceControlMessageTest.java @@ -44,7 +44,7 @@ public void resetConfig() { configuration.enableBlindsT1T2T3T4 = false; configuration.enableBlindsT0 = false; configuration.enableProGuard = false; -// configuration.enableFS20Packets = false; + // configuration.enableFS20Packets = false; configuration.enableLaCrosse = false; configuration.enableHidekiUPM = false; configuration.enableADLightwaveRF = false; @@ -60,11 +60,11 @@ public void resetConfig() { configuration.enableX10 = false; } - private void testMessage(RFXComInterfaceMessage.TransceiverType transceiverType, RFXComBridgeConfiguration configuration, String data) - throws RFXComException { + private void testMessage(RFXComInterfaceMessage.TransceiverType transceiverType, + RFXComBridgeConfiguration configuration, String data) throws RFXComException { assertArrayEquals(HexUtils.hexToBytes(data), - new RFXComInterfaceControlMessage(transceiverType, configuration).decodeMessage()); + new RFXComInterfaceControlMessage(transceiverType, configuration).decodeMessage()); } @Test @@ -133,11 +133,11 @@ public void testProGuardMessage() throws RFXComException { testMessage(_433_92MHZ_TRANSCEIVER, configuration, "0D00000203530000200000000000"); } -// @Test -// public void testFS20PacketsMessage() throws RFXComException { -// configuration.enableFS20Packets = true; -// testMessage(_433_92MHZ_TRANSCEIVER, configuration, "0D00000203530000100000000000"); -// } + // @Test + // public void testFS20PacketsMessage() throws RFXComException { + // configuration.enableFS20Packets = true; + // testMessage(_433_92MHZ_TRANSCEIVER, configuration, "0D00000203530000100000000000"); + // } @Test public void testLaCrosseMessage() throws RFXComException { diff --git a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComLighting4MessageTest.java b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComLighting4MessageTest.java index 719ea1a93195c..e230dcac38051 100644 --- a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComLighting4MessageTest.java +++ b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComLighting4MessageTest.java @@ -57,8 +57,7 @@ private void testMessage(String hexMsg, RFXComLighting4Message.SubType subType, } private void testMessage(String hexMsg, RFXComLighting4Message.SubType subType, String deviceId, Integer pulse, - byte commandByte, Integer seqNbr, int signalLevel, int offCommand, int onCommand) - throws RFXComException { + byte commandByte, Integer seqNbr, int signalLevel, int offCommand, int onCommand) throws RFXComException { RFXComLighting4Message msg = (RFXComLighting4Message) RFXComMessageFactory .createMessage(HexUtils.hexToBytes(hexMsg)); assertEquals("Sensor Id", deviceId, msg.getDeviceId()); diff --git a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComRfyMessageTest.java b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComRfyMessageTest.java index df89917c2858e..04cf080e36c8f 100644 --- a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComRfyMessageTest.java +++ b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComRfyMessageTest.java @@ -38,12 +38,11 @@ public void basicBoundaryCheck() throws RFXComException { RFXComTestHelper.basicBoundaryCheck(RFY, message); } - private void testMessage(SubType subType, Commands command, String deviceId, String data) - throws RFXComException { + private void testMessage(SubType subType, Commands command, String deviceId, String data) throws RFXComException { RFXComRfyMessage message = (RFXComRfyMessage) RFXComMessageFactory.createMessage(RFY); message.setSubType(subType); - message.command = command; + message.command = command; message.setDeviceId(deviceId); assertArrayEquals(HexUtils.hexToBytes(data), message.decodeMessage()); diff --git a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComThermostat3MessageTest.java b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComThermostat3MessageTest.java index ebc8a1445c517..8ef24a8770a28 100644 --- a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComThermostat3MessageTest.java +++ b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComThermostat3MessageTest.java @@ -12,13 +12,21 @@ */ package org.openhab.binding.rfxcom.internal.messages; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; +import static org.openhab.binding.rfxcom.internal.RFXComBindingConstants.*; import static org.openhab.binding.rfxcom.internal.messages.RFXComBaseMessage.PacketType.THERMOSTAT3; -import static org.openhab.binding.rfxcom.internal.messages.RFXComThermostat3Message.SubType.MERTIK__G6R_H4TB__G6R_H4T__G6R_H4T21_Z22; +import static org.openhab.binding.rfxcom.internal.messages.RFXComThermostat3Message.SubType.*; +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.types.State; +import org.eclipse.smarthome.core.types.UnDefType; import org.eclipse.smarthome.core.util.HexUtils; import org.junit.Test; import org.openhab.binding.rfxcom.internal.exceptions.RFXComException; +import org.openhab.binding.rfxcom.internal.exceptions.RFXComUnsupportedChannelException; /** * Test for RFXCom-binding @@ -43,18 +51,231 @@ public void basicBoundaryCheck() throws RFXComException { @Test public void testSomeMessages() throws RFXComException { - String hexMessage = "08420101019FAB0280"; + testMessage("08420101019FAB0280", MERTIK__G6R_H4TB__G6R_H4T__G6R_H4T21_Z22, 1, "106411", + RFXComThermostat3Message.Commands.UP, (byte) 8, OnOffType.ON, null, UpDownType.UP, + StringType.valueOf("UP")); + testMessage("084200000000410500", MERTIK__G6R_H4T1, 0, "65", RFXComThermostat3Message.Commands.RUN_DOWN, + (byte) 0, OnOffType.OFF, null, UpDownType.DOWN, StringType.valueOf("RUN_DOWN")); + } + + private void testMessage(String hexMessage, RFXComThermostat3Message.SubType subtype, int sequenceNumber, + String sensorId, RFXComThermostat3Message.Commands command, byte signalLevel, State commandChannel, + State secondCommandChannel, State controlChannel, State commandStringChannel) throws RFXComException { byte[] message = HexUtils.hexToBytes(hexMessage); RFXComThermostat3Message msg = (RFXComThermostat3Message) RFXComMessageFactory.createMessage(message); - assertEquals("SubType", MERTIK__G6R_H4TB__G6R_H4T__G6R_H4T21_Z22, msg.subType); - assertEquals("Seq Number", 1, (short) (msg.seqNbr & 0xFF)); - assertEquals("Sensor Id", "106411", msg.getDeviceId()); - assertEquals("Command", RFXComThermostat3Message.Commands.UP, msg.command); - assertEquals("Signal Level", (byte) 8, msg.signalLevel); + assertEquals("SubType", subtype, msg.subType); + assertEquals("Seq Number", sequenceNumber, (short) (msg.seqNbr & 0xFF)); + assertEquals("Sensor Id", sensorId, msg.getDeviceId()); + assertEquals(CHANNEL_COMMAND, command, msg.command); + assertEquals("Signal Level", signalLevel, msg.signalLevel); + + assertEquals(commandChannel, msg.convertToState(CHANNEL_COMMAND)); + assertEquals(secondCommandChannel, msg.convertToState(CHANNEL_COMMAND_SECOND)); + assertEquals(controlChannel, msg.convertToState(CHANNEL_CONTROL)); + assertEquals(commandStringChannel, msg.convertToState(CHANNEL_COMMAND_STRING)); byte[] decoded = msg.decodeMessage(); assertEquals("Message converted back", hexMessage, HexUtils.bytesToHex(decoded)); } // TODO please add tests for real messages + + @Test + public void testCommandChannelOn() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_COMMAND, OnOffType.ON); + + assertEquals(OnOffType.ON, msg.convertToState(CHANNEL_COMMAND)); + assertEquals(OnOffType.ON, msg.convertToState(CHANNEL_CONTROL)); + assertEquals(StringType.valueOf("ON"), msg.convertToState(CHANNEL_COMMAND_STRING)); + assertNull(msg.convertToState(CHANNEL_COMMAND_SECOND)); + } + + @Test + public void testCommandChannelOff() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_COMMAND, OnOffType.OFF); + + assertEquals(OnOffType.OFF, msg.convertToState(CHANNEL_COMMAND)); + assertEquals(OnOffType.OFF, msg.convertToState(CHANNEL_CONTROL)); + assertNull(msg.convertToState(CHANNEL_COMMAND_SECOND)); + assertEquals(StringType.valueOf("OFF"), msg.convertToState(CHANNEL_COMMAND_STRING)); + } + + @Test + public void testSecondCommandChannelOn() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_COMMAND_SECOND, OnOffType.ON); + + assertNull(msg.convertToState(CHANNEL_COMMAND)); + assertEquals(OnOffType.ON, msg.convertToState(CHANNEL_COMMAND_SECOND)); + assertNull(msg.convertToState(CHANNEL_CONTROL)); + assertEquals(StringType.valueOf("SECOND_ON"), msg.convertToState(CHANNEL_COMMAND_STRING)); + } + + @Test + public void testSecondCommandChannelOff() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_COMMAND_SECOND, OnOffType.OFF); + + assertNull(msg.convertToState(CHANNEL_COMMAND)); + assertEquals(OnOffType.OFF, msg.convertToState(CHANNEL_COMMAND_SECOND)); + assertNull(msg.convertToState(CHANNEL_CONTROL)); + assertEquals(StringType.valueOf("SECOND_OFF"), msg.convertToState(CHANNEL_COMMAND_STRING)); + } + + @Test + public void testControlUp() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_CONTROL, UpDownType.UP); + + assertEquals(OnOffType.ON, msg.convertToState(CHANNEL_COMMAND)); + assertEquals(UpDownType.UP, msg.convertToState(CHANNEL_CONTROL)); + assertEquals(StringType.valueOf("UP"), msg.convertToState(CHANNEL_COMMAND_STRING)); + + assertNull(msg.convertToState(CHANNEL_COMMAND_SECOND)); + } + + @Test + public void testControlDown() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_CONTROL, UpDownType.DOWN); + + assertEquals(UnDefType.UNDEF, msg.convertToState(CHANNEL_COMMAND)); + assertEquals(UpDownType.DOWN, msg.convertToState(CHANNEL_CONTROL)); + assertEquals(StringType.valueOf("DOWN"), msg.convertToState(CHANNEL_COMMAND_STRING)); + + assertNull(msg.convertToState(CHANNEL_COMMAND_SECOND)); + } + + @Test + public void testControlStop() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_CONTROL, StopMoveType.STOP); + + assertEquals(UnDefType.UNDEF, msg.convertToState(CHANNEL_COMMAND)); + assertEquals(StringType.valueOf("STOP"), msg.convertToState(CHANNEL_COMMAND_STRING)); + + assertNull(msg.convertToState(CHANNEL_CONTROL)); + assertNull(msg.convertToState(CHANNEL_COMMAND_SECOND)); + } + + @Test + public void testStatusOff() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_COMMAND_STRING, StringType.valueOf("OFF")); + + assertEquals(OnOffType.OFF, msg.convertToState(CHANNEL_COMMAND)); + assertEquals(OnOffType.OFF, msg.convertToState(CHANNEL_CONTROL)); + assertEquals(StringType.valueOf("OFF"), msg.convertToState(CHANNEL_COMMAND_STRING)); + assertNull(msg.convertToState(CHANNEL_COMMAND_SECOND)); + } + + @Test + public void testStatusOn() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_COMMAND_STRING, StringType.valueOf("On")); + + assertEquals(OnOffType.ON, msg.convertToState(CHANNEL_COMMAND)); + assertEquals(OnOffType.ON, msg.convertToState(CHANNEL_CONTROL)); + assertEquals(StringType.valueOf("ON"), msg.convertToState(CHANNEL_COMMAND_STRING)); + assertNull(msg.convertToState(CHANNEL_COMMAND_SECOND)); + } + + @Test + public void testStatusUp() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_COMMAND_STRING, StringType.valueOf("UP")); + + assertEquals(OnOffType.ON, msg.convertToState(CHANNEL_COMMAND)); + assertEquals(UpDownType.UP, msg.convertToState(CHANNEL_CONTROL)); + assertEquals(StringType.valueOf("UP"), msg.convertToState(CHANNEL_COMMAND_STRING)); + + assertNull(msg.convertToState(CHANNEL_COMMAND_SECOND)); + } + + @Test + public void testStatusDown() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_COMMAND_STRING, StringType.valueOf("down")); + + assertEquals(UnDefType.UNDEF, msg.convertToState(CHANNEL_COMMAND)); + assertEquals(UpDownType.DOWN, msg.convertToState(CHANNEL_CONTROL)); + assertEquals(StringType.valueOf("DOWN"), msg.convertToState(CHANNEL_COMMAND_STRING)); + assertNull(msg.convertToState(CHANNEL_COMMAND_SECOND)); + } + + @Test + public void testStatusRunUp() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_COMMAND_STRING, StringType.valueOf("RUN_UP")); + + assertEquals(OnOffType.ON, msg.convertToState(CHANNEL_COMMAND)); + assertEquals(UpDownType.UP, msg.convertToState(CHANNEL_CONTROL)); + assertEquals(StringType.valueOf("RUN_UP"), msg.convertToState(CHANNEL_COMMAND_STRING)); + assertNull(msg.convertToState(CHANNEL_COMMAND_SECOND)); + } + + @Test + public void testStatusRunDown() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_COMMAND_STRING, StringType.valueOf("RUN_DOWN")); + + assertEquals(OnOffType.OFF, msg.convertToState(CHANNEL_COMMAND)); + assertEquals(UpDownType.DOWN, msg.convertToState(CHANNEL_CONTROL)); + assertEquals(StringType.valueOf("RUN_DOWN"), msg.convertToState(CHANNEL_COMMAND_STRING)); + assertNull(msg.convertToState(CHANNEL_COMMAND_SECOND)); + } + + @Test + public void testStatusStop() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_COMMAND_STRING, StringType.valueOf("STOP")); + + assertEquals(UnDefType.UNDEF, msg.convertToState(CHANNEL_COMMAND)); + assertEquals(StringType.valueOf("STOP"), msg.convertToState(CHANNEL_COMMAND_STRING)); + + assertNull(msg.convertToState(CHANNEL_CONTROL)); + assertNull(msg.convertToState(CHANNEL_COMMAND_SECOND)); + } + + @Test + public void testStatusSecondOn() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_COMMAND_STRING, StringType.valueOf("SECOND_ON")); + + assertEquals(OnOffType.ON, msg.convertToState(CHANNEL_COMMAND_SECOND)); + assertEquals(StringType.valueOf("SECOND_ON"), msg.convertToState(CHANNEL_COMMAND_STRING)); + + assertNull(msg.convertToState(CHANNEL_COMMAND)); + assertNull(msg.convertToState(CHANNEL_CONTROL)); + } + + @Test + public void testStatusSecondOff() throws RFXComUnsupportedChannelException { + RFXComThermostat3Message msg = new RFXComThermostat3Message(); + + msg.convertFromState(CHANNEL_COMMAND_STRING, StringType.valueOf("SECOND_OFF")); + + assertEquals(OnOffType.OFF, msg.convertToState(CHANNEL_COMMAND_SECOND)); + assertEquals(StringType.valueOf("SECOND_OFF"), msg.convertToState(CHANNEL_COMMAND_STRING)); + + assertNull(msg.convertToState(CHANNEL_COMMAND)); + assertNull(msg.convertToState(CHANNEL_CONTROL)); + } } diff --git a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComUndecodedRFMessageTest.java b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComUndecodedRFMessageTest.java index 9d3ef1e3b9f57..7ef7efcdcd2ac 100644 --- a/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComUndecodedRFMessageTest.java +++ b/bundles/org.openhab.binding.rfxcom/src/test/java/org/openhab/binding/rfxcom/internal/messages/RFXComUndecodedRFMessageTest.java @@ -53,8 +53,7 @@ public void testLongMessage() throws RFXComException { .createMessage(PacketType.UNDECODED_RF_MESSAGE); msg.subType = RFXComUndecodedRFMessage.SubType.ARC; msg.seqNbr = 1; - msg.rawPayload = HexUtils - .hexToBytes("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021"); + msg.rawPayload = HexUtils.hexToBytes("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021"); msg.decodeMessage(); } } diff --git a/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/SamsungTvTlsTrustManagerProvider.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/SamsungTvTlsTrustManagerProvider.java index 1d35d69e6e373..2599c071edac7 100644 --- a/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/SamsungTvTlsTrustManagerProvider.java +++ b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/SamsungTvTlsTrustManagerProvider.java @@ -18,20 +18,26 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.X509ExtendedTrustManager; +import javax.security.auth.x500.X500Principal; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.io.net.http.TlsTrustManagerProvider; import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Provides a TrustManager to allow secure websocket connections to any TV (=server) + * TODO: convert to using TrustAllTrustMananger (not supported in OH2.4 yet) * * @author Arjan Mels - Initial Contribution */ @Component @NonNullByDefault public class SamsungTvTlsTrustManagerProvider implements TlsTrustManagerProvider { + private final Logger logger = LoggerFactory.getLogger(WakeOnLanUtility.class); + @Override public String getHostName() { return "SmartViewSDK"; @@ -39,14 +45,25 @@ public String getHostName() { private final X509ExtendedTrustManager trustAllCerts = new X509ExtendedTrustManager() { + private void logCertificateCN(String function, X509Certificate @Nullable [] x509Certificates) { + if (x509Certificates != null && logger.isTraceEnabled()) { + for (X509Certificate certificate : x509Certificates) { + logger.trace("TrustManager {} CN: {}", function, + certificate.getSubjectX500Principal().getName(X500Principal.RFC2253)); + } + } + } + @Override public void checkClientTrusted(X509Certificate @Nullable [] x509Certificates, @Nullable String s) throws CertificateException { + logCertificateCN("checkClientTrusted", x509Certificates); } @Override public void checkServerTrusted(X509Certificate @Nullable [] x509Certificates, @Nullable String s) throws CertificateException { + logCertificateCN("checkServerTrusted", x509Certificates); } @Override @@ -57,26 +74,31 @@ public void checkServerTrusted(X509Certificate @Nullable [] x509Certificates, @N @Override public void checkClientTrusted(X509Certificate @Nullable [] x509Certificates, @Nullable String s, @Nullable Socket socket) throws CertificateException { + logCertificateCN("checkClientTrustedSocket", x509Certificates); } @Override public void checkServerTrusted(X509Certificate @Nullable [] x509Certificates, @Nullable String s, @Nullable Socket socket) throws CertificateException { + logCertificateCN("checkServerTrustedSocket", x509Certificates); } @Override public void checkClientTrusted(X509Certificate @Nullable [] x509Certificates, @Nullable String s, @Nullable SSLEngine sslEngine) throws CertificateException { + logCertificateCN("checkClientTrustedEngine", x509Certificates); } @Override public void checkServerTrusted(X509Certificate @Nullable [] x509Certificates, @Nullable String s, @Nullable SSLEngine sslEngine) throws CertificateException { + logCertificateCN("checkServerTrustedEngine", x509Certificates); } }; @Override public X509ExtendedTrustManager getTrustManager() { return trustAllCerts; + // return TrustAllTrustMananger.getInstance(); } } diff --git a/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/WakeOnLanUtility.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/WakeOnLanUtility.java index 5e40210562cf6..672b0efe40c7b 100644 --- a/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/WakeOnLanUtility.java +++ b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/WakeOnLanUtility.java @@ -36,7 +36,7 @@ @NonNullByDefault public class WakeOnLanUtility { - private static Logger logger = LoggerFactory.getLogger(WakeOnLanUtility.class); + private static final Logger logger = LoggerFactory.getLogger(WakeOnLanUtility.class); /** * Get MAC address for host diff --git a/bundles/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 index eb29611db1cc0..7ff267e45ee9c 100644 --- a/bundles/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 @@ -28,7 +28,6 @@ import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.ThingUID; import org.jupnp.model.meta.RemoteDevice; -import org.openhab.binding.samsungtv.internal.service.RemoteControllerService; import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,8 +56,6 @@ public Set getSupportedThingTypeUIDs() { Map properties = new HashMap<>(); properties.put(HOST_NAME, device.getIdentity().getDescriptorURL().getHost()); - properties.putAll(RemoteControllerService.discover(device.getIdentity().getDescriptorURL().getHost())); - DiscoveryResult result = DiscoveryResultBuilder.create(uid).withProperties(properties) .withLabel(getLabel(device)).build(); @@ -98,8 +95,8 @@ private String getLabel(RemoteDevice device) { if (logger.isDebugEnabled()) { String modelName = device.getDetails().getModelDetails().getModelName(); String friendlyName = device.getDetails().getFriendlyName(); - logger.debug("Discovered a Samsung TV '{}' model '{}' thing with UDN '{}'", friendlyName, - modelName, udn); + logger.debug("Retrieved Thing UID for a Samsung TV '{}' model '{}' thing with UDN '{}'", + friendlyName, modelName, udn); } return new ThingUID(SAMSUNG_TV_THING_TYPE, udn); diff --git a/bundles/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 index e9c2ff233b4ba..41bbcd46755bb 100644 --- a/bundles/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 @@ -16,6 +16,7 @@ import java.util.Collection; import java.util.Collections; +import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.TimeUnit; @@ -67,7 +68,7 @@ public class SamsungTvHandler extends BaseThingHandler implements DiscoveryListe private static final int WOL_PACKET_RETRY_COUNT = 10; private static final int WOL_SERVICE_CHECK_COUNT = 30; - private Logger logger = LoggerFactory.getLogger(SamsungTvHandler.class); + private final Logger logger = LoggerFactory.getLogger(SamsungTvHandler.class); private UpnpIOService upnpIOService; private DiscoveryServiceRegistry discoveryServiceRegistry; @@ -149,14 +150,6 @@ public void initialize() { discoveryServiceRegistry.addDiscoveryListener(this); checkAndCreateServices(); - - SamsungTvConfiguration configuration = getConfigAs(SamsungTvConfiguration.class); - if (StringUtils.isEmpty(configuration.macAddress) && configuration.hostName != null) { - String macAddress = WakeOnLanUtility.getMACAddress(configuration.hostName); - if (macAddress != null) { - getConfig().put(SamsungTvConfiguration.MAC_ADDRESS, macAddress); - } - } } @Override @@ -277,14 +270,12 @@ private synchronized void checkCreateManualConnection() { RemoteControllerService service = (RemoteControllerService) findServiceInstance( RemoteControllerService.SERVICE_NAME); if (service == null) { - putOffline(); SamsungTvConfiguration configuration = getConfigAs(SamsungTvConfiguration.class); service = RemoteControllerService.createNonUpnpService(configuration.hostName, configuration.port); startService(service); } else { // open connection again if needed if (!service.checkConnection()) { - putOffline(); service.start(); } } @@ -309,9 +300,27 @@ private synchronized void stopService(SamsungTvService service) { public void thingDiscovered(DiscoveryService source, DiscoveryResult result) { SamsungTvConfiguration configuration = getConfigAs(SamsungTvConfiguration.class); - if (configuration.hostName.equals(result.getProperties().get(SamsungTvConfiguration.HOST_NAME))) { + if (configuration.hostName != null + && configuration.hostName.equals(result.getProperties().get(SamsungTvConfiguration.HOST_NAME))) { logger.debug("thingDiscovered: {}, {}", result.getProperties().get(SamsungTvConfiguration.HOST_NAME), result); + + /* Check if configuration should be updated */ + if (StringUtils.isEmpty(configuration.macAddress)) { + String macAddress = WakeOnLanUtility.getMACAddress(configuration.hostName); + if (macAddress != null) { + getConfig().put(SamsungTvConfiguration.MAC_ADDRESS, macAddress); + logger.debug("thingDiscovered, macAddress: {}", macAddress); + } + } + if (SamsungTvConfiguration.PROTOCOL_NONE.equals(configuration.protocol)) { + Map properties = RemoteControllerService.discover(configuration.hostName); + for (Map.Entry property : properties.entrySet()) { + getConfig().put(property.getKey(), property.getValue()); + logger.debug("thingDiscovered, {}: {}", property.getKey(), property.getValue()); + } + } + /* * SamsungTV discovery services creates thing UID from UPnP UDN. * When thing is generated manually, thing UID may not match UPnP UDN, so store it for later use (e.g. diff --git a/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/RemoteControllerWebSocket.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/RemoteControllerWebSocket.java index e9f61f042189b..7266cb5be47cf 100644 --- a/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/RemoteControllerWebSocket.java +++ b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/RemoteControllerWebSocket.java @@ -293,7 +293,7 @@ public void sendKeys(List keys) throws RemoteControllerException { * @throws RemoteControllerException */ public void sendKeys(List keys, int sleepInMs) throws RemoteControllerException { - logger.debug("Try to send sequnce of commands: {}", keys); + logger.debug("Try to send sequence of commands: {}", keys); if (!isConnected()) { openConnection(); diff --git a/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/WebSocketBase.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/WebSocketBase.java index 1ad26a8342d11..44ce3102bf031 100644 --- a/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/WebSocketBase.java +++ b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/WebSocketBase.java @@ -53,8 +53,11 @@ public void onWebSocketClose(int statusCode, @Nullable String reason) { @Override public void onWebSocketError(@Nullable Throwable error) { - logger.debug("{} connection error: {}", this.getClass().getSimpleName(), - error != null ? error.toString() : null); + if (logger.isTraceEnabled()) { + logger.trace("{} connection error", this.getClass().getSimpleName(), error); + } else { + logger.debug("{} connection error", this.getClass().getSimpleName()); + } super.onWebSocketError(error); isConnecting = false; } diff --git a/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/WebSocketRemote.java b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/WebSocketRemote.java index f1c8708bbc887..e1a3ebddb2c4f 100644 --- a/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/WebSocketRemote.java +++ b/bundles/org.openhab.binding.samsungtv/src/main/java/org/openhab/binding/samsungtv/internal/protocol/WebSocketRemote.java @@ -98,6 +98,12 @@ public void onWebSocketText(@Nullable String msgarg) { if (jsonMsg.data.token != null) { this.remoteControllerWebSocket.callback.putConfig(SamsungTvConfiguration.WEBSOCKET_TOKEN, jsonMsg.data.token); + // try opening additional websockets + try { + this.remoteControllerWebSocket.openConnection(); + } catch (RemoteControllerException e) { + logger.warn("{}: Error ({})", this.getClass().getSimpleName(), e.getMessage()); + } } getApps(); break; diff --git a/bundles/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 index c9945efccc592..3f7311ce750af 100644 --- a/bundles/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 @@ -55,7 +55,7 @@ public class MainTVServerService implements UpnpIOParticipant, SamsungTvService public static final String SERVICE_NAME = "MainTVServer2"; private static final List SUPPORTEDCOMMANDS = Arrays.asList(SOURCE_NAME, BROWSER_URL, STOP_BROWSER); - private Logger logger = LoggerFactory.getLogger(MainTVServerService.class); + private final Logger logger = LoggerFactory.getLogger(MainTVServerService.class); private UpnpIOService service; diff --git a/bundles/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 index 98ae36f4cb7a0..1fa0f5c3c4a1b 100644 --- a/bundles/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 @@ -55,7 +55,7 @@ public class MediaRendererService implements UpnpIOParticipant, SamsungTvService private final List supportedCommands = Arrays.asList(VOLUME, MUTE, BRIGHTNESS, CONTRAST, SHARPNESS, COLOR_TEMPERATURE); - private Logger logger = LoggerFactory.getLogger(MediaRendererService.class); + private final Logger logger = LoggerFactory.getLogger(MediaRendererService.class); private @Nullable UpnpIOService service; diff --git a/bundles/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 index 48652a03d4b84..f65082b7ca5c7 100644 --- a/bundles/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 @@ -60,7 +60,7 @@ @NonNullByDefault public class RemoteControllerService implements SamsungTvService, RemoteControllerWebsocketCallback { - private Logger logger = LoggerFactory.getLogger(RemoteControllerService.class); + private final Logger logger = LoggerFactory.getLogger(RemoteControllerService.class); public static final String SERVICE_NAME = "RemoteControlReceiver"; @@ -209,17 +209,18 @@ public void start() { String protocol = (String) getConfig(SamsungTvConfiguration.PROTOCOL); logger.info("Using {} interface", protocol); - if (SamsungTvConfiguration.PROTOCOL_NONE.equals(protocol)) { - remoteController = null; - return; - } else if (SamsungTvConfiguration.PROTOCOL_LEGACY.equals(protocol)) { + if (SamsungTvConfiguration.PROTOCOL_LEGACY.equals(protocol)) { remoteController = new RemoteControllerLegacy(host, port, "openHAB", "openHAB"); - } else { + } else if (SamsungTvConfiguration.PROTOCOL_WEBSOCKET.equals(protocol) + || SamsungTvConfiguration.PROTOCOL_SECUREWEBSOCKET.equals(protocol)) { try { remoteController = new RemoteControllerWebSocket(host, port, "openHAB", "openHAB", this); } catch (RemoteControllerException e) { reportError("Cannot connect to remote control service", e); } + } else { + remoteController = null; + return; } if (remoteController != null) { diff --git a/bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/config/config.xml b/bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/config/config.xml index 459739cc0149b..2d4d5afe78390 100644 --- a/bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/config/config.xml +++ b/bundles/org.openhab.binding.samsungtv/src/main/resources/ESH-INF/config/config.xml @@ -25,6 +25,22 @@ 1000 + + + The type of remote control protocol. This depends on the age of the TV. + + + + + + + None + + + + Security token for secure websocket connection + true + diff --git a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/SatelHandlerFactory.java b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/SatelHandlerFactory.java index 4f32fb0ee69d8..874f250847665 100644 --- a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/SatelHandlerFactory.java +++ b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/SatelHandlerFactory.java @@ -30,6 +30,7 @@ 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.io.transport.serial.SerialPortManager; import org.openhab.binding.satel.internal.config.SatelThingConfig; import org.openhab.binding.satel.internal.discovery.SatelDeviceDiscoveryService; import org.openhab.binding.satel.internal.handler.Ethm1BridgeHandler; @@ -43,6 +44,7 @@ import org.openhab.binding.satel.internal.handler.SatelZoneHandler; import org.osgi.framework.ServiceRegistration; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; /** * The {@link SatelHandlerFactory} is responsible for creating things and thing @@ -59,6 +61,8 @@ public class SatelHandlerFactory extends BaseThingHandlerFactory { private Map> discoveryServiceRegistrations = new ConcurrentHashMap<>(); + private SerialPortManager serialPortManager; + @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); @@ -87,7 +91,7 @@ protected ThingHandler createHandler(Thing thing) { registerDiscoveryService(bridgeHandler); return bridgeHandler; } else if (IntRSBridgeHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) { - SatelBridgeHandler bridgeHandler = new IntRSBridgeHandler((Bridge) thing); + SatelBridgeHandler bridgeHandler = new IntRSBridgeHandler((Bridge) thing, serialPortManager); registerDiscoveryService(bridgeHandler); return bridgeHandler; } else if (SatelZoneHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) { @@ -118,6 +122,15 @@ protected void removeHandler(ThingHandler thingHandler) { } } + @Reference + protected void setSerialPortManager(final SerialPortManager serialPortManager) { + this.serialPortManager = serialPortManager; + } + + protected void unsetSerialPortManager(final SerialPortManager serialPortManager) { + this.serialPortManager = null; + } + private void registerDiscoveryService(SatelBridgeHandler bridgeHandler) { SatelDeviceDiscoveryService discoveryService = new SatelDeviceDiscoveryService(bridgeHandler, (thingTypeUID) -> getThingTypeByUID(thingTypeUID)); diff --git a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/handler/IntRSBridgeHandler.java b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/handler/IntRSBridgeHandler.java index f92ab07814eaf..c25b766527017 100644 --- a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/handler/IntRSBridgeHandler.java +++ b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/handler/IntRSBridgeHandler.java @@ -20,11 +20,13 @@ import java.util.Set; import org.apache.commons.lang.StringUtils; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.config.core.status.ConfigStatusMessage; 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.ThingTypeUID; +import org.eclipse.smarthome.io.transport.serial.SerialPortManager; import org.openhab.binding.satel.internal.config.IntRSConfig; import org.openhab.binding.satel.internal.protocol.IntRSModule; import org.openhab.binding.satel.internal.protocol.SatelModule; @@ -37,14 +39,18 @@ * * @author Krzysztof Goworek - Initial contribution */ +@NonNullByDefault public class IntRSBridgeHandler extends SatelBridgeHandler { public static final Set SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_INTRS); private final Logger logger = LoggerFactory.getLogger(IntRSBridgeHandler.class); - public IntRSBridgeHandler(Bridge bridge) { + private final SerialPortManager serialPortManager; + + public IntRSBridgeHandler(Bridge bridge, SerialPortManager serialPortManager) { super(bridge); + this.serialPortManager = serialPortManager; } @Override @@ -53,7 +59,7 @@ public void initialize() { IntRSConfig config = getConfigAs(IntRSConfig.class); if (StringUtils.isNotBlank(config.getPort())) { - SatelModule satelModule = new IntRSModule(config.getPort(), config.getTimeout()); + SatelModule satelModule = new IntRSModule(config.getPort(), serialPortManager, config.getTimeout()); super.initialize(satelModule); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, diff --git a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/protocol/IntRSModule.java b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/protocol/IntRSModule.java index 6aba8de091dcd..b63a96fa3ad89 100644 --- a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/protocol/IntRSModule.java +++ b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/protocol/IntRSModule.java @@ -17,47 +17,49 @@ import java.io.OutputStream; import java.util.TooManyListenersException; -import javax.naming.ConfigurationException; - +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.io.transport.serial.PortInUseException; +import org.eclipse.smarthome.io.transport.serial.SerialPort; +import org.eclipse.smarthome.io.transport.serial.SerialPortEvent; +import org.eclipse.smarthome.io.transport.serial.SerialPortEventListener; +import org.eclipse.smarthome.io.transport.serial.SerialPortIdentifier; +import org.eclipse.smarthome.io.transport.serial.SerialPortManager; +import org.eclipse.smarthome.io.transport.serial.UnsupportedCommOperationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gnu.io.CommPortIdentifier; -import gnu.io.NoSuchPortException; -import gnu.io.PortInUseException; -import gnu.io.SerialPort; -import gnu.io.SerialPortEvent; -import gnu.io.SerialPortEventListener; -import gnu.io.UnsupportedCommOperationException; - /** * Represents Satel INT-RS module. Implements methods required to connect and * communicate with that module over serial protocol. * * @author Krzysztof Goworek - Initial contribution */ +@NonNullByDefault public class IntRSModule extends SatelModule { private final Logger logger = LoggerFactory.getLogger(IntRSModule.class); - private String port; + private final String port; + + private final SerialPortManager serialPortManager; /** * Creates new instance with port and timeout set to specified values. * * @param port - * serial port the module is connected to + * serial port the module is connected to + * @param serialPortManager + * serial port manager object * @param timeout - * timeout value in milliseconds for connect/read/write - * operations - * @throws ConfigurationException - * unconditionally throws this exception as it is not - * implemented yet + * timeout value in milliseconds for connect/read/write + * operations */ - public IntRSModule(String port, int timeout) { + public IntRSModule(String port, SerialPortManager serialPortManager, int timeout) { super(timeout); this.port = port; + this.serialPortManager = serialPortManager; } @Override @@ -65,7 +67,10 @@ protected CommunicationChannel connect() throws ConnectionFailureException { logger.info("Connecting to INT-RS module at {}", this.port); try { - CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(this.port); + SerialPortIdentifier portIdentifier = serialPortManager.getIdentifier(this.port); + if (portIdentifier == null) { + throw new ConnectionFailureException(String.format("Port %s does not exist", this.port)); + } SerialPort serialPort = portIdentifier.open("org.openhab.binding.satel", 2000); serialPort.setSerialPortParams(19200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); serialPort.enableReceiveTimeout(this.getTimeout()); @@ -86,8 +91,6 @@ public void serialEvent(SerialPortEvent ev) { logger.info("INT-RS module connected successfuly"); return new SerialCommunicationChannel(serialPort); - } catch (NoSuchPortException e) { - throw new ConnectionFailureException(String.format("Port %s does not exist", this.port), e); } catch (PortInUseException e) { throw new ConnectionFailureException(String.format("Port %s in use", this.port), e); } catch (UnsupportedCommOperationException e) { @@ -106,11 +109,13 @@ public SerialCommunicationChannel(SerialPort serialPort) { } @Override + @Nullable public InputStream getInputStream() throws IOException { return this.serialPort.getInputStream(); } @Override + @Nullable public OutputStream getOutputStream() throws IOException { return this.serialPort.getOutputStream(); } diff --git a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/protocol/SatelModule.java b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/protocol/SatelModule.java index 46165b9649ebf..d02e043cc8483 100644 --- a/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/protocol/SatelModule.java +++ b/bundles/org.openhab.binding.satel/src/main/java/org/openhab/binding/satel/internal/protocol/SatelModule.java @@ -88,7 +88,11 @@ protected interface TimeoutTimer { */ protected static class ConnectionFailureException extends Exception { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; + + public ConnectionFailureException(String message) { + super(message); + } public ConnectionFailureException(String message, Throwable cause) { super(message, cause); diff --git a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/SomfyTahomaBindingConstants.java b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/SomfyTahomaBindingConstants.java index 4b48b6f01000c..7c945f6e4725b 100644 --- a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/SomfyTahomaBindingConstants.java +++ b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/SomfyTahomaBindingConstants.java @@ -193,6 +193,8 @@ public class SomfyTahomaBindingConstants { public static final String STATUS_STATE = "core:StatusState"; public static final String UNAVAILABLE = "unavailable"; public static final String AUTHENTICATION_CHALLENGE = "HTTP protocol violation: Authentication challenge without WWW-Authenticate header"; + public static final String TOO_MANY_REQUESTS = "Too many requests, try again later"; + public static final int SUSPEND_TIME = 120; // supported uiClasses public static final String ROLLERSHUTTER = "RollerShutter"; diff --git a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/config/SomfyTahomaConfig.java b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/config/SomfyTahomaConfig.java index c3a2abbecee49..61ca7bc8275e8 100644 --- a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/config/SomfyTahomaConfig.java +++ b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/config/SomfyTahomaConfig.java @@ -23,8 +23,7 @@ @NonNullByDefault public class SomfyTahomaConfig { private String email = ""; - private String password = ""; - private String thingUid = ""; + private String password = ""; private int refresh = 30; private int statusTimeout = 300; @@ -36,14 +35,6 @@ public String getPassword() { return password; } - public String getThingUid() { - return thingUid; - } - - public void setThingUid(String thingUid) { - this.thingUid = thingUid; - } - public int getRefresh() { return refresh; } @@ -51,4 +42,12 @@ public int getRefresh() { public int getStatusTimeout() { return statusTimeout; } + + public void setEmail(String email) { + this.email = email; + } + + public void setPassword(String password) { + this.password = password; + } } diff --git a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/discovery/SomfyTahomaItemDiscoveryService.java b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/discovery/SomfyTahomaItemDiscoveryService.java index 16f8535a5ecf0..429cc8505b06c 100644 --- a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/discovery/SomfyTahomaItemDiscoveryService.java +++ b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/discovery/SomfyTahomaItemDiscoveryService.java @@ -47,11 +47,9 @@ public class SomfyTahomaItemDiscoveryService extends AbstractDiscoveryService im private SomfyTahomaBridgeHandler bridge; - @Nullable - private DiscoveryServiceCallback discoveryServiceCallback; - @Nullable - private ScheduledFuture discoveryJob; + private @Nullable DiscoveryServiceCallback discoveryServiceCallback; + private @Nullable ScheduledFuture discoveryJob; private static final int DISCOVERY_TIMEOUT_SEC = 10; private static final int DISCOVERY_REFRESH_SEC = 1800; diff --git a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBaseThingHandler.java b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBaseThingHandler.java index c9c28b69ea129..036d91374debf 100644 --- a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBaseThingHandler.java +++ b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBaseThingHandler.java @@ -44,8 +44,7 @@ public abstract class SomfyTahomaBaseThingHandler extends BaseThingHandler { protected HashMap stateNames = new HashMap<>(); //cache - @Nullable - private ExpiringCache> thingStates; + private @Nullable ExpiringCache> thingStates; public SomfyTahomaBaseThingHandler(Thing thing) { super(thing); @@ -59,8 +58,12 @@ public HashMap getStateNames() { public void initialize() { thingStates = new ExpiringCache<>(CACHE_EXPIRY, () -> getThingStates()); - SomfyTahomaState state = getCachedThingState(STATUS_STATE); - updateThingStatus(state); + if (ThingStatus.ONLINE == getBridge().getStatus()) { + SomfyTahomaState state = getCachedThingState(STATUS_STATE); + updateThingStatus(state); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE); + } } private synchronized @Nullable SomfyTahomaState getCachedThingState(String state) { @@ -94,13 +97,13 @@ private String getURL() { } private void setAvailable() { - if (!ThingStatus.ONLINE.equals(thing.getStatus())) { + if (ThingStatus.ONLINE != thing.getStatus()) { updateStatus(ThingStatus.ONLINE); } } private void setUnavailable() { - if (!ThingStatus.OFFLINE.equals(thing.getStatus()) && !isAlwaysOnline()) { + if (ThingStatus.OFFLINE != thing.getStatus() && !isAlwaysOnline()) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, UNAVAILABLE); } } @@ -313,7 +316,7 @@ public void updateThingChannels(ArrayList states) { if (isChannelLinked(channel)) { State channelState = getChannelState(channel, states); if (channelState != null) { - logger.trace("Updating channel: {} with state: {}", channel.getUID(), channelState.toString()); + logger.trace("Updating channel: {} with state: {}", channel.getUID(), channelState); updateState(channel.getUID(), channelState); } else { logger.debug("Cannot find state for channel {}", channel.getUID()); diff --git a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBridgeHandler.java b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBridgeHandler.java index 9145eb169bc50..9a8baad9c2345 100644 --- a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBridgeHandler.java +++ b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaBridgeHandler.java @@ -36,6 +36,9 @@ import static org.openhab.binding.somfytahoma.internal.SomfyTahomaBindingConstants.*; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledFuture; @@ -62,15 +65,15 @@ public class SomfyTahomaBridgeHandler extends ConfigStatusBridgeHandler { /** * Future to poll for updates */ - @Nullable - private ScheduledFuture pollFuture; + private @Nullable ScheduledFuture pollFuture; /** * Future to poll for status */ - @Nullable - private ScheduledFuture statusFuture; + private @Nullable ScheduledFuture statusFuture; + // Too many request flag + private boolean tooManyRequests = false; /** * Our configuration @@ -90,9 +93,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { @Override public void initialize() { - String thingUid = getThing().getUID().toString(); thingConfig = getConfigAs(SomfyTahomaConfig.class); - thingConfig.setThingUid(thingUid); httpClient.setFollowRedirects(false); @@ -127,6 +128,10 @@ public synchronized void login() { return; } + if (tooManyRequests || ThingStatus.ONLINE == thing.getStatus()) { + return; + } + try { if (httpClient.isStarted()) { httpClient.stop(); @@ -134,27 +139,31 @@ public synchronized void login() { httpClient.start(); url = TAHOMA_URL + "login"; - String urlParameters = "userId=" + thingConfig.getEmail() + "&userPassword=" + thingConfig.getPassword(); + String urlParameters = "userId=" + urlEncode(thingConfig.getEmail()) + "&userPassword=" + urlEncode(thingConfig.getPassword()); ContentResponse response = sendRequestBuilder(url, HttpMethod.POST) .content(new StringContentProvider(urlParameters), "application/x-www-form-urlencoded; charset=UTF-8") .send(); - logger.trace("Login response: {}", response.getContentAsString()); + if (logger.isTraceEnabled()) { + logger.trace("Login response: {}", response.getContentAsString()); + } + SomfyTahomaLoginResponse data = gson.fromJson(response.getContentAsString(), SomfyTahomaLoginResponse.class); if (data.isSuccess()) { logger.debug("SomfyTahoma version: {}", data.getVersion()); updateStatus(ThingStatus.ONLINE); } else { - logger.debug("Login response: {}", response.getContentAsString()); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error logging in"); - throw new SomfyTahomaException(response.getContentAsString()); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error logging in: " + data.getError()); + if (data.getError().startsWith(TOO_MANY_REQUESTS)) { + logger.debug("Too many requests error, suspending activity for {} seconds", SUSPEND_TIME); + tooManyRequests = true; + scheduler.schedule(this::enableLogin, SUSPEND_TIME, TimeUnit.SECONDS); + } } } catch (JsonSyntaxException e) { logger.debug("Received invalid data", e); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Received invalid data"); - } catch (SomfyTahomaException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Unauthorized. Please check credentials"); } catch (InterruptedException | ExecutionException | TimeoutException e) { logger.debug("Cannot get login cookie!", e); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot get login cookie"); @@ -165,7 +174,15 @@ public synchronized void login() { } - private ArrayList getEvents() { + private String urlEncode(String text) throws UnsupportedEncodingException { + return URLEncoder.encode(text, StandardCharsets.UTF_8.toString()); + } + + private void enableLogin() { + tooManyRequests = false; + } + + private List getEvents() { String url; String line = ""; @@ -319,12 +336,12 @@ private String getFormattedParameters(Collection stateNames) { sb.append(','); sb.append("{\"name\": \"").append(name).append("\"}"); } - logger.debug("Formatted parameters: {}", sb.toString()); + logger.trace("Formatted parameters: {}", sb); return sb.toString(); } - public @Nullable List getAllStates(Collection stateNames, String deviceUrl) { + public @Nullable synchronized List getAllStates(Collection stateNames, String deviceUrl) { String url; String line = ""; @@ -364,12 +381,11 @@ private String getFormattedParameters(Collection stateNames) { private void getTahomaUpdates() { logger.debug("Getting Tahoma Updates..."); - if (ThingStatus.OFFLINE.equals(thing.getStatus())) { - logger.debug("Doing relogin"); - login(); + if (ThingStatus.OFFLINE == thing.getStatus() && !reLogin()) { + return; } - ArrayList events = getEvents(); + List events = getEvents(); logger.debug("Got total of {} events", events.size()); for (SomfyTahomaEvent event : events) { processEvent(event); @@ -415,7 +431,7 @@ private synchronized void updateAllStates() { private void processStateChangedEvent(SomfyTahomaEvent event) { String deviceUrl = event.getDeviceUrl(); ArrayList states = event.getDeviceStates(); - logger.debug("States for device {} : {}", deviceUrl, states.toString()); + logger.debug("States for device {} : {}", deviceUrl, states); Thing thing = getThingByDeviceUrl(deviceUrl); if (thing != null) { @@ -434,9 +450,8 @@ private void processStateChangedEvent(SomfyTahomaEvent event) { private void refreshTahomaStates() { logger.debug("Refreshing Tahoma states..."); - if (ThingStatus.OFFLINE.equals(thing.getStatus())) { - logger.debug("Doing relogin"); - login(); + if (ThingStatus.OFFLINE == thing.getStatus() && !reLogin()) { + return; } //force Tahoma to ask for actual states @@ -467,7 +482,10 @@ private String sendDataToTahomaWithCookie(String url, String urlParameters) thro .content(new StringContentProvider(urlParameters), "application/json;charset=UTF-8") .send(); - logger.trace("Response: {}", response.getContentAsString()); + if (logger.isTraceEnabled()) { + logger.trace("Response: {}", response.getContentAsString()); + } + if (response.getStatus() < 200 || response.getStatus() >= 300) { logger.error("Received error code: {}", response.getStatus()); if (response.getStatus() == 404) { @@ -493,7 +511,10 @@ private String sendMethodToTahomaWithCookie(String url, HttpMethod method) throw logger.trace("Sending {} to Tahoma to url: {}", method.asString(), url); ContentResponse response = sendRequestBuilder(url, method).send(); - logger.trace("Response: {}", response.getContentAsString()); + if (logger.isTraceEnabled()) { + logger.trace("Response: {}", response.getContentAsString()); + } + if (response.getStatus() < 200 || response.getStatus() >= 300) { logger.error("Received error code: {}", response.getStatus()); if (response.getStatus() == 404) { @@ -514,6 +535,10 @@ private Request sendRequestBuilder(String url, HttpMethod method) { } public void sendCommand(String io, String command, String params) { + if (ThingStatus.OFFLINE == thing.getStatus() && !reLogin()) { + return; + } + Boolean result = sendCommandInternal(io, command, params); if (result != null && !result) { sendCommandInternal(io, command, params); @@ -689,12 +714,21 @@ public void cancelExecution(String executionId) { } public void executeActionGroup(String id) { + if (ThingStatus.OFFLINE == thing.getStatus() && !reLogin()) { + return; + } String execId = executeActionGroupInternal(id); if (UNAUTHORIZED.equals(execId)) { executeActionGroupInternal(id); } } + private boolean reLogin() { + logger.debug("Doing relogin"); + login(); + return ThingStatus.OFFLINE != thing.getStatus(); + } + public @Nullable String executeActionGroupInternal(String id) { String line = ""; try { @@ -777,4 +811,15 @@ public void refreshDeviceStates() { private boolean isAuthenticationChallenge(Exception ex) { return ex.getMessage().contains(AUTHENTICATION_CHALLENGE); } + + @Override + public void handleConfigurationUpdate(Map configurationParameters) { + super.handleConfigurationUpdate(configurationParameters); + if (configurationParameters.containsKey("email")) { + thingConfig.setEmail(configurationParameters.get("email").toString()); + } + if (configurationParameters.containsKey("password")) { + thingConfig.setPassword(configurationParameters.get("password").toString()); + } + } } diff --git a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaWindowHandler.java b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaWindowHandler.java index 27ff3e4487800..6f3180b6d72e9 100644 --- a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaWindowHandler.java +++ b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/handler/SomfyTahomaWindowHandler.java @@ -12,8 +12,6 @@ */ package org.openhab.binding.somfytahoma.internal.handler; -import static org.openhab.binding.somfytahoma.internal.SomfyTahomaBindingConstants.*; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; @@ -22,6 +20,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.openhab.binding.somfytahoma.internal.SomfyTahomaBindingConstants.*; + /** * The {@link SomfyTahomaWindowHandler} is responsible for handling commands, * which are sent to one of the channels of the window thing. @@ -48,19 +48,17 @@ public void handleCommand(ChannelUID channelUID, Command command) { updateChannelState(channelUID); } else { String cmd = getTahomaCommand(command.toString()); - //Check if the rollershutter is not moving - String executionId = getCurrentExecutions(); - if (executionId != null) { - //STOP command should be interpreted if rollershutter is moving - //otherwise do nothing - if (COMMAND_STOP.equals(cmd)) { + if (COMMAND_STOP.equals(cmd)) { + //Check if the window is not moving + String executionId = getCurrentExecutions(); + if (executionId != null) { + //STOP command should be interpreted if window is moving + //otherwise do nothing cancelExecution(executionId); } } else { - if (!cmd.equals(COMMAND_STOP)) { - String param = COMMAND_SET_CLOSURE.equals(cmd) ? "[" + command.toString() + "]" : "[]"; - sendCommand(cmd, param); - } + String param = COMMAND_SET_CLOSURE.equals(cmd) ? "[" + command.toString() + "]" : "[]"; + sendCommand(cmd, param); } } } diff --git a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/model/SomfyTahomaLoginResponse.java b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/model/SomfyTahomaLoginResponse.java index a55a9f0a53e65..d7772046d223d 100644 --- a/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/model/SomfyTahomaLoginResponse.java +++ b/bundles/org.openhab.binding.somfytahoma/src/main/java/org/openhab/binding/somfytahoma/internal/model/SomfyTahomaLoginResponse.java @@ -25,6 +25,7 @@ public class SomfyTahomaLoginResponse { private boolean success; private String version = ""; + private String error = ""; public boolean isSuccess() { return success; @@ -33,4 +34,8 @@ public boolean isSuccess() { public String getVersion() { return version; } + + public String getError() { + return error; + } } diff --git a/bundles/org.openhab.binding.sonyaudio/src/main/java/org/openhab/binding/sonyaudio/internal/protocol/SonyAudioClientSocket.java b/bundles/org.openhab.binding.sonyaudio/src/main/java/org/openhab/binding/sonyaudio/internal/protocol/SonyAudioClientSocket.java index 8e26f37111bb8..4143593af2d1c 100644 --- a/bundles/org.openhab.binding.sonyaudio/src/main/java/org/openhab/binding/sonyaudio/internal/protocol/SonyAudioClientSocket.java +++ b/bundles/org.openhab.binding.sonyaudio/src/main/java/org/openhab/binding/sonyaudio/internal/protocol/SonyAudioClientSocket.java @@ -24,6 +24,7 @@ import org.eclipse.jetty.websocket.api.Session; 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; @@ -191,6 +192,11 @@ public void onClose(int statusCode, String reason) { } }); } + + @OnWebSocketError + public void onError(Throwable error) { + onClose(0, error.getMessage()); + } } private void sendMessage(String str) throws IOException { diff --git a/bundles/org.openhab.binding.tellstick/pom.xml b/bundles/org.openhab.binding.tellstick/pom.xml index 35695560ce5e7..b86b2d782036f 100644 --- a/bundles/org.openhab.binding.tellstick/pom.xml +++ b/bundles/org.openhab.binding.tellstick/pom.xml @@ -38,9 +38,9 @@ provided - javax.activation - activation - 1.1.1 + org.apache.servicemix.specs + org.apache.servicemix.specs.activation-api-1.1 + 2.9.0 provided diff --git a/bundles/org.openhab.binding.tplinksmarthome/.classpath b/bundles/org.openhab.binding.tplinksmarthome/.classpath index a5d95095ccaaf..7737f39c03922 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/.classpath +++ b/bundles/org.openhab.binding.tplinksmarthome/.classpath @@ -13,11 +13,12 @@ + - + diff --git a/bundles/org.openhab.binding.tplinksmarthome/README.md b/bundles/org.openhab.binding.tplinksmarthome/README.md index 7050ee6fb8a03..51d2a96c441c7 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/README.md +++ b/bundles/org.openhab.binding.tplinksmarthome/README.md @@ -26,7 +26,10 @@ The following TP-Link Smart Devices are supported: ### HS107 Smart Wi-Fi Plug, 2-Outlets -Not supported yet. +* Switch On/Off Group +* Switch On/Off Outlets +* Led On/Off +* Wi-Fi signal strength (rssi) ### HS110 Smart Wi-Fi Plug @@ -64,7 +67,11 @@ Switching via openHAB activates the switch directly. ### HS300 Smart Wi-Fi Power Strip -Not supported yet. +* Switch On/Off Group +* Switch On/Off Outlets +* Energy readings Outlets +* Led On/Off +* Wi-Fi signal strength (rssi) ### KB100 Kasa Smart Light Bulb @@ -94,11 +101,17 @@ Switching, Brightness and Color is done using the `color` channel. ### KP200 Smart Wi-Fi Power Outlet, 2-Sockets -Not supported yet. +* Switch On/Off Group +* Switch On/Off Outlets +* Led On/Off +* Wi-Fi signal strength (rssi) ### KP400 Smart Outdoor Plug -Not supported yet. +* Switch On/Off Group +* Switch On/Off Outlets +* Led On/Off +* Wi-Fi signal strength (rssi) ### LB100 Smart Wi-Fi LED Bulb with Dimmable Light @@ -241,33 +254,44 @@ Either `deviceId` or `ipAddress` must be set. All devices support some of the following channels: -| Channel Type ID | Item Type | Description | Thing types supporting this channel | -|------------------|-----------|----------------------------------------------------|-----------------------------------------------------------------| -| switch | Switch | Switch the Smart Home device on or off. | HS100, HS103, HS105, HS110, HS200, HS210, KP100, RE270K, RE370K | -| brightness | Dimmer | Set the brightness of Smart Home device or dimmer. | HS220, KB100, KL110, KL120, LB100, LB110, LB120, LB200 | -| colorTemperature | Dimmer | Set the color temperature of Smart Home light. | KB130, KL120, KL130, LB120, LB130, LB230 | -| color | Color | Set the color of the Smart Home light. | KB130, KL130, LB130, LB230 | -| power | Number | Actual energy usage in Watt. | HS110, KLxxx, LBxxx | -| eneryUsage | Number | Energy Usage in kWh. | HS110 | -| current | Number | Actual current usage in Ampere. | HS110 | -| voltage | Number | Actual voltage usage in Volt. | HS110 | -| led | Switch | Switch the status led on the device on or off. | HS100, HS103, HS105, HS110, HS200, HS210, HS220, KP100 | -| rssi | Number | Wi-Fi signal strength indicator in dBm. | All | +| Channel Type ID | Item Type | Description | Thing types supporting this channel | +|------------------|-----------|----------------------------------------------------|---------------------------------------------------------------------------------------------| +| switch | Switch | Switch the Smart Home device on or off. | HS100, HS103, HS105, HS107, HS110, HS200, HS210, HS300, KP100, KP200, KP400, RE270K, RE370K | +| brightness | Dimmer | Set the brightness of Smart Home device or dimmer. | HS220, KB100, KL110, KL120, LB100, LB110, LB120, LB200 | +| colorTemperature | Dimmer | Set the color temperature of Smart Home light. | KB130, KL120, KL130, LB120, LB130, LB230 | +| color | Color | Set the color of the Smart Home light. | KB130, KL130, LB130, LB230 | +| power | Number | Actual energy usage in Watt. | HS110, HS300, KLxxx, LBxxx | +| eneryUsage | Number | Energy Usage in kWh. | HS110, HS300 | +| current | Number | Actual current usage in Ampere. | HS110, HS300 | +| voltage | Number | Actual voltage usage in Volt. | HS110, HS300 | +| led | Switch | Switch the status led on the device on or off. | HS100, HS103, HS105, HS107, HS110, HS200, HS210, HS220, HS300, KP100, KP200, KP400 | +| rssi | Number | Wi-Fi signal strength indicator in dBm. | All | + +The outlet devices (HS107, HS300, KP200, KP400) have group channels. +This means the channel is prefixed with the group id. +The following group ids are available: + +| Group ID | Description | +|-------------------|-------------------------------------------------------------------------------------------------------| +| groupSwitch | General channels. e.g. `groupSwitch#switch` | +| outlet<number> | The outlet to control. <number> is the number of the outlet (starts with 1). e.g. `outlet1#switch` | ## Full Example ### tplinksmarthome.things: ``` -tplinksmarthome:hs100:tv "Living Room" [ deviceId="00000000000000000000000000000001", refresh=60 ] -tplinksmarthome:lb110:bulb1 "Living Room Bulb 1" [ deviceId="00000000000000000000000000000002", refresh=60, transitionPeriod=2500 ] -tplinksmarthome:lb130:bulb2 "Living Room Bulb 2" [ deviceId="00000000000000000000000000000003", refresh=60, transitionPeriod=2500 ] +tplinksmarthome:hs100:tv "TV" [ deviceId="00000000000000000000000000000001", refresh=60 ] +tplinksmarthome:hs300:laptop "Laptop" [ deviceId="00000000000000000000000000000004", refresh=60 ] +tplinksmarthome:lb110:bulb1 "Living Room Bulb 1" [ deviceId="00000000000000000000000000000002", refresh=60, transitionPeriod=2500 ] +tplinksmarthome:lb130:bulb2 "Living Room Bulb 2" [ deviceId="00000000000000000000000000000003", refresh=60, transitionPeriod=2500 ] ``` ### tplinksmarthome.items: ``` -Switch TP_L_Switch "Switch" { channel="tplinksmarthome:hs100:tv:switch" } +Switch TP_L_TV "TV" { channel="tplinksmarthome:hs100:tv:switch" } +Switch TP_L_Laptop "Laptop" { channel="tplinksmarthome:hs300:laptop:outlet1#switch" } Number TP_L_RSSI "Signal [%d] dB" { channel="tplinksmarthome:hs100:tv:rssi" } Dimmer TP_LB_Bulb "Dimmer [%d %%]" { channel="tplinksmarthome:lb110:bulb1:brightness" } Dimmer TP_LB_ColorT "Color Temperature [%d] %%" { channel="tplinksmarthome:lb130:bulb2:colorTemperature" } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Commands.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Commands.java index 16037e3e2b250..59594c3b437f1 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Commands.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Commands.java @@ -13,6 +13,7 @@ package org.openhab.binding.tplinksmarthome.internal; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.HSBType; import org.eclipse.smarthome.core.library.types.OnOffType; import org.openhab.binding.tplinksmarthome.internal.model.GetRealtime; @@ -43,10 +44,11 @@ @NonNullByDefault public class Commands { + private static final String CONTEXT = "{\"context\":{\"child_ids\":[\"%s\"]},"; private static final String SYSTEM_GET_SYSINFO = "\"system\":{\"get_sysinfo\":{}}"; private static final String GET_SYSINFO = "{" + SYSTEM_GET_SYSINFO + "}"; - private static final String GET_REALTIME_AND_SYSINFO = "{" + SYSTEM_GET_SYSINFO - + ", \"emeter\":{\"get_realtime\":{}}}"; + private static final String REALTIME = "\"emeter\":{\"get_realtime\":{}}"; + private static final String GET_REALTIME_AND_SYSINFO = "{" + SYSTEM_GET_SYSINFO + ", " + REALTIME + "}"; private static final String GET_REALTIME_BULB_AND_SYSINFO = "{" + SYSTEM_GET_SYSINFO + ", \"smartlife.iot.common.emeter\":{\"get_realtime\":{}}}"; @@ -71,6 +73,16 @@ public static String getRealtimeBulbAndSysinfo() { return GET_REALTIME_BULB_AND_SYSINFO; } + /** + * Returns the json to get the energy and sys info data from an outlet device. + * + * @param id optional id of the device + * @return The json string of the command to send to the device + */ + public static String getRealtimeWithContext(String id) { + return String.format(CONTEXT, id) + REALTIME + "}"; + } + /** * Returns the json response of the get_realtime command to the data object. * @@ -108,11 +120,15 @@ public Sysinfo getSysinfoReponse(String getSysinfoReponse) { * Returns the json for the set_relay_state command to switch on or off. * * @param onOff the switch state to set + * @param childId optional child id if multiple children are supported by a single device * @return The json string of the command to send to the device */ - public String setRelayState(OnOffType onOff) { + public String setRelayState(OnOffType onOff, @Nullable String childId) { SetRelayState relayState = new SetRelayState(); relayState.setRelayState(onOff); + if (childId != null) { + relayState.setChildId(childId); + } return gsonWithExpose.toJson(relayState); } @@ -122,7 +138,7 @@ public String setRelayState(OnOffType onOff) { * @param relayStateResponse the json string * @return The data object containing the state data from the json string */ - public SetRelayState setRelayStateResponse(String relayStateResponse) { + public @Nullable SetRelayState setRelayStateResponse(String relayStateResponse) { return gsonWithExpose.fromJson(relayStateResponse, SetRelayState.class); } @@ -144,7 +160,7 @@ public String setSwitchState(OnOffType onOff) { * @param switchStateResponse the json string * @return The data object containing the state data from the json string */ - public SetSwitchState setSwitchStateResponse(String switchStateResponse) { + public @Nullable SetSwitchState setSwitchStateResponse(String switchStateResponse) { return gsonWithExpose.fromJson(switchStateResponse, SetSwitchState.class); } @@ -166,7 +182,7 @@ public String setDimmerBrightness(int brightness) { * @param dimmerBrightnessResponse the json string * @return The data object containing the state data from the json string */ - public HasErrorResponse setDimmerBrightnessResponse(String dimmerBrightnessResponse) { + public @Nullable HasErrorResponse setDimmerBrightnessResponse(String dimmerBrightnessResponse) { return gsonWithExpose.fromJson(dimmerBrightnessResponse, SetBrightness.class); } @@ -190,11 +206,15 @@ public String setLightState(OnOffType onOff, int transitionPeriod) { * Returns the json for the set_led_off command to switch the led of the device on or off. * * @param onOff the led state to set + * @param childId optional child id if multiple children are supported by a single device * @return The json string of the command to send to the device */ - public String setLedOn(OnOffType onOff) { + public String setLedOn(OnOffType onOff, @Nullable String childId) { SetLedOff sLOff = new SetLedOff(); sLOff.setLed(onOff); + if (childId != null) { + sLOff.setChildId(childId); + } return gsonWithExpose.toJson(sLOff); } @@ -204,7 +224,7 @@ public String setLedOn(OnOffType onOff) { * @param setLedOnResponse the json string * @return The data object containing the data from the json string */ - public SetLedOff setLedOnResponse(String setLedOnResponse) { + public @Nullable SetLedOff setLedOnResponse(String setLedOnResponse) { return gsonWithExpose.fromJson(setLedOnResponse, SetLedOff.class); } @@ -268,7 +288,7 @@ public String setColorTemperature(int colorTemperature, int transitionPeriod) { * @param response the json string * @return The data object containing the state data from the json string */ - public TransitionLightStateResponse setTransitionLightStateResponse(String response) { + public @Nullable TransitionLightStateResponse setTransitionLightStateResponse(String response) { return gson.fromJson(response, TransitionLightStateResponse.class); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Connection.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Connection.java index a734c4e3afb1d..76076d84758c8 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Connection.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/Connection.java @@ -67,7 +67,7 @@ public String sendCommand(String command) throws IOException { logger.trace("Executing command: {}", command); try (Socket socket = createSocket(); final OutputStream outputStream = socket.getOutputStream()) { outputStream.write(CryptUtil.encryptWithLength(command)); - String response = readReturnValue(socket); + final String response = readReturnValue(socket); logger.trace("Command response: {}", response); return response; diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeBindingConstants.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeBindingConstants.java index 728d3524ebe6f..7354234415488 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeBindingConstants.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeBindingConstants.java @@ -29,7 +29,7 @@ public final class TPLinkSmartHomeBindingConstants { public static final String BINDING_ID = "tplinksmarthome"; - // List of all channel ids + // List of all switch channel ids public static final String CHANNEL_SWITCH = "switch"; // List of all plug channel ids @@ -60,6 +60,10 @@ public final class TPLinkSmartHomeBindingConstants { // List of all misc channel ids public static final String CHANNEL_RSSI = "rssi"; + // List of all group channel ids + public static final String CHANNEL_SWITCH_GROUP = "group"; + public static final String CHANNEL_OUTLET_GROUP_PREFIX = "outlet"; + // List of configuration keys public static final String CONFIG_IP = "ipAddress"; public static final String CONFIG_DEVICE_ID = "deviceId"; diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeDiscoveryService.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeDiscoveryService.java index a7235a0a6efcb..e8656a7df83b6 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeDiscoveryService.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeDiscoveryService.java @@ -48,7 +48,7 @@ * @author Hilbrand Bouwkamp - Complete make-over, reorganized code and code cleanup. */ @Component(service = { DiscoveryService.class, - TPLinkIpAddressService.class }, immediate = true, configurationPid = "discovery.tplinksmarthome") +TPLinkIpAddressService.class }, immediate = true, configurationPid = "discovery.tplinksmarthome") @NonNullByDefault public class TPLinkSmartHomeDiscoveryService extends AbstractDiscoveryService implements TPLinkIpAddressService { @@ -69,7 +69,7 @@ public class TPLinkSmartHomeDiscoveryService extends AbstractDiscoveryService im public TPLinkSmartHomeDiscoveryService() throws UnknownHostException { super(SUPPORTED_THING_TYPES, DISCOVERY_TIMEOUT_SECONDS); InetAddress broadcast = InetAddress.getByName(BROADCAST_IP); - byte[] discoverbuffer = CryptUtil.encrypt(Commands.getSysinfo()); + final byte[] discoverbuffer = CryptUtil.encrypt(Commands.getSysinfo()); discoverPacket = new DatagramPacket(discoverbuffer, discoverbuffer.length, broadcast, Connection.TP_LINK_SMART_HOME_PORT); } @@ -106,7 +106,7 @@ protected void startScan() { if (discoverSocket == null) { break; } - DatagramPacket packet = new DatagramPacket(buffer, buffer.length); + final DatagramPacket packet = new DatagramPacket(buffer, buffer.length); discoverSocket.receive(packet); logger.debug("TP-Link Smart device discovery returned package with length {}", packet.getLength()); @@ -139,7 +139,8 @@ protected void stopScan() { * @throws IOException exception in case sending the packet failed */ protected DatagramSocket sendDiscoveryPacket() throws IOException { - DatagramSocket ds = new DatagramSocket(null); + final DatagramSocket ds = new DatagramSocket(null); + ds.setBroadcast(true); ds.setSoTimeout(UDP_PACKET_TIMEOUT_MS); ds.send(discoverPacket); @@ -165,24 +166,25 @@ private void closeDiscoverSocket() { * @throws IOException in case decrypting of the data failed */ private void detectThing(DatagramPacket packet) throws IOException { - String ipAddress = packet.getAddress().getHostAddress(); - String rawData = CryptUtil.decrypt(packet.getData(), packet.getLength()); - Sysinfo sysinfoRaw = commands.getSysinfoReponse(rawData); - Sysinfo sysinfo = sysinfoRaw.getActualSysinfo(); + final String ipAddress = packet.getAddress().getHostAddress(); + final String rawData = CryptUtil.decrypt(packet.getData(), packet.getLength()); + final Sysinfo sysinfoRaw = commands.getSysinfoReponse(rawData); + final Sysinfo sysinfo = sysinfoRaw.getActualSysinfo(); logger.trace("Detected TP-Link Smart Home device: {}", rawData); - String deviceId = sysinfo.getDeviceId(); + final String deviceId = sysinfo.getDeviceId(); logger.debug("TP-Link Smart Home device '{}' with id {} found on {} ", sysinfo.getAlias(), deviceId, ipAddress); idInetAddressCache.put(deviceId, ipAddress); - Optional thingTypeUID = getThingTypeUID(sysinfo.getModel()); + final Optional thingTypeUID = getThingTypeUID(sysinfo.getModel()); if (thingTypeUID.isPresent()) { - ThingUID thingUID = new ThingUID(thingTypeUID.get(), + final ThingUID thingUID = new ThingUID(thingTypeUID.get(), deviceId.substring(deviceId.length() - 6, deviceId.length())); - Map properties = PropertiesCollector.collectProperties(thingTypeUID.get(), ipAddress, + final Map properties = PropertiesCollector.collectProperties(thingTypeUID.get(), ipAddress, sysinfoRaw); - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(sysinfo.getAlias()) - .withRepresentationProperty(deviceId).withProperties(properties).build(); + final DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) + .withLabel(sysinfo.getAlias()).withRepresentationProperty(deviceId).withProperties(properties) + .build(); thingDiscovered(discoveryResult); } else { logger.debug("Detected, but ignoring unsupported TP-Link Smart Home device model '{}'", sysinfo.getModel()); @@ -196,7 +198,7 @@ private void detectThing(DatagramPacket packet) throws IOException { * @return {@link ThingTypeUID} or null if device not recognized */ private Optional getThingTypeUID(String model) { - String modelLC = model.toLowerCase(Locale.ENGLISH); + final String modelLC = model.toLowerCase(Locale.ENGLISH); return SUPPORTED_THING_TYPES.stream().filter(suid -> modelLC.startsWith(suid.getId())).findFirst(); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeHandlerFactory.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeHandlerFactory.java index e25ebe580e3fb..5bb9eecfebea4 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeHandlerFactory.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeHandlerFactory.java @@ -25,6 +25,7 @@ import org.openhab.binding.tplinksmarthome.internal.device.BulbDevice; import org.openhab.binding.tplinksmarthome.internal.device.DimmerDevice; import org.openhab.binding.tplinksmarthome.internal.device.EnergySwitchDevice; +import org.openhab.binding.tplinksmarthome.internal.device.PowerStripDevice; import org.openhab.binding.tplinksmarthome.internal.device.RangeExtenderDevice; import org.openhab.binding.tplinksmarthome.internal.device.SmartHomeDevice; import org.openhab.binding.tplinksmarthome.internal.device.SwitchDevice; @@ -52,8 +53,8 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { @Nullable @Override protected ThingHandler createHandler(Thing thing) { - ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - SmartHomeDevice device; + final ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + final SmartHomeDevice device; if (HS110.is(thingTypeUID)) { device = new EnergySwitchDevice(); @@ -63,6 +64,8 @@ protected ThingHandler createHandler(Thing thing) { device = new BulbDevice(thingTypeUID, COLOR_TEMPERATURE_LB130_MIN, COLOR_TEMPERATURE_LB130_MAX); } else if (LB120.is(thingTypeUID) || KL120.is(thingTypeUID)) { device = new BulbDevice(thingTypeUID, COLOR_TEMPERATURE_LB120_MIN, COLOR_TEMPERATURE_LB120_MAX); + } else if (TPLinkSmartHomeThingType.isStripDevice(thingTypeUID)) { + device = new PowerStripDevice(thingTypeUID); } else if (TPLinkSmartHomeThingType.isSwitchingDevice(thingTypeUID)) { device = new SwitchDevice(); } else if (TPLinkSmartHomeThingType.isBulbDevice(thingTypeUID)) { diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeThingType.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeThingType.java index 354e94bd27c2d..5e9a807c131cb 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeThingType.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/TPLinkSmartHomeThingType.java @@ -27,7 +27,7 @@ * */ @NonNullByDefault -enum TPLinkSmartHomeThingType { +public enum TPLinkSmartHomeThingType { // Bulb Thing Type UIDs KB100("kb100", DeviceType.BULB), @@ -56,6 +56,12 @@ enum TPLinkSmartHomeThingType { // Dimmer Thing Type UIDs HS220("hs220", DeviceType.DIMMER), + // Power Strip Thing Type UIDs. + HS107("hs107", DeviceType.STRIP), + HS300("hs300", DeviceType.STRIP), + KP200("kp200", DeviceType.STRIP), + KP400("kp400", DeviceType.STRIP), + // Range Extender Thing Type UIDs RE270K("re270", DeviceType.RANGE_EXTENDER), RE370K("re370", DeviceType.RANGE_EXTENDER); @@ -99,26 +105,38 @@ public static boolean isBulbDevice(ThingTypeUID thingTypeUID) { } /** - * Returns true if the given {@link ThingTypeUID} matches a device that supports the switching communication + * Returns true if the given {@link ThingTypeUID} matches a device that is a range extender. + * + * @param thingTypeUID if the check + * @return true if it's a range extender + */ + public static boolean isRangeExtenderDevice(ThingTypeUID thingTypeUID) { + return SUPPORTED_THING_TYPES_LIST.stream().filter(t -> t.is(thingTypeUID)) + .anyMatch(t -> t.type == DeviceType.RANGE_EXTENDER); + } + + /** + * Returns true if the given {@link ThingTypeUID} matches a device that supports the power strip communication * protocol. * * @param thingTypeUID if the check - * @return true if it's a switching supporting device + * @return true if it's a power strip supporting device */ - public static boolean isSwitchingDevice(ThingTypeUID thingTypeUID) { + public static boolean isStripDevice(ThingTypeUID thingTypeUID) { return SUPPORTED_THING_TYPES_LIST.stream().filter(t -> t.is(thingTypeUID)) - .anyMatch(t -> t.type == DeviceType.PLUG || t.type == DeviceType.SWITCH); + .anyMatch(t -> t.type == DeviceType.STRIP); } /** - * Returns true if the given {@link ThingTypeUID} matches a device that is a range extender. + * Returns true if the given {@link ThingTypeUID} matches a device that supports the switching communication + * protocol. * * @param thingTypeUID if the check - * @return true if it's a range extender + * @return true if it's a switching supporting device */ - public static boolean isRangeExtenderDevice(ThingTypeUID thingTypeUID) { + public static boolean isSwitchingDevice(ThingTypeUID thingTypeUID) { return SUPPORTED_THING_TYPES_LIST.stream().filter(t -> t.is(thingTypeUID)) - .anyMatch(t -> t.type == DeviceType.RANGE_EXTENDER); + .anyMatch(t -> t.type == DeviceType.PLUG || t.type == DeviceType.SWITCH); } /** @@ -151,6 +169,10 @@ private enum DeviceType { * Wi-Fi range extender device with plug. */ RANGE_EXTENDER, + /** + * Power strip device. + */ + STRIP, /** * Switch device. */ diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDevice.java index 20b05acf52c4c..186999258a163 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDevice.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDevice.java @@ -22,13 +22,12 @@ 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.thing.ChannelUID; 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.openhab.binding.tplinksmarthome.internal.Commands; -import org.openhab.binding.tplinksmarthome.internal.Connection; -import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration; import org.openhab.binding.tplinksmarthome.internal.model.HasErrorResponse; import org.openhab.binding.tplinksmarthome.internal.model.LightState; import org.openhab.binding.tplinksmarthome.internal.model.TransitionLightStateResponse; @@ -63,17 +62,17 @@ public String getUpdateCommand() { } @Override - public boolean handleCommand(String channelID, Connection connection, Command command, - TPLinkSmartHomeConfiguration configuration) throws IOException { - int transitionPeriod = configuration.transitionPeriod; - HasErrorResponse response; + public boolean handleCommand(ChannelUID channelUid, Command command) throws IOException { + final String channelId = channelUid.getId(); + final int transitionPeriod = configuration.transitionPeriod; + final HasErrorResponse response; if (command instanceof OnOffType) { - response = handleOnOffType(channelID, connection, (OnOffType) command, transitionPeriod); + response = handleOnOffType(channelId, (OnOffType) command, transitionPeriod); } else if (command instanceof HSBType) { - response = handleHSBType(channelID, connection, (HSBType) command, transitionPeriod); + response = handleHSBType(channelId, (HSBType) command, transitionPeriod); } else if (command instanceof DecimalType) { - response = handleDecimalType(channelID, connection, (DecimalType) command, transitionPeriod); + response = handleDecimalType(channelId, (DecimalType) command, transitionPeriod); } else { return false; } @@ -81,9 +80,8 @@ public boolean handleCommand(String channelID, Connection connection, Command co return response != null; } - @Nullable - private HasErrorResponse handleOnOffType(String channelID, Connection connection, OnOffType onOff, - int transitionPeriod) throws IOException { + private @Nullable HasErrorResponse handleOnOffType(String channelID, OnOffType onOff, int transitionPeriod) + throws IOException { if (CHANNELS_BULB_SWITCH.contains(channelID)) { return commands.setTransitionLightStateResponse( connection.sendCommand(commands.setLightState(onOff, transitionPeriod))); @@ -91,27 +89,25 @@ private HasErrorResponse handleOnOffType(String channelID, Connection connection return null; } - @Nullable - private HasErrorResponse handleDecimalType(String channelID, Connection connection, DecimalType command, - int transitionPeriod) throws IOException { + private @Nullable HasErrorResponse handleDecimalType(String channelID, DecimalType command, int transitionPeriod) + throws IOException { if (CHANNEL_COLOR.equals(channelID) || CHANNEL_BRIGHTNESS.equals(channelID)) { return commands.setTransitionLightStateResponse( connection.sendCommand(commands.setBrightness(command.intValue(), transitionPeriod))); } else if (CHANNEL_COLOR_TEMPERATURE.equals(channelID)) { - return handleColorTemperature(connection, convertPercentageToKelvin(command.intValue()), transitionPeriod); + return handleColorTemperature(convertPercentageToKelvin(command.intValue()), transitionPeriod); } return null; } - private TransitionLightStateResponse handleColorTemperature(Connection connection, int colorTemperature, - int transitionPeriod) throws IOException { + private @Nullable TransitionLightStateResponse handleColorTemperature(int colorTemperature, int transitionPeriod) + throws IOException { return commands.setTransitionLightStateResponse( connection.sendCommand(commands.setColorTemperature(colorTemperature, transitionPeriod))); } @Nullable - private HasErrorResponse handleHSBType(String channelID, Connection connection, HSBType command, - int transitionPeriod) throws IOException { + private HasErrorResponse handleHSBType(String channelID, HSBType command, int transitionPeriod) throws IOException { if (CHANNEL_COLOR.equals(channelID)) { return commands.setTransitionLightStateResponse( connection.sendCommand(commands.setColor(command, transitionPeriod))); @@ -120,11 +116,11 @@ private HasErrorResponse handleHSBType(String channelID, Connection connection, } @Override - public State updateChannel(String channelId, DeviceState deviceState) { - LightState lightState = deviceState.getSysinfo().getLightState(); + public State updateChannel(ChannelUID channelUid, DeviceState deviceState) { + final LightState lightState = deviceState.getSysinfo().getLightState(); final State state; - switch (channelId) { + switch (channelUid.getId()) { case CHANNEL_BRIGHTNESS: state = lightState.getBrightness(); break; diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDevice.java index 3541366ad74f1..b2566119c30ce 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDevice.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDevice.java @@ -20,10 +20,9 @@ import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.PercentType; +import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.tplinksmarthome.internal.Connection; -import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration; import org.openhab.binding.tplinksmarthome.internal.model.HasErrorResponse; /** @@ -35,33 +34,30 @@ public class DimmerDevice extends SwitchDevice { @Override - protected @Nullable HasErrorResponse setOnOffState(Connection connection, OnOffType onOff) throws IOException { + protected @Nullable HasErrorResponse setOnOffState(ChannelUID channelUid, OnOffType onOff) throws IOException { return commands.setSwitchStateResponse(connection.sendCommand(commands.setSwitchState(onOff))); } @Override - public boolean handleCommand(String channelId, Connection connection, Command command, - TPLinkSmartHomeConfiguration configuration) throws IOException { - return CHANNEL_BRIGHTNESS.equals(channelId) - ? handleBrightnessChannel(channelId, connection, command, configuration) - : super.handleCommand(channelId, connection, command, configuration); + public boolean handleCommand(ChannelUID channelUid, Command command) throws IOException { + return CHANNEL_BRIGHTNESS.equals(channelUid.getId()) ? handleBrightnessChannel(channelUid, command) + : super.handleCommand(channelUid, command); } /** * Handle the brightness channel. Because the device has different commands for setting the device on/off and * setting the brightness the on/off command must be send to the device as well when the brightness. * - * @param connection Connection to use + * @param channelUid uid of the channel to handle * @param command command to the send * @return returns true if the command was handled * @throws IOException throws an {@link IOException} if the command handling failed */ - private boolean handleBrightnessChannel(String channelId, Connection connection, Command command, - TPLinkSmartHomeConfiguration configuration) throws IOException { + private boolean handleBrightnessChannel(ChannelUID channelUid, Command command) throws IOException { HasErrorResponse response = null; if (command instanceof OnOffType) { - response = setOnOffState(connection, (OnOffType) command); + response = setOnOffState(channelUid, (OnOffType) command); } else if (command instanceof PercentType) { PercentType percentCommand = (PercentType) command; @@ -70,7 +66,7 @@ private boolean handleBrightnessChannel(String channelId, Connection connection, response = commands.setDimmerBrightnessResponse( connection.sendCommand(commands.setDimmerBrightness(percentCommand.intValue()))); } else { - response = setOnOffState(connection, OnOffType.OFF); + response = setOnOffState(channelUid, OnOffType.OFF); } } checkErrors(response); @@ -78,12 +74,12 @@ private boolean handleBrightnessChannel(String channelId, Connection connection, } @Override - public State updateChannel(String channelId, DeviceState deviceState) { - if (CHANNEL_BRIGHTNESS.equals(channelId)) { + public State updateChannel(ChannelUID channelUid, DeviceState deviceState) { + if (CHANNEL_BRIGHTNESS.equals(channelUid.getId())) { return deviceState.getSysinfo().getRelayState() == OnOffType.OFF ? PercentType.ZERO : new PercentType(deviceState.getSysinfo().getBrightness()); } else { - return super.updateChannel(channelId, deviceState); + return super.updateChannel(channelUid, deviceState); } } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDevice.java index 33701d3cec2cd..8849b548acbe8 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDevice.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDevice.java @@ -16,6 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.State; import org.openhab.binding.tplinksmarthome.internal.Commands; import org.openhab.binding.tplinksmarthome.internal.model.Realtime; @@ -34,18 +35,26 @@ public String getUpdateCommand() { } @Override - public State updateChannel(String channelId, DeviceState deviceState) { + public State updateChannel(ChannelUID channelUid, DeviceState deviceState) { final State state; + final String matchChannelId = channelUid.isInGroup() ? channelUid.getIdWithoutGroup() : channelUid.getId(); - if (CHANNELS_ENERGY.contains(channelId)) { - state = updateEnergyChannel(channelId, deviceState.getRealtime()); + if (CHANNELS_ENERGY.contains(matchChannelId)) { + state = updateEnergyChannel(matchChannelId, deviceState.getRealtime()); } else { - state = super.updateChannel(channelId, deviceState); + state = super.updateChannel(channelUid, deviceState); } return state; } - private State updateEnergyChannel(String channelId, Realtime realtime) { + /** + * Gets the state for an energy channel. + * + * @param channelId Id of the energy channel to get the state + * @param realtime data object containing the data from the device + * @return state object for the given channel + */ + protected State updateEnergyChannel(String channelId, Realtime realtime) { final double value; switch (channelId) { diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDevice.java new file mode 100644 index 0000000000000..640991ebd639c --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDevice.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.tplinksmarthome.internal.device; + +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.HS300; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.ThingTypeUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.tplinksmarthome.internal.Commands; +import org.openhab.binding.tplinksmarthome.internal.model.Realtime; +import org.openhab.binding.tplinksmarthome.internal.model.Sysinfo.Outlet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * TP-Link Smart Home device with multiple sockets, like the HS107 and HS300. + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public class PowerStripDevice extends EnergySwitchDevice { + + private final Logger logger = LoggerFactory.getLogger(PowerStripDevice.class); + + private final List<@Nullable Realtime> realTimeCacheList; + private final List<@Nullable String> childIds; + + public PowerStripDevice(ThingTypeUID thingTypeUID) { + final int nrOfSockets; + + if (HS300.is(thingTypeUID)) { + nrOfSockets = 6; + } else { + nrOfSockets = 2; + } + realTimeCacheList = new ArrayList<>(nrOfSockets); + childIds = new ArrayList<>(Collections.nCopies(nrOfSockets, "")); + } + + @Override + public String getUpdateCommand() { + return Commands.getSysinfo(); + } + + @Override + public void refreshedDeviceState(@Nullable DeviceState deviceState) { + if (deviceState != null) { + for (int i = 0; i < childIds.size(); i++) { + childIds.set(i, deviceState.getSysinfo().getChildren().get(i).getId()); + realTimeCacheList.add(i, refreshCache(i)); + } + } + } + + @Override + public State updateChannel(ChannelUID channelUid, DeviceState deviceState) { + final int idx = channelToIndex(channelUid); + + if (idx >= 0 && idx < childIds.size()) { + final Outlet outlet = deviceState.getSysinfo().getChildren().get(idx); + final String baseChannel = channelUid.getIdWithoutGroup(); + + if (CHANNEL_SWITCH.equals(baseChannel)) { + return outlet.getState(); + } else if (CHANNELS_ENERGY.contains(baseChannel)) { + final Realtime realTime = realTimeCacheList.get(idx); + + return realTime == null ? UnDefType.UNDEF : updateEnergyChannel(baseChannel, realTime); + } + } else { + if (idx >= 0) { + logger.debug("For channel update the index '{}' could be mapped to a channel. passed channel: {}", idx, + channelUid); + } + } + return super.updateChannel(channelUid, deviceState); + } + + @Override + protected @Nullable String getChildId(ChannelUID channelUid) { + final int idx = channelToIndex(channelUid); + + return idx >= 0 && idx < childIds.size() ? childIds.get(idx) : null; + } + + private int channelToIndex(final ChannelUID channelUid) { + final String groupId = channelUid.getGroupId(); + + return (groupId != null && groupId.startsWith(CHANNEL_OUTLET_GROUP_PREFIX) + ? Integer.parseInt(groupId.substring(CHANNEL_OUTLET_GROUP_PREFIX.length())) + : 0) - 1; + } + + private @Nullable Realtime refreshCache(int idx) { + try { + final String childId = childIds.get(idx); + + return childId == null ? null + : commands.getRealtimeResponse(connection.sendCommand(Commands.getRealtimeWithContext(childId))); + } catch (IOException e) { + return null; + } catch (RuntimeException e) { + logger.debug( + "Obtaining realtime data for channel-'{}' unexpectedly crashed. If this keeps happening please report: ", + idx, e); + return null; + } + } +} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDevice.java index be684e6335e77..7956067ea0f8c 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDevice.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDevice.java @@ -14,12 +14,11 @@ import java.io.IOException; -import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.State; -import org.openhab.binding.tplinksmarthome.internal.Connection; import org.openhab.binding.tplinksmarthome.internal.model.SetRelayState; /** @@ -36,7 +35,7 @@ protected State getOnOffState(DeviceState deviceState) { } @Override - protected @Nullable SetRelayState setOnOffState(@NonNull Connection connection, @NonNull OnOffType onOff) + protected @Nullable SetRelayState setOnOffState(ChannelUID channelUid, OnOffType onOff) throws IOException { // It's unknown what the command is to send to the device so it's not supported. return null; diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SmartHomeDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SmartHomeDevice.java index 9e663fb220675..10b55b24030f0 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SmartHomeDevice.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SmartHomeDevice.java @@ -16,6 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; import org.openhab.binding.tplinksmarthome.internal.Commands; @@ -32,7 +33,9 @@ @NonNullByDefault public abstract class SmartHomeDevice { - protected Commands commands = new Commands(); + protected final Commands commands = new Commands(); + protected @NonNullByDefault({}) Connection connection; + protected @NonNullByDefault({}) TPLinkSmartHomeConfiguration configuration; /** * Checks if the response object contains errors and if so throws an {@link IOException} when an error code was set. @@ -41,13 +44,24 @@ public abstract class SmartHomeDevice { * @throws IOException if an error code was set in the response object */ protected void checkErrors(@Nullable HasErrorResponse response) throws IOException { - ErrorResponse errorResponse = response == null ? null : response.getErrorResponse(); + final ErrorResponse errorResponse = response == null ? null : response.getErrorResponse(); if (errorResponse != null && errorResponse.getErrorCode() != 0) { throw new IOException("Error (" + errorResponse.getErrorCode() + "): " + errorResponse.getErrorMessage()); } } + /** + * Sets connection and configuration values. + * + * @param connection The connection to the device + * @param configuration The global configuration + */ + public void initialize(Connection connection, TPLinkSmartHomeConfiguration configuration) { + this.connection = connection; + this.configuration = configuration; + } + /** * @return the json string to send to the device to get the state of the device. */ @@ -56,23 +70,27 @@ protected void checkErrors(@Nullable HasErrorResponse response) throws IOExcepti /** * Handle the command for the given channel * - * @param channelID The channel the command is for - * @param connection The connection to the device + * @param channelUID The channel the command is for * @param command The command to be send to the device - * @param configuration The global configuration * @return Returns true if the commands successfully was send to the device * @throws IOException In case of communications error or the device returned an error */ - public abstract boolean handleCommand(String channelID, Connection connection, Command command, - TPLinkSmartHomeConfiguration configuration) throws IOException; + public abstract boolean handleCommand(ChannelUID channelUID, Command command) throws IOException; /** * Returns the {@link State} value for the given value extracted from the deviceState data. * - * @param channelId channel to get state for + * @param channelUid channel to get state for * @param deviceState state object containing the state * @return {@link State} value for the given channel */ - public abstract State updateChannel(String channelId, DeviceState deviceState); + public abstract State updateChannel(ChannelUID channelUid, DeviceState deviceState); + /** + * Called with the new device state after the new device state is retrieved from the device. + * + * @param deviceState new device state + */ + public void refreshedDeviceState(@Nullable DeviceState deviceState) { + } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDevice.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDevice.java index ae5fe31d42264..bfcaa6e46d7a1 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDevice.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDevice.java @@ -19,12 +19,11 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.tplinksmarthome.internal.Commands; -import org.openhab.binding.tplinksmarthome.internal.Connection; -import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration; import org.openhab.binding.tplinksmarthome.internal.model.HasErrorResponse; /** @@ -41,9 +40,8 @@ public String getUpdateCommand() { } @Override - public boolean handleCommand(String channelID, Connection connection, Command command, - TPLinkSmartHomeConfiguration configuration) throws IOException { - return command instanceof OnOffType && handleOnOffType(channelID, connection, (OnOffType) command); + public boolean handleCommand(ChannelUID channelUid, Command command) throws IOException { + return command instanceof OnOffType && handleOnOffType(channelUid, (OnOffType) command); } /** @@ -56,13 +54,17 @@ protected State getOnOffState(DeviceState deviceState) { return deviceState.getSysinfo().getRelayState(); } - private boolean handleOnOffType(String channelID, Connection connection, OnOffType onOff) throws IOException { - HasErrorResponse response = null; + private boolean handleOnOffType(ChannelUID channelUid, OnOffType onOff) throws IOException { + final HasErrorResponse response; + final String baseChannelId = getBaseChannel(channelUid); - if (CHANNEL_SWITCH.equals(channelID)) { - response = setOnOffState(connection, onOff); - } else if (CHANNEL_LED.equals(channelID)) { - response = commands.setLedOnResponse(connection.sendCommand(commands.setLedOn(onOff))); + if (CHANNEL_SWITCH.contentEquals(baseChannelId)) { + response = setOnOffState(channelUid, onOff); + } else if (CHANNEL_LED.contentEquals(baseChannelId)) { + response = commands + .setLedOnResponse(connection.sendCommand(commands.setLedOn(onOff, getChildId(channelUid)))); + } else { + response = null; } checkErrors(response); return response != null; @@ -71,21 +73,40 @@ private boolean handleOnOffType(String channelID, Connection connection, OnOffTy /** * Sends the {@link OnOffType} command to the device and returns the returned answer. * - * @param connection Connection to use + * @param channelUid channel Id to use to determine child id * @param onOff command to the send * @return state returned by the device + * @throws IOException exception in case device not reachable */ - protected @Nullable HasErrorResponse setOnOffState(Connection connection, OnOffType onOff) throws IOException { - return commands.setRelayStateResponse(connection.sendCommand(commands.setRelayState(onOff))); + protected @Nullable HasErrorResponse setOnOffState(ChannelUID channelUid, OnOffType onOff) throws IOException { + return commands + .setRelayStateResponse(connection.sendCommand(commands.setRelayState(onOff, getChildId(channelUid)))); } @Override - public State updateChannel(String channelId, DeviceState deviceState) { - if (CHANNEL_SWITCH.equals(channelId)) { + public State updateChannel(ChannelUID channelUid, DeviceState deviceState) { + final String baseChannelId = getBaseChannel(channelUid); + + if (CHANNEL_SWITCH.equals(baseChannelId)) { return getOnOffState(deviceState); - } else if (CHANNEL_LED.equals(channelId)) { + } else if (CHANNEL_LED.equals(baseChannelId)) { return deviceState.getSysinfo().getLedOff(); } return UnDefType.UNDEF; } + + /** + * Returns the child Id for the given channel if the device supports children and it's a channel for a specific + * child. + * + * @param channelUid channel Id to get the child id for + * @return null or child id + */ + protected @Nullable String getChildId(ChannelUID channelUid) { + return null; + } + + private String getBaseChannel(ChannelUID channelUid) { + return channelUid.isInGroup() ? channelUid.getIdWithoutGroup() : channelUid.getId(); + } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandler.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandler.java index 67da055cd810f..70b51efd2d590 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandler.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandler.java @@ -61,7 +61,7 @@ public class SmartHomeHandler extends BaseThingHandler { private @NonNullByDefault({}) ExpiringCache<@Nullable DeviceState> cache; /** - * Constructor + * Constructor. * * @param thing The thing to handle * @param smartHomeDevice Specific Smart Home device handler @@ -74,12 +74,12 @@ public SmartHomeHandler(Thing thing, SmartHomeDevice smartHomeDevice, TPLinkIpAd } @Override - public void handleCommand(ChannelUID channelUID, Command command) { + public void handleCommand(ChannelUID channelUid, Command command) { try { if (command instanceof RefreshType) { - updateChannelState(channelUID, cache.getValue()); - } else if (!smartHomeDevice.handleCommand(channelUID.getId(), connection, command, configuration)) { - logger.debug("Command {} is not supported for channel: {}", command, channelUID.getId()); + updateChannelState(channelUid, cache.getValue()); + } else if (!smartHomeDevice.handleCommand(channelUid, command)) { + logger.debug("Command {} is not supported for channel: {}", command, channelUid.getId()); } } catch (IOException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); @@ -102,8 +102,10 @@ public void initialize() { "No ip address or the device id configured."); return; } - logger.debug("Initializing TP-Link Smart device on ip {}", configuration.ipAddress); + logger.debug("Initializing TP-Link Smart device on ip '{}' or deviceId '{}' ", configuration.ipAddress, + configuration.deviceId); connection = createConnection(configuration); + smartHomeDevice.initialize(connection, configuration); cache = new ExpiringCache<@Nullable DeviceState>(TimeUnit.SECONDS.toMillis(configuration.refresh), this::refreshCache); updateStatus(ThingStatus.UNKNOWN); @@ -123,12 +125,12 @@ Connection createConnection(TPLinkSmartHomeConfiguration config) { return new Connection(config.ipAddress); } - @Nullable - private DeviceState refreshCache() { + private @Nullable DeviceState refreshCache() { try { updateIpAddress(); - DeviceState deviceState = new DeviceState(connection.sendCommand(smartHomeDevice.getUpdateCommand())); + final DeviceState deviceState = new DeviceState(connection.sendCommand(smartHomeDevice.getUpdateCommand())); updateDeviceId(deviceState.getSysinfo().getDeviceId()); + smartHomeDevice.refreshedDeviceState(deviceState); if (getThing().getStatus() != ThingStatus.ONLINE) { updateStatus(ThingStatus.ONLINE); } @@ -197,8 +199,7 @@ private void startAutomaticRefresh(TPLinkSmartHomeConfiguration config) { void refreshChannels() { logger.trace("Update Channels for:{}", thing.getUID()); - DeviceState value = cache.getValue(); - getThing().getChannels().forEach(channel -> updateChannelState(channel.getUID(), value)); + getThing().getChannels().forEach(channel -> updateChannelState(channel.getUID(), cache.getValue())); } /** @@ -209,7 +210,10 @@ void refreshChannels() { * */ private void updateChannelState(ChannelUID channelUID, @Nullable DeviceState deviceState) { - String channelId = channelUID.getId(); + if (!isLinked(channelUID)) { + return; + } + String channelId = channelUID.isInGroup() ? channelUID.getIdWithoutGroup() : channelUID.getId(); final State state; if (deviceState == null) { @@ -217,7 +221,7 @@ private void updateChannelState(ChannelUID channelUID, @Nullable DeviceState dev } else if (CHANNEL_RSSI.equals(channelId)) { state = new DecimalType(deviceState.getSysinfo().getRssi()); } else { - state = smartHomeDevice.updateChannel(channelId, deviceState); + state = smartHomeDevice.updateChannel(channelUID, deviceState); } updateState(channelUID, state); } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/ContextState.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/ContextState.java new file mode 100644 index 0000000000000..2505abf96a537 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/ContextState.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.tplinksmarthome.internal.model; + +import com.google.gson.annotations.Expose; + +/** + * Class to be extended by state classes that support context. i.e. has multiple children that can be controlled. + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +public class ContextState { + + public static class Context { + @Expose + private String[] childIds = new String[1]; + + public void setChildId(String childId) { + this.childIds[0] = childId; + } + + @Override + public String toString() { + return " child_ids:[" + childIds[0] + "]"; + } + } + + @Expose + private Context context; + + public void setChildId(String childId) { + context = new Context(); + context.setChildId(childId); + } + + @Override + public String toString() { + return context == null ? "" : context.toString(); + } +} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/GetRealtime.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/GetRealtime.java index 4343c1f743a90..e83640941fe55 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/GetRealtime.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/GetRealtime.java @@ -23,7 +23,7 @@ * @author Hilbrand Bouwkamp - Initial contribution */ @NonNullByDefault -public class GetRealtime { +public class GetRealtime extends ContextState { public static class EMeter { private Realtime getRealtime = new Realtime(); diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/HasErrorResponse.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/HasErrorResponse.java index e219d804e72df..bb7724c86313b 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/HasErrorResponse.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/HasErrorResponse.java @@ -17,6 +17,7 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ +@FunctionalInterface public interface HasErrorResponse { /** * @return returns the object containing the error response diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetLedOff.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetLedOff.java index 49e57b6754316..6093859d71a25 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetLedOff.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetLedOff.java @@ -22,7 +22,7 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class SetLedOff implements HasErrorResponse { +public class SetLedOff extends ContextState implements HasErrorResponse { public static class LedOff extends ErrorResponse { @Expose diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetRelayState.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetRelayState.java index 498ea8cd0c374..5033ccffe50d5 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetRelayState.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/SetRelayState.java @@ -22,7 +22,7 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class SetRelayState implements HasErrorResponse { +public class SetRelayState extends ContextState implements HasErrorResponse { public static class RelayState extends ErrorResponse { @Expose(deserialize = false) @@ -38,6 +38,10 @@ public static class System { @Expose private RelayState setRelayState = new RelayState(); + public void setRelayState(OnOffType onOff) { + setRelayState.state = onOff == OnOffType.ON ? 1 : 0; + } + @Override public String toString() { return "set_relay_state:{" + setRelayState + "}"; @@ -53,7 +57,7 @@ public ErrorResponse getErrorResponse() { } public void setRelayState(OnOffType onOff) { - system.setRelayState.state = onOff == OnOffType.ON ? 1 : 0; + system.setRelayState(onOff); } @Override diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/Sysinfo.java b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/Sysinfo.java index 03acec812b1f4..5e28ce3189d02 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/Sysinfo.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/java/org/openhab/binding/tplinksmarthome/internal/model/Sysinfo.java @@ -12,6 +12,9 @@ */ package org.openhab.binding.tplinksmarthome.internal.model; +import java.util.ArrayList; +import java.util.List; + import org.eclipse.smarthome.core.library.types.OnOffType; import com.google.gson.annotations.SerializedName; @@ -80,6 +83,32 @@ public OnOffType getRelayStatus() { } } + /** + * Status of a single outlet on power strip. + */ + public static class Outlet { + private String alias; + private String id; + private long onTime; + private int state; + + public String getAlias() { + return alias; + } + + public String getId() { + return id; + } + + public long getOnTime() { + return onTime; + } + + public OnOffType getState() { + return state == 1 ? OnOffType.ON : OnOffType.OFF; + } + } + /** * Status of the range extended Wi-Fi. */ @@ -122,6 +151,10 @@ public int getW2gRssi() { private double latitude; private double longitude; + // powerstrip/multiple plugs support. + private int childNum; + private List children = new ArrayList<>(); + // dimmer specific system info private int brightness; @@ -251,6 +284,14 @@ public Plug getPlug() { return plug; } + public int getChildNum() { + return childNum; + } + + public List getChildren() { + return children; + } + public Sysinfo getSystem() { return system; } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS107.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS107.xml new file mode 100644 index 0000000000000..452caec2571de --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS107.xml @@ -0,0 +1,26 @@ + + + + + + TP-Link HS107 Smart Wi-Fi Plug, 2-Outlets + PowerOutlet + + + + + + + + + + + + deviceId + + + + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS110.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS110.xml index c7542b31de0cc..79a36c47f0f5a 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS110.xml +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS110.xml @@ -14,7 +14,7 @@ - + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS300.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS300.xml new file mode 100644 index 0000000000000..57779f37a8925 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/HS300.xml @@ -0,0 +1,39 @@ + + + + + + TP-Link HS300 Smart Wi-Fi Power Strip + PowerOutlet + + + + + + + + + + + + + + + + + + + + + + + + deviceId + + + + + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP200.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP200.xml new file mode 100644 index 0000000000000..0a96fb11f526b --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP200.xml @@ -0,0 +1,26 @@ + + + + + + TP-Link KP200 Smart Wi-Fi Power Outlet, 2-Sockets + PowerOutlet + + + + + + + + + + + + deviceId + + + + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP400.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP400.xml new file mode 100644 index 0000000000000..d2fd8122dc7ba --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/KP400.xml @@ -0,0 +1,26 @@ + + + + + + TP-Link KP400 Smart Outdoor Plug + PowerOutlet + + + + + + + + + + + + deviceId + + + + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE270K.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE270K.xml index 008810abd6852..9f10a59173d0b 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE270K.xml +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE270K.xml @@ -10,7 +10,7 @@ PowerOutlet - + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE370K.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE370K.xml index efb63a052763e..19838ad31c8eb 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE370K.xml +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/RE370K.xml @@ -10,7 +10,7 @@ PowerOutlet - + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/channels.xml b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/channels.xml index 871d48d660413..c8931f35f1d54 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.tplinksmarthome/src/main/resources/ESH-INF/thing/channels.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd"> - + Switch Shows the switch state of the Smart Home device. @@ -27,7 +27,7 @@ Energy - + Number Actual energy usage. @@ -58,4 +58,32 @@ + + + + PowerOutlet + + + + + + + + + PowerOutlet + + + + + + + PowerOutlet + + + + + + + + diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/ChannelUIDConstants.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/ChannelUIDConstants.java new file mode 100644 index 0000000000000..f36c1be9e0329 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/ChannelUIDConstants.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.tplinksmarthome.internal; + +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.thing.ThingUID; + +/** + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public final class ChannelUIDConstants { + + public static final ChannelUID CHANNEL_UID_BRIGHTNESS = createChannel(LB130, CHANNEL_BRIGHTNESS); + public static final ChannelUID CHANNEL_UID_COLOR = createChannel(LB130, CHANNEL_COLOR); + public static final ChannelUID CHANNEL_UID_COLOR_TEMPERATURE = createChannel(LB130, CHANNEL_COLOR_TEMPERATURE); + public static final ChannelUID CHANNEL_UID_ENERGY_CURRENT = createChannel(HS110, CHANNEL_ENERGY_CURRENT); + public static final ChannelUID CHANNEL_UID_ENERGY_POWER = createChannel(HS110, CHANNEL_ENERGY_POWER); + public static final ChannelUID CHANNEL_UID_ENERGY_TOTAL = createChannel(HS110, CHANNEL_ENERGY_TOTAL); + public static final ChannelUID CHANNEL_UID_ENERGY_VOLTAGE = createChannel(HS110, CHANNEL_ENERGY_VOLTAGE); + public static final ChannelUID CHANNEL_UID_LED = createChannel(HS100, CHANNEL_LED); + public static final ChannelUID CHANNEL_UID_OTHER = createChannel(HS100, "OTHER"); + public static final ChannelUID CHANNEL_UID_RSSI = createChannel(HS100, CHANNEL_RSSI); + public static final ChannelUID CHANNEL_UID_SWITCH = createChannel(HS100, CHANNEL_SWITCH); + + private static final String ID = "1234"; + + private ChannelUIDConstants() { + // Util class + } + + private static ChannelUID createChannel(TPLinkSmartHomeThingType thingType, String channelId) { + return new ChannelUID(new ThingUID(thingType.thingTypeUID(), ID), channelId); + } + + public static ChannelUID createChannel(TPLinkSmartHomeThingType thingType, String groupId, String channelId) { + return new ChannelUID(new ThingUID(thingType.thingTypeUID(), ID), groupId, channelId); + } +} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/PropertiesCollectorTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/PropertiesCollectorTest.java index 19652da9b94db..9fc45fea56f33 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/PropertiesCollectorTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/PropertiesCollectorTest.java @@ -17,24 +17,20 @@ import java.io.IOException; import java.util.Map; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.junit.Test; import org.openhab.binding.tplinksmarthome.internal.model.GetSysinfo; -import org.openhab.binding.tplinksmarthome.internal.model.GsonUtil; import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil; -import com.google.gson.Gson; - /** * Test class for {@link PropertiesCollector} class. * * @author Hilbrand Bouwkamp - Initial contribution */ +@NonNullByDefault public class PropertiesCollectorTest { - private final Gson gson = GsonUtil.createGson(); - /** * Tests if properties for a bulb device are correctly parsed. * @@ -65,11 +61,11 @@ public void testRangeExtenderProperties() throws IOException { assertProperties("rangeextender_get_sysinfo_response", TPLinkSmartHomeThingType.RE270K, 11); } - private void assertProperties(@NonNull String responseFile, @NonNull TPLinkSmartHomeThingType thingType, - int expectedSize) throws IOException { + private void assertProperties(String responseFile, TPLinkSmartHomeThingType thingType, int expectedSize) + throws IOException { ThingTypeUID thingTypeUID = thingType.thingTypeUID(); Map props = PropertiesCollector.collectProperties(thingTypeUID, "localhost", - ModelTestUtil.toJson(gson, responseFile, GetSysinfo.class).getSysinfo()); + ModelTestUtil.jsonFromFile(responseFile, GetSysinfo.class).getSysinfo()); assertEquals("Number of properties not as expected for properties: " + props, expectedSize, props.size()); props.entrySet().stream().forEach( diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDeviceTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDeviceTest.java index 39e344702a615..6283714d14992 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDeviceTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/BulbDeviceTest.java @@ -13,15 +13,17 @@ package org.openhab.binding.tplinksmarthome.internal.device; import static org.junit.Assert.*; +import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*; import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.LB130; import java.io.IOException; +import org.eclipse.jdt.annotation.NonNullByDefault; 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.PercentType; -import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.types.UnDefType; import org.junit.Test; import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil; @@ -31,15 +33,14 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class BulbDeviceTest extends DeviceTestBase { +@NonNullByDefault +public class BulbDeviceTest extends DeviceTestBase { private static final String DEVICE_OFF = "bulb_get_sysinfo_response_off"; - private final BulbDevice device = new BulbDevice(new ThingTypeUID(BINDING_ID, "lb130"), COLOR_TEMPERATURE_LB130_MIN, - COLOR_TEMPERATURE_LB130_MAX); - public BulbDeviceTest() throws IOException { - super("bulb_get_sysinfo_response_on"); + super(new BulbDevice(LB130.thingTypeUID(), COLOR_TEMPERATURE_LB130_MIN, COLOR_TEMPERATURE_LB130_MAX), + "bulb_get_sysinfo_response_on"); } @Override @@ -52,106 +53,107 @@ public void setUp() throws IOException { public void testHandleCommandBrightness() throws IOException { assertInput("bulb_transition_light_state_brightness"); assertTrue("Brightness channel should be handled", - device.handleCommand(CHANNEL_BRIGHTNESS, connection, new PercentType(33), configuration)); + device.handleCommand(CHANNEL_UID_BRIGHTNESS, new PercentType(33))); } @Test public void testHandleCommandBrightnessOnOff() throws IOException { assertInput("bulb_transition_light_state_on"); assertTrue("Brightness channel with OnOff state should be handled", - device.handleCommand(CHANNEL_BRIGHTNESS, connection, OnOffType.ON, configuration)); + device.handleCommand(CHANNEL_UID_BRIGHTNESS, OnOffType.ON)); } @Test public void testHandleCommandColor() throws IOException { assertInput("bulb_transition_light_state_color"); - assertTrue("Color channel should be handled", - device.handleCommand(CHANNEL_COLOR, connection, new HSBType("55,44,33"), configuration)); + assertTrue("Color channel should be handled", device.handleCommand(CHANNEL_UID_COLOR, new HSBType("55,44,33"))); } public void testHandleCommandColorBrightness() throws IOException { assertInput("bulb_transition_light_state_brightness"); assertTrue("Color channel with Percentage state (=brightness) should be handled", - device.handleCommand(CHANNEL_COLOR, connection, new PercentType(33), configuration)); + device.handleCommand(CHANNEL_UID_COLOR, new PercentType(33))); } public void testHandleCommandColorOnOff() throws IOException { assertInput("bulb_transition_light_state_on"); assertTrue("Color channel with OnOff state should be handled", - device.handleCommand(CHANNEL_COLOR, connection, OnOffType.ON, configuration)); + device.handleCommand(CHANNEL_UID_COLOR, OnOffType.ON)); } @Test public void testHandleCommandColorTemperature() throws IOException { assertInput("bulb_transition_light_state_color_temp"); assertTrue("Color temperature channel should be handled", - device.handleCommand(CHANNEL_COLOR_TEMPERATURE, connection, new PercentType(40), configuration)); + device.handleCommand(CHANNEL_UID_COLOR_TEMPERATURE, new PercentType(40))); } @Test public void testHandleCommandColorTemperatureOnOff() throws IOException { assertInput("bulb_transition_light_state_on"); assertTrue("Color temperature channel with OnOff state should be handled", - device.handleCommand(CHANNEL_COLOR_TEMPERATURE, connection, OnOffType.ON, configuration)); + device.handleCommand(CHANNEL_UID_COLOR_TEMPERATURE, OnOffType.ON)); } @Test public void testHandleCommandSwitch() throws IOException { assertInput("bulb_transition_light_state_on"); - assertTrue("Switch channel should be handled", - device.handleCommand(CHANNEL_SWITCH, connection, OnOffType.ON, configuration)); + assertTrue("Switch channel should be handled", device.handleCommand(CHANNEL_UID_SWITCH, OnOffType.ON)); } @Test public void testUpdateChannelBrightnessOn() { assertEquals("Brightness should be on", new PercentType(92), - device.updateChannel(CHANNEL_BRIGHTNESS, deviceState)); + device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState)); } @Test public void testUpdateChannelBrightnessOff() throws IOException { deviceState = new DeviceState(ModelTestUtil.readJson(DEVICE_OFF)); assertEquals("Brightness should be off", PercentType.ZERO, - device.updateChannel(CHANNEL_BRIGHTNESS, deviceState)); + device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState)); } @Test public void testUpdateChannelColorOn() { - assertEquals("Color should be on", new HSBType("7,44,92"), device.updateChannel(CHANNEL_COLOR, deviceState)); + assertEquals("Color should be on", new HSBType("7,44,92"), + device.updateChannel(CHANNEL_UID_COLOR, deviceState)); } @Test public void testUpdateChannelColorOff() throws IOException { deviceState = new DeviceState(ModelTestUtil.readJson(DEVICE_OFF)); - assertEquals("Color should be off", new HSBType("7,44,0"), device.updateChannel(CHANNEL_COLOR, deviceState)); + assertEquals("Color should be off", new HSBType("7,44,0"), + device.updateChannel(CHANNEL_UID_COLOR, deviceState)); } @Test public void testUpdateChannelSwitchOn() { - assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_SWITCH, deviceState)); + assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_SWITCH, deviceState)); } @Test public void testUpdateChannelSwitchOff() throws IOException { deviceState = new DeviceState(ModelTestUtil.readJson(DEVICE_OFF)); - assertSame("Switch should be off", OnOffType.OFF, device.updateChannel(CHANNEL_SWITCH, deviceState)); + assertSame("Switch should be off", OnOffType.OFF, device.updateChannel(CHANNEL_UID_SWITCH, deviceState)); } @Test public void testUpdateChannelColorTemperature() { assertEquals("Color temperature should be set", new PercentType(3), - device.updateChannel(CHANNEL_COLOR_TEMPERATURE, deviceState)); + device.updateChannel(CHANNEL_UID_COLOR_TEMPERATURE, deviceState)); } @Test public void testUpdateChannelOther() { - assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, device.updateChannel("OTHER", deviceState)); + assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, + device.updateChannel(CHANNEL_UID_OTHER, deviceState)); } @Test public void testUpdateChannelPower() { assertEquals("Power values should be set", new DecimalType(10.8), - device.updateChannel(CHANNEL_ENERGY_POWER, deviceState)); + device.updateChannel(CHANNEL_UID_ENERGY_POWER, deviceState)); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DeviceTestBase.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DeviceTestBase.java index cb5ec2ff99ef3..041bcf198c955 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DeviceTestBase.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DeviceTestBase.java @@ -22,8 +22,9 @@ import java.io.OutputStream; import java.net.Socket; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.junit.Before; import org.mockito.Mock; import org.openhab.binding.tplinksmarthome.internal.Connection; @@ -36,28 +37,31 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class DeviceTestBase { +@NonNullByDefault +public class DeviceTestBase { - @NonNull + protected final T device; protected final Connection connection; - @NonNull protected final TPLinkSmartHomeConfiguration configuration = new TPLinkSmartHomeConfiguration(); - protected DeviceState deviceState; + protected @NonNullByDefault({}) DeviceState deviceState; private final String deviceStateFilename; @Mock - private Socket socket; + private @NonNullByDefault({}) Socket socket; @Mock - private OutputStream outputStream; + private @NonNullByDefault({}) OutputStream outputStream; /** * Constructor. * + * @param device Device under test * @param deviceStateFilename name of the file to read the device state json from to use in tests + * * @throws IOException exception in case device not reachable */ - public DeviceTestBase(@NonNull String deviceStateFilename) throws IOException { + protected DeviceTestBase(T device, String deviceStateFilename) throws IOException { + this.device = device; this.deviceStateFilename = deviceStateFilename; configuration.ipAddress = "localhost"; configuration.refresh = 30; @@ -68,6 +72,7 @@ protected Socket createSocket() throws IOException { return socket; }; }; + device.initialize(connection, configuration); } @Before @@ -84,7 +89,7 @@ public void setUp() throws IOException { * @param responseFilenames names of the files to read that contains the answer. It's the unencrypted json string * @throws IOException exception in case device not reachable */ - protected void setSocketReturnAssert(@NonNull String... responseFilenames) throws IOException { + protected void setSocketReturnAssert(String... responseFilenames) throws IOException { AtomicInteger index = new AtomicInteger(); doAnswer(i -> { @@ -103,15 +108,21 @@ protected void setSocketReturnAssert(@NonNull String... responseFilenames) throw * @param filenames names of the files containing the reference json * @throws IOException exception in case device not reachable */ - protected void assertInput(@NonNull String... filename) throws IOException { + protected void assertInput(String... filenames) throws IOException { + assertInput(Function.identity(), Function.identity(), filenames); + } + + protected void assertInput(Function jsonProcessor, Function expectedProcessor, + String... filenames) throws IOException { AtomicInteger index = new AtomicInteger(); doAnswer(arg -> { - String json = ModelTestUtil.readJson(filename[index.get()]); + String json = jsonProcessor.apply(ModelTestUtil.readJson(filenames[index.get()])); byte[] input = (byte[]) arg.getArguments()[0]; try (ByteArrayInputStream inputStream = new ByteArrayInputStream(input)) { - assertEquals(filename[index.get()], json, CryptUtil.decryptWithLength(inputStream)); + String expectedString = expectedProcessor.apply(CryptUtil.decryptWithLength(inputStream)); + assertEquals(filenames[index.get()], json, expectedString); } index.incrementAndGet(); return null; diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDeviceTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDeviceTest.java index 97cd710cffd42..7c3ef0ea0aaf5 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDeviceTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/DimmerDeviceTest.java @@ -13,10 +13,11 @@ package org.openhab.binding.tplinksmarthome.internal.device; import static org.junit.Assert.*; -import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.CHANNEL_BRIGHTNESS; +import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*; import java.io.IOException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.PercentType; import org.eclipse.smarthome.core.types.UnDefType; @@ -28,22 +29,21 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class DimmerDeviceTest extends DeviceTestBase { +@NonNullByDefault +public class DimmerDeviceTest extends DeviceTestBase { private static final PercentType BRIGHTNESS_VALUE = new PercentType(50); - private final DimmerDevice device = new DimmerDevice(); - public DimmerDeviceTest() throws IOException { - super("hs220_get_sysinfo_response_on"); + super(new DimmerDevice(), "hs220_get_sysinfo_response_on"); } @Test public void testHandleCommandBrightnessOnOff() throws IOException { assertInput("dimmer_set_switch_state_on"); - setSocketReturnAssert("dimmer_set_switch_state_response"); + setSocketReturnAssert("dimmer_set_switch_state_on"); assertTrue("Brightness channel as OnOffType type should be handled", - device.handleCommand(CHANNEL_BRIGHTNESS, connection, OnOffType.ON, configuration)); + device.handleCommand(CHANNEL_UID_BRIGHTNESS, OnOffType.ON)); } @Test @@ -51,7 +51,7 @@ public void testHandleCommandBrightnessZero() throws IOException { assertInput("dimmer_set_switch_state_off"); setSocketReturnAssert("dimmer_set_switch_state_response"); assertTrue("Brightness channel with percentage 0 should be handled", - device.handleCommand(CHANNEL_BRIGHTNESS, connection, PercentType.ZERO, configuration)); + device.handleCommand(CHANNEL_UID_BRIGHTNESS, PercentType.ZERO)); } @Test @@ -59,7 +59,7 @@ public void testHandleCommandBrightness() throws IOException { assertInput("dimmer_set_brightness"); setSocketReturnAssert("dimmer_set_brightness_response"); assertTrue("Brightness channel should be handled", - device.handleCommand(CHANNEL_BRIGHTNESS, connection, new PercentType(17), configuration)); + device.handleCommand(CHANNEL_UID_BRIGHTNESS, new PercentType(17))); } @Test @@ -67,17 +67,18 @@ public void testUpdateChannelSwitch() throws IOException { deviceState = new DeviceState(ModelTestUtil.readJson("hs220_get_sysinfo_response_off")); assertSame("Dimmer device should be off", OnOffType.OFF, - ((PercentType) device.updateChannel(CHANNEL_BRIGHTNESS, deviceState)).as(OnOffType.class)); + ((PercentType) device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState)).as(OnOffType.class)); } @Test public void testUpdateChannelBrightness() { assertEquals("Dimmer brightness should be set", BRIGHTNESS_VALUE, - device.updateChannel(CHANNEL_BRIGHTNESS, deviceState)); + device.updateChannel(CHANNEL_UID_BRIGHTNESS, deviceState)); } @Test public void testUpdateChannelOther() { - assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, device.updateChannel("OTHER", deviceState)); + assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, + device.updateChannel(CHANNEL_UID_OTHER, deviceState)); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDeviceTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDeviceTest.java index a3bf32795abfc..6f63fa7232a71 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDeviceTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/EnergySwitchDeviceTest.java @@ -13,13 +13,13 @@ package org.openhab.binding.tplinksmarthome.internal.device; import static org.junit.Assert.*; -import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*; import java.io.IOException; import java.util.Arrays; import java.util.List; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; @@ -35,13 +35,13 @@ * @author Hilbrand Bouwkamp - Initial contribution */ @RunWith(value = Parameterized.class) +@NonNullByDefault public class EnergySwitchDeviceTest { private static final List TESTS = Arrays .asList(new Object[][] { { "plug_get_realtime_response", }, { "plug_get_realtime_response_v2", } }); private final EnergySwitchDevice device = new EnergySwitchDevice(); - @NonNull private final DeviceState deviceState; public EnergySwitchDeviceTest(String name) throws IOException { @@ -56,18 +56,18 @@ public static List data() { @Test public void testUpdateChannelEnergyCurrent() { assertEquals("Energy current should have valid state value", 1, - ((DecimalType) device.updateChannel(CHANNEL_ENERGY_CURRENT, deviceState)).intValue()); + ((DecimalType) device.updateChannel(CHANNEL_UID_ENERGY_CURRENT, deviceState)).intValue()); } @Test public void testUpdateChannelEnergyTotal() { assertEquals("Energy total should have valid state value", 10, - ((DecimalType) device.updateChannel(CHANNEL_ENERGY_TOTAL, deviceState)).intValue()); + ((DecimalType) device.updateChannel(CHANNEL_UID_ENERGY_TOTAL, deviceState)).intValue()); } @Test public void testUpdateChannelEnergyVoltage() { - State state = device.updateChannel(CHANNEL_ENERGY_VOLTAGE, deviceState); + State state = device.updateChannel(CHANNEL_UID_ENERGY_VOLTAGE, deviceState); assertEquals("Energy voltage should have valid state value", 230, ((DecimalType) state).intValue()); assertEquals("Channel patten to display as int", "230 V", state.format("%.0f V")); } @@ -75,12 +75,13 @@ public void testUpdateChannelEnergyVoltage() { @Test public void testUpdateChanneEnergyPowerl() { assertEquals("Energy power should have valid state value", 20, - ((DecimalType) device.updateChannel(CHANNEL_ENERGY_POWER, deviceState)).intValue()); + ((DecimalType) device.updateChannel(CHANNEL_UID_ENERGY_POWER, deviceState)).intValue()); } @Test public void testUpdateChannelOther() { - assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, device.updateChannel("OTHER", deviceState)); + assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, + device.updateChannel(CHANNEL_UID_OTHER, deviceState)); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDeviceTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDeviceTest.java new file mode 100644 index 0000000000000..884d711fc4809 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/PowerStripDeviceTest.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.tplinksmarthome.internal.device; + +import static org.junit.Assert.*; +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeThingType.HS300; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +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.thing.ChannelUID; +import org.junit.Before; +import org.junit.Test; +import org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants; +import org.openhab.binding.tplinksmarthome.internal.model.ModelTestUtil; +import org.openhab.binding.tplinksmarthome.internal.model.SetRelayState; + +/** + * Test class for {@link PowerStripDevice} class. + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public class PowerStripDeviceTest extends DeviceTestBase { + + private static final ChannelUID CHANNEL_OUTLET_1 = ChannelUIDConstants.createChannel(HS300, + CHANNEL_OUTLET_GROUP_PREFIX + '1', CHANNEL_SWITCH); + private static final ChannelUID CHANNEL_OUTLET_2 = ChannelUIDConstants.createChannel(HS300, + CHANNEL_OUTLET_GROUP_PREFIX + '2', CHANNEL_SWITCH); + private static final ChannelUID CHANNEL_ENERGY_CURRENT_OUTLET_2 = ChannelUIDConstants.createChannel(HS300, + CHANNEL_OUTLET_GROUP_PREFIX + '2', CHANNEL_ENERGY_CURRENT); + private static final String[] REALTIME_INPUTS = IntStream.range(0, 6).mapToObj(i -> "hs300_get_realtime") + .collect(Collectors.toList()).toArray(new String[0]); + private static final String[] REALTIME_RESPONSES = IntStream.range(0, 6).mapToObj(i -> "plug_get_realtime_response") + .collect(Collectors.toList()).toArray(new String[0]); + + public PowerStripDeviceTest() throws IOException { + super(new PowerStripDevice(HS300.thingTypeUID()), "hs300_get_sysinfo_response"); + } + + @Override + @Before + public void setUp() throws IOException { + super.setUp(); + final AtomicInteger inputCounter = new AtomicInteger(0); + final Function inputWrapper = s -> s.replace("001", "00" + inputCounter.incrementAndGet()); + + assertInput(inputWrapper, Function.identity(), REALTIME_INPUTS); + setSocketReturnAssert(REALTIME_RESPONSES); + device.refreshedDeviceState(deviceState); + } + + @Test + public void testHandleCommandSwitchChannel2() throws IOException { + Function normalize = s -> ModelTestUtil.GSON + .toJson(ModelTestUtil.GSON.fromJson(s, SetRelayState.class)); + assertInput(normalize, normalize, "hs300_set_relay_state"); + setSocketReturnAssert("hs300_set_relay_state_response"); + assertTrue("Outlet channel 2 should be handled", device.handleCommand(CHANNEL_OUTLET_2, OnOffType.ON)); + } + + @Test + public void testUpdateChannelOutlet1() { + assertSame("Outlet 1 should be on", OnOffType.ON, device.updateChannel(CHANNEL_OUTLET_1, deviceState)); + } + + @Test + public void testUpdateChannelOutlet2() { + assertSame("Outlet 2 should be off", OnOffType.OFF, device.updateChannel(CHANNEL_OUTLET_2, deviceState)); + } + + @Test + public void testUpdateChannelEnergyCurrent() { + assertEquals("Energy current should have valid state value", 1, + ((DecimalType) device.updateChannel(CHANNEL_ENERGY_CURRENT_OUTLET_2, deviceState)).intValue()); + } +} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDeviceTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDeviceTest.java index 177df0a4f890e..01c72ae239a9b 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDeviceTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/RangeExtenderDeviceTest.java @@ -13,13 +13,13 @@ package org.openhab.binding.tplinksmarthome.internal.device; import static org.junit.Assert.*; -import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*; import java.io.IOException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.types.UnDefType; -import org.junit.Ignore; import org.junit.Test; /** @@ -27,41 +27,33 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class RangeExtenderDeviceTest extends DeviceTestBase { - private final RangeExtenderDevice device = new RangeExtenderDevice(); +@NonNullByDefault +public class RangeExtenderDeviceTest extends DeviceTestBase { public RangeExtenderDeviceTest() throws IOException { - super("rangeextender_get_sysinfo_response"); + super(new RangeExtenderDevice(), "rangeextender_get_sysinfo_response"); } @Test public void testHandleCommandSwitch() throws IOException { assertFalse("Switch channel not yet supported so should not be handled", - device.handleCommand(CHANNEL_SWITCH, connection, OnOffType.ON, configuration)); - } - - @Ignore("Ignore handle led command because led support in binding not (yet) implemented.") - @Test - public void testHandleCommandLed() throws IOException { - // assertInput("rangeextender_set_led_on"); - // setSocketReturnAssert("rangeextender_set_led_on"); - assertFalse("Led channel should be handled", - device.handleCommand(CHANNEL_LED, connection, OnOffType.ON, configuration)); + device.handleCommand(CHANNEL_UID_SWITCH, OnOffType.ON)); } @Test public void testUpdateChannelSwitch() { - assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_SWITCH, deviceState)); + assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_SWITCH, deviceState)); } @Test public void testUpdateChannelLed() { - assertSame("Led should be on", OnOffType.ON, device.updateChannel(CHANNEL_LED, deviceState)); + assertSame("Led should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_LED, deviceState)); } @Test public void testUpdateChannelOther() { - assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, device.updateChannel("OTHER", deviceState)); + assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, + device.updateChannel(CHANNEL_UID_OTHER, deviceState)); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDeviceTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDeviceTest.java index bcdfb04dc23a9..c045a4c319e27 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDeviceTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/device/SwitchDeviceTest.java @@ -13,10 +13,11 @@ package org.openhab.binding.tplinksmarthome.internal.device; import static org.junit.Assert.*; -import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; +import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.*; import java.io.IOException; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.types.UnDefType; import org.junit.Test; @@ -26,42 +27,40 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ -public class SwitchDeviceTest extends DeviceTestBase { - - private final SwitchDevice device = new SwitchDevice(); +@NonNullByDefault +public class SwitchDeviceTest extends DeviceTestBase { public SwitchDeviceTest() throws IOException { - super("plug_get_sysinfo_response"); + super(new SwitchDevice(), "plug_get_sysinfo_response"); } @Test public void testHandleCommandSwitch() throws IOException { assertInput("plug_set_relay_state_on"); setSocketReturnAssert("plug_set_relay_state_on"); - assertTrue("Switch channel should be handled", - device.handleCommand(CHANNEL_SWITCH, connection, OnOffType.ON, configuration)); + assertTrue("Switch channel should be handled", device.handleCommand(CHANNEL_UID_SWITCH, OnOffType.ON)); } @Test public void testHandleCommandLed() throws IOException { assertInput("plug_set_led_on"); setSocketReturnAssert("plug_set_led_on"); - assertTrue("Led channel should be handled", - device.handleCommand(CHANNEL_LED, connection, OnOffType.ON, configuration)); + assertTrue("Led channel should be handled", device.handleCommand(CHANNEL_UID_LED, OnOffType.ON)); } @Test public void testUpdateChannelSwitch() { - assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_SWITCH, deviceState)); + assertSame("Switch should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_SWITCH, deviceState)); } @Test public void testUpdateChannelLed() { - assertSame("Led should be on", OnOffType.ON, device.updateChannel(CHANNEL_LED, deviceState)); + assertSame("Led should be on", OnOffType.ON, device.updateChannel(CHANNEL_UID_LED, deviceState)); } @Test public void testUpdateChannelOther() { - assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, device.updateChannel("OTHER", deviceState)); + assertSame("Unknown channel should return UNDEF", UnDefType.UNDEF, + device.updateChannel(CHANNEL_UID_OTHER, deviceState)); } } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandlerTest.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandlerTest.java index 54da03d5f550d..0b3daa77b0036 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandlerTest.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/handler/SmartHomeHandlerTest.java @@ -16,11 +16,12 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; +import static org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants.CHANNEL_UID_SWITCH; import static org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeBindingConstants.*; import java.io.IOException; -import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.OnOffType; @@ -37,6 +38,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; +import org.openhab.binding.tplinksmarthome.internal.ChannelUIDConstants; import org.openhab.binding.tplinksmarthome.internal.Commands; import org.openhab.binding.tplinksmarthome.internal.Connection; import org.openhab.binding.tplinksmarthome.internal.TPLinkSmartHomeConfiguration; @@ -49,24 +51,23 @@ * * @author Hilbrand Bouwkamp - Initial contribution */ +@NonNullByDefault public class SmartHomeHandlerTest { - private static final String CHANNEL_PREFIX = "binding:tplinksmarthome:1234:"; - - private SmartHomeHandler handler; + private @NonNullByDefault({}) SmartHomeHandler handler; @Mock - private Connection connection; + private @NonNullByDefault({}) Connection connection; @Mock - private ThingHandlerCallback callback; + private @NonNullByDefault({}) ThingHandlerCallback callback; @Mock - private Thing thing; + private @NonNullByDefault({}) Thing thing; @Mock - private SmartHomeDevice smartHomeDevice; + private @NonNullByDefault({}) SmartHomeDevice smartHomeDevice; @Mock - private TPLinkSmartHomeDiscoveryService discoveryService; + private @NonNullByDefault({}) TPLinkSmartHomeDiscoveryService discoveryService; - private @NonNull final Configuration configuration = new Configuration(); + private final Configuration configuration = new Configuration(); @Before public void setUp() throws IOException { @@ -83,7 +84,8 @@ Connection createConnection(TPLinkSmartHomeConfiguration config) { return connection; } }; - when(smartHomeDevice.handleCommand(eq(CHANNEL_SWITCH), eq(connection), any(), any())).thenReturn(true); + when(smartHomeDevice.handleCommand(eq(CHANNEL_UID_SWITCH), any())).thenReturn(true); + when(callback.isChannelLinked(any())).thenReturn(true); handler.setCallback(callback); } @@ -118,7 +120,7 @@ public void testHandleCommandRefreshTypeRangeExtender() throws IOException { private void assertHandleCommandRefreshType(int expectedRssi) { handler.initialize(); - ChannelUID channelUID = new ChannelUID(CHANNEL_PREFIX + CHANNEL_RSSI); + ChannelUID channelUID = ChannelUIDConstants.CHANNEL_UID_RSSI; handler.handleCommand(channelUID, RefreshType.REFRESH); ArgumentCaptor stateCaptor = ArgumentCaptor.forClass(State.class); verify(callback).stateUpdated(eq(channelUID), stateCaptor.capture()); @@ -128,8 +130,8 @@ private void assertHandleCommandRefreshType(int expectedRssi) { @Test public void testHandleCommandOther() throws InterruptedException { handler.initialize(); - ChannelUID channelUID = new ChannelUID(CHANNEL_PREFIX + CHANNEL_SWITCH); - Mockito.doReturn(OnOffType.ON).when(smartHomeDevice).updateChannel(eq(channelUID.getId()), any()); + ChannelUID channelUID = ChannelUIDConstants.CHANNEL_UID_SWITCH; + Mockito.doReturn(OnOffType.ON).when(smartHomeDevice).updateChannel(eq(channelUID), any()); handler.handleCommand(channelUID, RefreshType.REFRESH); ArgumentCaptor stateCaptor = ArgumentCaptor.forClass(State.class); verify(callback).stateUpdated(eq(channelUID), stateCaptor.capture()); diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/model/ModelTestUtil.java b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/model/ModelTestUtil.java index 906e017377d95..cbf44bfc425c1 100644 --- a/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/model/ModelTestUtil.java +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/java/org/openhab/binding/tplinksmarthome/internal/model/ModelTestUtil.java @@ -28,6 +28,8 @@ @NonNullByDefault public final class ModelTestUtil { + public static final Gson GSON = GsonUtil.createGson(); + private ModelTestUtil() { // Util class } @@ -42,8 +44,9 @@ private ModelTestUtil() { * @return instance of clazz with read data from json file * @throws IOException when file could not be read. */ - public static T toJson(Gson gson, String filename, Class clazz) throws IOException { - return gson.fromJson(readJson(filename), clazz); + public static T jsonFromFile(String filename, Class clazz) throws IOException { + return GSON.fromJson(readJson(filename), clazz); + } /** @@ -60,4 +63,5 @@ public static String readJson(String filename) throws IOException { .toString(ModelTestUtil.class.getResourceAsStream(filename + ".json"), StandardCharsets.UTF_8.name()) .replaceAll("[\n\r\t ]", ""); } + } diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime.json new file mode 100644 index 0000000000000..fe1c17fba8307 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime.json @@ -0,0 +1 @@ +{"context": {"child_ids": ["00000000000000000000000000000001"]}, "emeter": {"get_realtime": {}}} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime_response.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime_response.json new file mode 100644 index 0000000000000..fe1c17fba8307 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_realtime_response.json @@ -0,0 +1 @@ +{"context": {"child_ids": ["00000000000000000000000000000001"]}, "emeter": {"get_realtime": {}}} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_sysinfo_response.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_sysinfo_response.json new file mode 100644 index 0000000000000..e6228e293cd36 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_get_sysinfo_response.json @@ -0,0 +1,78 @@ +{ + "system": { + "get_sysinfo": { + "alias": "powerstip", + "child_num": 6, + "children": [ + { + "alias": "plug 1", + "id": "00000000000000000000000000000001", + "next_action": { + "type": -1 + }, + "on_time": 12345, + "state": 1 + }, + { + "alias": "plug 2", + "id": "00000000000000000000000000000002", + "next_action": { + "type": -1 + }, + "on_time": 0, + "state": 0 + }, + { + "alias": "plug 3", + "id": "00000000000000000000000000000003", + "next_action": { + "type": -1 + }, + "on_time": 0, + "state": 0 + }, + { + "alias": "plug 4", + "id": "00000000000000000000000000000004", + "next_action": { + "type": -1 + }, + "on_time": 0, + "state": 0 + }, + { + "alias": "plug 5", + "id": "00000000000000000000000000000005", + "next_action": { + "type": -1 + }, + "on_time": 0, + "state": 0 + }, + { + "alias": "plug 6", + "id": "00000000000000000000000000000006", + "next_action": { + "type": -1 + }, + "on_time": 0, + "state": 0 + } + ], + "deviceId": "00000000000000000000000000000000", + "feature": "TIM:ENE", + "hwId": "00000000000000000000000000000000", + "hw_ver": "1.0", + "latitude_i": 0, + "led_off": 0, + "longitude_i": -0, + "mac": "00:00:00:00:00:00", + "mic_type": "IOT.SMARTPLUGSWITCH", + "model": "HS300(US)", + "oemId": "00000000000000000000000000000000", + "rssi": -10, + "sw_ver": "1.0.6 Build 180627 Rel.081000", + "updating": 0 + } + } +} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state.json new file mode 100644 index 0000000000000..3253ea2003bd8 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state.json @@ -0,0 +1 @@ +{"context":{"child_ids":["00000000000000000000000000000002"]},"system":{"set_relay_state":{"state":1}}} diff --git a/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state_response.json b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state_response.json new file mode 100644 index 0000000000000..3253ea2003bd8 --- /dev/null +++ b/bundles/org.openhab.binding.tplinksmarthome/src/test/resources/org/openhab/binding/tplinksmarthome/internal/model/hs300_set_relay_state_response.json @@ -0,0 +1 @@ +{"context":{"child_ids":["00000000000000000000000000000002"]},"system":{"set_relay_state":{"state":1}}} diff --git a/bundles/org.openhab.binding.xmltv/pom.xml b/bundles/org.openhab.binding.xmltv/pom.xml index f9a587d4f19d1..33213a4e17be8 100644 --- a/bundles/org.openhab.binding.xmltv/pom.xml +++ b/bundles/org.openhab.binding.xmltv/pom.xml @@ -33,10 +33,10 @@ provided - javax.activation - activation - 1.1.1 - provided + org.apache.servicemix.specs + org.apache.servicemix.specs.activation-api-1.1 + 2.9.0 + provided diff --git a/bundles/org.openhab.binding.xmppclient/README.md b/bundles/org.openhab.binding.xmppclient/README.md index 412f8613ef54d..db7d9c04a2fd6 100644 --- a/bundles/org.openhab.binding.xmppclient/README.md +++ b/bundles/org.openhab.binding.xmppclient/README.md @@ -8,24 +8,40 @@ xmppBridge - Basic XMPP (Jabber) client thing, that can send and receive message ## Thing Configuration -**xmppBridge** parameters: +Sample configurations: + +``` +Bridge xmppclient:xmppBridge:xmpp "XMPP Client" [ username="openhab", domain="example.com", password="********" ] { + Channels: + Trigger String : xmpp_command [ separator="##" ] +} +``` -| 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 | +``` +Bridge xmppclient:xmppBridge:xmpp "XMPP Client" [ host="xmpp.example.com", port=7222, username="openhab", domain="example.com", password="********" ] { + Channels: + Trigger String : xmpp_command [ separator="##" ] +} +``` + +**xmppBridge** parameters: +| Name | Label | Description | Required | Default value | +|----------|--------------------|-------------------------------------------|-----------|-----------------------| +| username | Username | The XMPP username (left part of JID) | true | - | +| domain | Domain | The XMPP domain name (right part of JID) | true | - | +| password | Password | The XMPP user password | true | - | +| host | Server Hostname/IP | The IP/Hostname of the XMPP server | false | as "domain" parameter | +| port | XMPP server Port | The typical port is 5222 | false | 5222 | ## 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 | +| 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 diff --git a/bundles/org.openhab.binding.xmppclient/src/main/feature/feature.xml b/bundles/org.openhab.binding.xmppclient/src/main/feature/feature.xml index fb23e4f4b80cd..d6fc0ab5158a7 100644 --- a/bundles/org.openhab.binding.xmppclient/src/main/feature/feature.xml +++ b/bundles/org.openhab.binding.xmppclient/src/main/feature/feature.xml @@ -4,8 +4,7 @@ openhab-runtime-base - wrap - 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 @@ -14,9 +13,11 @@ 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.apache.servicemix.bundles/org.apache.servicemix.bundles.xpp3/1.1.4c_7 + + mvn:org.igniterealtime.smack/smack-resolver-javax/4.3.3 + mvn:org.igniterealtime.smack/smack-java7/4.3.3 mvn:org.openhab.addons.bundles/org.openhab.binding.xmppclient/${project.version} diff --git a/bundles/org.openhab.io.azureiothub/pom.xml b/bundles/org.openhab.io.azureiothub/pom.xml index 97e505c31766a..586527c955477 100644 --- a/bundles/org.openhab.io.azureiothub/pom.xml +++ b/bundles/org.openhab.io.azureiothub/pom.xml @@ -15,20 +15,20 @@ - com.microsoft.azure.sdk.iot - iot-device-client + org.openhab.osgiify + com.microsoft.azure.sdk.iot.iot-device-client 1.2.27 provided - com.microsoft.azure.sdk.iot - iot-service-client + org.openhab.osgiify + com.microsoft.azure.sdk.iot.iot-service-client 1.4.20 provided - com.microsoft.azure - azure-eventhubs + org.openhab.osgiify + com.microsoft.azure.azure-eventhubs 0.14.0 provided diff --git a/bundles/org.openhab.io.azureiothub/src/main/feature/feature.xml b/bundles/org.openhab.io.azureiothub/src/main/feature/feature.xml index 5ea7393feb40e..08e3fdf36c00e 100644 --- a/bundles/org.openhab.io.azureiothub/src/main/feature/feature.xml +++ b/bundles/org.openhab.io.azureiothub/src/main/feature/feature.xml @@ -5,10 +5,9 @@ openhab-runtime-base openhab-transport-mqtt - 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:org.openhab.osgiify/com.microsoft.azure.sdk.iot.iot-device-client/1.2.27 + mvn:org.openhab.osgiify/com.microsoft.azure.sdk.iot.iot-service-client/1.4.20 + mvn:org.openhab.osgiify/com.microsoft.azure.azure-eventhubs/0.14.0 mvn:javax.json/javax.json-api/1.0 mvn:org.openhab.addons.bundles/org.openhab.io.azureiothub/${project.version} diff --git a/bundles/org.openhab.io.homekit/pom.xml b/bundles/org.openhab.io.homekit/pom.xml index b8ee5915cd836..aaae838aadb1d 100644 --- a/bundles/org.openhab.io.homekit/pom.xml +++ b/bundles/org.openhab.io.homekit/pom.xml @@ -28,19 +28,49 @@ io.netty - netty-all + netty-common 4.0.32.Final provided - com.nimbusds - srp6a + io.netty + netty-buffer + 4.0.32.Final + provided + + + io.netty + netty-transport + 4.0.32.Final + provided + + + io.netty + netty-handler + 4.0.32.Final + provided + + + io.netty + netty-codec + 4.0.32.Final + provided + + + io.netty + netty-codec-http + 4.0.32.Final + provided + + + org.openhab.osgiify + com.nimbusds.srp6a 1.5.2 provided - net.vrallev.ecc - ecc-25519-java + org.openhab.osgiify + net.vrallev.ecc.ecc-25519-java 1.0.1 provided @@ -51,8 +81,8 @@ provided - org.zeromq - curve25519-java + org.openhab.osgiify + org.zeromq.curve25519-java 0.1.0 provided diff --git a/bundles/org.openhab.io.homekit/src/main/feature/feature.xml b/bundles/org.openhab.io.homekit/src/main/feature/feature.xml index 80c451c0d2c67..05c206a067f5e 100644 --- a/bundles/org.openhab.io.homekit/src/main/feature/feature.xml +++ b/bundles/org.openhab.io.homekit/src/main/feature/feature.xml @@ -5,14 +5,18 @@ openhab-runtime-base openhab-transport-mdns - wrap mvn:org.glassfish/javax.json/1.0.4 mvn:javax.json/javax.json-api/1.0 mvn:org.bouncycastle/bcprov-jdk15on/1.51 - wrap:mvn:io.netty/netty-all/4.0.32.Final$Bundle-Name=Netty%20All&Bundle-SymbolicName=io.netty.netty-all&Bundle-Version=4.0.32 - wrap:mvn:com.nimbusds/srp6a/1.5.2$Bundle-Name=Nimbus%20SRP6a&Bundle-SymbolicName=com.nimbusds.srp6a&Bundle-Version=1.5.2 - wrap:mvn:net.vrallev.ecc/ecc-25519-java/1.0.1$Bundle-Name=ECC-25519&Bundle-SymbolicName=net.vrallev.ecc.ecc-25519-java&Bundle-Version=1.0.1 - wrap:mvn:org.zeromq/curve25519-java/0.1.0$Bundle-Name=curve25519-java&Bundle-SymbolicName=org.zeromq.curve25519-java&Bundle-Version=0.1.0 + mvn:io.netty/netty-common/4.0.32.Final + mvn:io.netty/netty-buffer/4.0.32.Final + mvn:io.netty/netty-transport/4.0.32.Final + mvn:io.netty/netty-handler/4.0.32.Final + mvn:io.netty/netty-codec/4.0.32.Final + mvn:io.netty/netty-codec-http/4.0.32.Final + mvn:org.openhab.osgiify/com.nimbusds.srp6a/1.5.2 + mvn:org.openhab.osgiify/net.vrallev.ecc.ecc-25519-java/1.0.1 + mvn:org.openhab.osgiify/org.zeromq.curve25519-java/0.1.0 mvn:org.openhab.addons.bundles/org.openhab.io.homekit/${project.version} 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 index 26e16582dde2a..344ec0588a77f 100644 --- 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 @@ -25,7 +25,7 @@ */ public class JerseyServletContainerInitializer implements ServletContainerInitializer { @Override - public void onStartup(Set> c, ServletContext ctx) throws ServletException { + public void onStartup(Set> c, ServletContext ctx) { } } 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 index dce7b2bc1bb92..d7133f31adbd2 100644 --- 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 @@ -91,8 +91,7 @@ public static class JerseyApplication extends Application { public class LogAccessInterceptor implements ContainerResponseFilter { @NonNullByDefault({}) @Override - public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) - throws IOException { + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) { if (!logger.isDebugEnabled()) { return; @@ -117,7 +116,7 @@ public void filter(ContainerRequestContext requestContext, ContainerResponseCont // 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; + protected @Nullable UpnpServer discovery; @Reference protected @NonNullByDefault({}) ConfigStore cs; @Reference @@ -201,10 +200,11 @@ public void handleEvent(@Nullable Event event) { 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) { + UpnpServer localDiscovery = discovery; + if (localDiscovery == null) { logger.warn("The UPnP Server service has not been started!"); - } else if (!discovery.upnpAnnouncementThreadRunning()) { - discovery.handleEvent(null); + } else if (!localDiscovery.upnpAnnouncementThreadRunning()) { + localDiscovery.handleEvent(null); } statusResource.startUpnpSelfTest(); logger.info("Hue Emulation service available under {}", RESTAPI_PATH); 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 index a6e0cbec60ba9..4d56fb1e4f116 100644 --- 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 @@ -113,7 +113,7 @@ public synchronized void dispose() { } @Override - public Duration call() throws Exception { + public Duration call() { ((TriggerHandlerCallback) callback).triggered(module, null); config.repeat -= 1; if (config.repeat == 0) { 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 index 09ba6045c7959..53ef26ffa613f 100644 --- 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 @@ -78,7 +78,7 @@ public Response getReducedConfigApi() { @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 { + @PathParam("username") @ApiParam(value = "username") String username) { if (!userManagement.authorizeUser(username)) { return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); } @@ -91,7 +91,7 @@ public Response getAllApi(@Context UriInfo uri, @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 { + @PathParam("username") @ApiParam(value = "username") String username) { if (!userManagement.authorizeUser(username)) { return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); } @@ -104,7 +104,7 @@ public Response getFullConfigApi(@Context UriInfo uri, @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 { + @PathParam("username") @ApiParam(value = "username") String username, String body) { if (!userManagement.authorizeUser(username)) { return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); } 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 index e66d62fb92759..f4e5c5fa10563 100644 --- 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 @@ -32,6 +32,7 @@ import javax.ws.rs.core.UriInfo; 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.events.EventPublisher; import org.eclipse.smarthome.core.items.GenericItem; @@ -111,7 +112,7 @@ public class LightsAndGroups implements RegistryChangeListener { @Reference protected @NonNullByDefault({}) ItemRegistry itemRegistry; @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL) - protected volatile @NonNullByDefault({}) EventPublisher eventPublisher; + protected volatile @Nullable EventPublisher eventPublisher; /** * Registers to the {@link ItemRegistry} and enumerates currently existing items. @@ -373,9 +374,10 @@ public Response setLightStateApi(@Context UriInfo uri, // // If a command could be created, post it to the framework now if (command != null) { - if (eventPublisher != null) { + EventPublisher localEventPublisher = eventPublisher; + if (localEventPublisher != null) { logger.debug("sending {} to {}", command, itemUID); - eventPublisher.post(ItemEventFactory.createCommandEvent(itemUID, command, "hueemulation")); + localEventPublisher.post(ItemEventFactory.createCommandEvent(itemUID, command, "hueemulation")); } else { logger.warn("No event publisher. Cannot post item '{}' command!", itemUID); } @@ -418,8 +420,10 @@ public Response setGroupActionApi(@Context UriInfo uri, // // 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")); + EventPublisher localEventPublisher = eventPublisher; + if (localEventPublisher != null) { + localEventPublisher + .post(ItemEventFactory.createCommandEvent(groupItem.getUID(), command, "hueemulation")); } else { logger.warn("No event publisher. Cannot post item '{}' command!", groupItem.getUID()); } 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 index 56b30bc5544fc..7f16240bc0419 100644 --- 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 @@ -239,7 +239,7 @@ protected static List createActions(String uid, List hueActi @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 { + @PathParam("username") @ApiParam(value = "username") String username) { if (!userManagement.authorizeUser(username)) { return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); } 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 index 64d6529efcbe6..7ee44ba57b102 100644 --- 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 @@ -169,7 +169,7 @@ public void updated(Rule oldElement, Rule element) { @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 { + @PathParam("username") @ApiParam(value = "username") String username) { if (!userManagement.authorizeUser(username)) { return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); } 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 index 29e749e31ec9a..80472599edca8 100644 --- 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 @@ -232,7 +232,7 @@ protected static Rule createRule(String uid, RuleBuilder builder, List o @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 { + @PathParam("username") @ApiParam(value = "username") String username) { if (!userManagement.authorizeUser(username)) { return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized"); } 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 index a8157d0ab918d..b7cdc3dcd76d8 100644 --- 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 @@ -54,7 +54,7 @@ @NonNullByDefault public class StatusResource implements RegistryListener { @Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY) - protected @NonNullByDefault({}) UpnpServer discovery; + protected @Nullable UpnpServer discovery; @Reference protected @NonNullByDefault({}) ConfigStore cs; @Reference @@ -125,12 +125,11 @@ private static String TR(String s) { @Path("status") @Produces("text/html") public String getStatus() { - if (discovery == null) { // Optional service wiring + UpnpServer localDiscovery = discovery; + if (localDiscovery == 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

" @@ -145,18 +144,18 @@ public String getStatus() { + // "

Users

    %s
"; - final String users = cs.ds.config.whitelist.entrySet().stream().map(user -> "
  • " + user.getKey() + " " + 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); + String url = "http://" + cs.ds.config.ipaddress + ":" + String.valueOf(localDiscovery.getDefaultport()); - final String reachable = discovery.selfTests().stream() + String reachable = localDiscovery.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; + String upnps; if (registry != null) { upnps = registry.getRemoteDevices().stream().map(device -> getDetails(device)) .map(details -> TR(TD(details.getSerialNumber()) + TD(details.getFriendlyName()))) @@ -165,7 +164,7 @@ public String getStatus() { upnps = TR(TD("service not available") + TD("")); } - if (!discovery.upnpAnnouncementThreadRunning()) { + if (!localDiscovery.upnpAnnouncementThreadRunning()) { selfTestUpnpFound = upnpStatus.upnp_announcement_thread_not_running; } @@ -224,7 +223,9 @@ public void remoteDeviceUpdated(Registry registry, RemoteDevice device) { @NonNullByDefault({}) @Override public void remoteDeviceRemoved(Registry registry, RemoteDevice device) { - if (selfTestUpnpFound != upnpStatus.success || discovery.upnpAnnouncementThreadRunning()) { + UpnpServer localDiscovery = discovery; + if (selfTestUpnpFound != upnpStatus.success || localDiscovery == null + || localDiscovery.upnpAnnouncementThreadRunning()) { return; } DeviceDetails details = getDetails(device); @@ -237,7 +238,9 @@ public void remoteDeviceRemoved(Registry registry, RemoteDevice device) { @NonNullByDefault({}) @Override public void localDeviceAdded(Registry registry, LocalDevice device) { - if (selfTestUpnpFound == upnpStatus.success || discovery.upnpAnnouncementThreadRunning()) { + UpnpServer localDiscovery = discovery; + if (selfTestUpnpFound == upnpStatus.success || localDiscovery == null + || localDiscovery.upnpAnnouncementThreadRunning()) { return; } checkForDevice(getDetails(device)); 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 index 98c127f65aa91..a2de10d0d0c6e 100644 --- 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 @@ -23,6 +23,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.io.hueemulation.internal.HueEmulationConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The upnp server runtime configuration. Based on a {@link HueEmulationConfig} and determined ip address and port. @@ -33,6 +35,9 @@ */ @NonNullByDefault class HueEmulationConfigWithRuntime extends Thread implements Runnable { + + private final Logger logger = LoggerFactory.getLogger(HueEmulationConfigWithRuntime.class); + final @NonNullByDefault({}) HueEmulationConfig config; final InetAddress address; final String addressString; @@ -93,7 +98,8 @@ String getMulticastAddress() { public synchronized CompletableFuture<@Nullable HueEmulationConfigWithRuntime> startNow( @Nullable HueEmulationConfigWithRuntime ignored) { if (hasAlreadyBeenStarted) { - throw new IllegalStateException("Cannot restart thread"); + logger.debug("Cannot restart thread"); + return future; } hasAlreadyBeenStarted = true; super.start(); 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 index 0c277a0940a6a..e43b62c3678b3 100644 --- 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 @@ -17,16 +17,7 @@ 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.net.*; import java.nio.ByteBuffer; import java.nio.channels.ClosedSelectorException; import java.nio.channels.DatagramChannel; @@ -37,7 +28,6 @@ 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; @@ -155,7 +145,7 @@ public UpnpServer(Executor executor) { */ @NonNullByDefault({}) @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { if (xmlDocWithAddress == null || xmlDocWithAddress.isEmpty()) { resp.sendError(HttpServletResponse.SC_NOT_FOUND); return; @@ -208,7 +198,7 @@ protected void activate() { } private void useAddressPort(HueEmulationConfigWithRuntime r) { - final String urlBase = "http://" + r.addressString + ":" + String.valueOf(r.port); + final String urlBase = "http://" + r.addressString + ":" + r.port; this.baseurl = urlBase + DISCOVERY_FILE; final String[] stVersions = { "upnp:rootdevice", "urn:schemas-upnp-org:device:basic:1", @@ -271,7 +261,7 @@ private void useAddressPort(HueEmulationConfigWithRuntime r) { ip = "[" + ip.split("%")[0] + "]"; } try { - url = "http://" + ip + ":" + String.valueOf(config.port) + DISCOVERY_FILE; + url = "http://" + ip + ":" + 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)); @@ -308,7 +298,7 @@ private void useAddressPort(HueEmulationConfigWithRuntime r) { 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()); + logger.warn("The picked default IP address is not valid: {}", e.getMessage()); throw new IllegalStateException(e); } return r; @@ -339,7 +329,7 @@ private void useAddressPort(HueEmulationConfigWithRuntime r) { * 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 + * Therefore this method is "synchronized" and chains a completable future for each call to re-evaluate the config * after the former future has finished. */ @Override @@ -363,8 +353,6 @@ public synchronized void handleEvent(@Nullable Event event) { /** * Stops the upnp server from running - * - * @throws InterruptedException */ @Deactivate public void deactivate() { @@ -375,7 +363,7 @@ public void deactivate() { } } - private void handleRead(SelectionKey key, Set addresses) throws IOException { + private void handleRead(SelectionKey key) throws IOException { logger.trace("upnp thread handle received message"); DatagramChannel channel = (DatagramChannel) key.channel(); ClientRecord clntRec = (ClientRecord) key.attachment(); @@ -424,22 +412,25 @@ public void accept(HueEmulationConfigWithRuntime threadContext) { boolean hasIPv4 = false; boolean hasIPv6 = false; - try (DatagramChannel channel = DatagramChannel.open(); Selector selector = Selector.open()) { + try ( + DatagramChannel channelV4 = DatagramChannel.open(StandardProtocolFamily.INET); + DatagramChannel channelV6 = DatagramChannel.open(StandardProtocolFamily.INET6); + Selector selector = Selector.open()) { threadContext.asyncIOselector = selector; - channel.setOption(StandardSocketOptions.SO_REUSEADDR, true) - .setOption(StandardSocketOptions.IP_MULTICAST_LOOP, true).bind(new InetSocketAddress(UPNP_PORT)); + bind(channelV4); + bind(channelV6); for (InetAddress address : cs.getDiscoveryIps()) { NetworkInterface networkInterface = NetworkInterface.getByInetAddress(address); if (networkInterface == null) { continue; } if (address instanceof Inet4Address) { - channel.join(MULTI_ADDR_IPV4, networkInterface); + channelV4.join(MULTI_ADDR_IPV4, networkInterface); hasIPv4 = true; } else { - channel.join(MULTI_ADDR_IPV6, networkInterface); + channelV6.join(MULTI_ADDR_IPV6, networkInterface); hasIPv6 = true; } } @@ -450,16 +441,18 @@ public void accept(HueEmulationConfigWithRuntime threadContext) { return; } - channel.configureBlocking(false); - channel.register(selector, SelectionKey.OP_READ, new ClientRecord()); if (hasIPv4) { + channelV4.configureBlocking(false); + channelV4.register(selector, SelectionKey.OP_READ, new ClientRecord()); try (DatagramSocket sendSocket = new DatagramSocket(new InetSocketAddress(config.address, 0))) { sendUPNPDatagrams(sendSocket, MULTI_ADDR_IPV4, UPNP_PORT); } } if (hasIPv6) { + channelV6.configureBlocking(false); + channelV6.register(selector, SelectionKey.OP_READ, new ClientRecord()); try (DatagramSocket sendSocket = new DatagramSocket()) { sendUPNPDatagrams(sendSocket, MULTI_ADDR_IPV6, UPNP_PORT); } @@ -475,7 +468,7 @@ public void accept(HueEmulationConfigWithRuntime threadContext) { while (keyIter.hasNext()) { SelectionKey key = keyIter.next(); if (key.isReadable()) { - handleRead(key, cs.getDiscoveryIps()); + handleRead(key); } keyIter.remove(); } @@ -504,6 +497,12 @@ public void accept(HueEmulationConfigWithRuntime threadContext) { } } + private void bind(DatagramChannel channel) throws IOException { + channel.setOption(StandardSocketOptions.SO_REUSEADDR, true) + .setOption(StandardSocketOptions.IP_MULTICAST_LOOP, true) + .bind(new InetSocketAddress(UPNP_PORT)); + } + /** * The upnp server performs some self-tests * 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 index d972fbfb128ca..738cc9be11f80 100644 --- 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 @@ -67,7 +67,7 @@ protected LocalDateTime getNow() { } @Before - public void setUp() throws IOException { + public void setUp() { ds = new HueDataStore(); ds.lights.put("1", new HueLightEntry(new SwitchItem("switch"), "switch", DeviceType.SwitchType)); 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 index cf99574e590a9..796c3b8cfd9b0 100644 --- 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 @@ -89,7 +89,7 @@ public void tearDown() { } @Test - public void addSwitchableByCategory() throws IOException { + public void addSwitchableByCategory() { SwitchItem item = new SwitchItem("switch1"); item.setCategory("Light"); itemRegistry.add(item); @@ -100,7 +100,7 @@ public void addSwitchableByCategory() throws IOException { } @Test - public void addSwitchableByTag() throws IOException { + public void addSwitchableByTag() { SwitchItem item = new SwitchItem("switch1"); item.addTag("Switchable"); itemRegistry.add(item); @@ -110,7 +110,7 @@ public void addSwitchableByTag() throws IOException { } @Test - public void ignoreByTag() throws IOException { + public void ignoreByTag() { SwitchItem item = new SwitchItem("switch1"); item.addTags("Switchable", "internal"); // The ignore tag will win itemRegistry.add(item); @@ -119,7 +119,7 @@ public void ignoreByTag() throws IOException { } @Test - public void addGroupSwitchableByTag() throws IOException { + public void addGroupSwitchableByTag() { GroupItem item = new GroupItem("group1", new SwitchItem("switch1")); item.addTag("Switchable"); itemRegistry.add(item); @@ -129,7 +129,7 @@ public void addGroupSwitchableByTag() throws IOException { } @Test - public void addGroupWithoutTypeByTag() throws IOException { + public void addGroupWithoutTypeByTag() { GroupItem item = new GroupItem("group1", null); item.addTag("Switchable"); @@ -142,7 +142,7 @@ public void addGroupWithoutTypeByTag() throws IOException { } @Test - public void removeGroupWithoutTypeAndTag() throws IOException { + public void removeGroupWithoutTypeAndTag() { String groupName = "group1"; GroupItem item = new GroupItem(groupName, null); item.addTag("Switchable"); @@ -157,7 +157,7 @@ public void removeGroupWithoutTypeAndTag() throws IOException { } @Test - public void updateSwitchable() throws IOException { + public void updateSwitchable() { SwitchItem item = new SwitchItem("switch1"); item.setLabel("labelOld"); item.addTag("Switchable"); @@ -187,7 +187,7 @@ public void updateSwitchable() throws IOException { } @Test - public void changeSwitchState() throws IOException { + public void changeSwitchState() { assertThat(((HueStatePlug) cs.ds.lights.get("1").state).on, is(false)); @@ -204,7 +204,7 @@ public void changeSwitchState() throws IOException { } @Test - public void changeGroupItemSwitchState() throws IOException { + public void changeGroupItemSwitchState() { assertThat(((HueStatePlug) cs.ds.groups.get("10").action).on, is(false)); @@ -221,7 +221,7 @@ public void changeGroupItemSwitchState() throws IOException { } @Test - public void changeOnValue() throws IOException { + public void changeOnValue() { assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(false)); @@ -235,7 +235,7 @@ public void changeOnValue() throws IOException { } @Test - public void changeOnAndBriValues() throws IOException { + public void changeOnAndBriValues() { assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(false)); assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).bri, is(0)); @@ -250,7 +250,7 @@ public void changeOnAndBriValues() throws IOException { } @Test - public void changeHueSatValues() throws IOException { + public void changeHueSatValues() { HueLightEntry hueDevice = cs.ds.lights.get("2"); hueDevice.item.setState(OnOffType.ON); hueDevice.state.as(HueStateColorBulb.class).on = true; @@ -271,7 +271,7 @@ public void changeHueSatValues() throws IOException { * Amazon echos are setting ct only, if commanded to turn a light white. */ @Test - public void changeCtValue() throws IOException { + public void changeCtValue() { HueLightEntry hueDevice = cs.ds.lights.get("2"); hueDevice.item.setState(OnOffType.ON); hueDevice.state.as(HueStateColorBulb.class).on = true; @@ -292,7 +292,7 @@ public void changeCtValue() throws IOException { } @Test - public void switchOnWithXY() throws IOException { + public void switchOnWithXY() { assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).on, is(false)); assertThat(((HueStateColorBulb) cs.ds.lights.get("2").state).bri, is(0)); @@ -313,8 +313,7 @@ public void switchOnWithXY() throws IOException { } @Test - public void allLightsAndSingleLight() - throws InterruptedException, ExecutionException, TimeoutException, IOException { + public void allLightsAndSingleLight() { Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/lights").request().get(); assertEquals(200, response.getStatus()); 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 index 4418ecaf0838b..a6e5583f2423f 100644 --- 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 @@ -92,7 +92,7 @@ public void tearDown() { } @Test - public void renameSensor() throws IOException { + public void renameSensor() { assertThat(cs.ds.sensors.get("switch1").name, is("name1")); @@ -107,7 +107,7 @@ public void renameSensor() throws IOException { } @Test - public void allAndSingleSensor() throws InterruptedException, ExecutionException, TimeoutException, IOException { + public void allAndSingleSensor() { Response response = commonSetup.client.target(commonSetup.basePath + "/testuser/sensors").request().get(); assertEquals(200, response.getStatus()); 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 index 12727fb1045a0..1c6dd124e796f 100644 --- 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 @@ -68,17 +68,17 @@ public void tearDown() { } @Test - public void invalidUser() throws IOException { + public void invalidUser() { assertFalse(commonSetup.userManagement.authorizeUser("blub")); } @Test - public void validUser() throws IOException { + public void validUser() { assertTrue(commonSetup.userManagement.authorizeUser("testuser")); } @Test - public void configStoreRestartOnNoUUID() throws IOException { + public void configStoreRestartOnNoUUID() { ConfigStore configStore = new ConfigStoreWithoutMetadata(commonSetup.networkAddressService, commonSetup.configAdmin, commonSetup.scheduler); @@ -95,7 +95,7 @@ public void configStoreRestartOnNoUUID() throws IOException { } @Test - public void addUser() throws IOException { + public void addUser() { // GET should fail assertEquals(405, commonSetup.client.target(commonSetup.basePath).request().get().getStatus()); @@ -123,8 +123,7 @@ public void addUser() throws IOException { } @Test - public void UnauthorizedAccessTest() - throws InterruptedException, ExecutionException, TimeoutException, IOException { + public void UnauthorizedAccessTest() { // Unauthorized config Response response; 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 index aa7c366b49648..200a4d6f109c7 100644 --- 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 @@ -90,12 +90,12 @@ public Item add(Item element) { } @Override - public Item getItem(@Nullable String name) throws ItemNotFoundException { + public Item getItem(@Nullable String name) { return items.get(name); } @Override - public Item getItemByPattern(String name) throws ItemNotFoundException, ItemNotUniqueException { + public Item getItemByPattern(String name) { return items.get(name); } diff --git a/bundles/org.openhab.io.javasound/pom.xml b/bundles/org.openhab.io.javasound/pom.xml index 95c3754d778cb..385915f4530fe 100644 --- a/bundles/org.openhab.io.javasound/pom.xml +++ b/bundles/org.openhab.io.javasound/pom.xml @@ -15,8 +15,8 @@ - javazoom - jlayer + org.openhab.osgiify + javazoom.jlayer 1.0.1 provided diff --git a/bundles/org.openhab.io.javasound/src/main/feature/feature.xml b/bundles/org.openhab.io.javasound/src/main/feature/feature.xml index 0c612ee09a602..a8baf9860eced 100644 --- a/bundles/org.openhab.io.javasound/src/main/feature/feature.xml +++ b/bundles/org.openhab.io.javasound/src/main/feature/feature.xml @@ -4,8 +4,7 @@ openhab-runtime-base - wrap - wrap:mvn:javazoom/jlayer/1.0.1$Bundle-Name=JLayer&Bundle-SymbolicName=javazoom.jlayer&Bundle-Version=1.0.1 + mvn:org.openhab.osgiify/javazoom.jlayer/1.0.1 mvn:org.openhab.addons.bundles/org.openhab.io.javasound/${project.version} diff --git a/bundles/org.openhab.io.mqttembeddedbroker/pom.xml b/bundles/org.openhab.io.mqttembeddedbroker/pom.xml index 66928e739399f..7f9bb9a81f060 100644 --- a/bundles/org.openhab.io.mqttembeddedbroker/pom.xml +++ b/bundles/org.openhab.io.mqttembeddedbroker/pom.xml @@ -19,8 +19,8 @@ - io.moquette - moquette-broker + org.openhab.osgiify + io.moquette.moquette-broker 0.12.1 provided diff --git a/bundles/org.openhab.io.mqttembeddedbroker/src/main/feature/feature.xml b/bundles/org.openhab.io.mqttembeddedbroker/src/main/feature/feature.xml index d65441abfb41d..8b4da52ef12d3 100644 --- a/bundles/org.openhab.io.mqttembeddedbroker/src/main/feature/feature.xml +++ b/bundles/org.openhab.io.mqttembeddedbroker/src/main/feature/feature.xml @@ -5,7 +5,6 @@ openhab-runtime-base openhab-transport-mqtt - wrap mvn:io.netty/netty-common/4.1.34.Final mvn:io.netty/netty-buffer/4.1.34.Final mvn:io.netty/netty-transport/4.1.34.Final @@ -14,7 +13,7 @@ mvn:io.netty/netty-resolver/4.1.34.Final mvn:io.netty/netty-handler/4.1.34.Final mvn:com.h2database/h2-mvstore/1.4.199 - wrap:mvn:io.moquette/moquette-broker/0.12.1$Bundle-Name=Moquette%20MQTT%20Broker&Bundle-SymbolicName=io.moquette.moquette-broker&Bundle-Version=0.12.1 + mvn:org.openhab.osgiify/io.moquette.moquette-broker/0.12.1 mvn:org.openhab.addons.bundles/org.openhab.io.mqttembeddedbroker/${project.version} diff --git a/bundles/org.openhab.io.openhabcloud/pom.xml b/bundles/org.openhab.io.openhabcloud/pom.xml index 86fec15ea632c..73b09eb541a87 100644 --- a/bundles/org.openhab.io.openhabcloud/pom.xml +++ b/bundles/org.openhab.io.openhabcloud/pom.xml @@ -21,26 +21,26 @@ provided - com.squareup.okhttp3 - okhttp - 3.12.1 + org.apache.servicemix.bundles + org.apache.servicemix.bundles.okhttp + 3.8.1_1 provided - com.squareup.okio - okio - 1.15.0 + org.apache.servicemix.bundles + org.apache.servicemix.bundles.okio + 1.13.0_1 provided - io.socket - socket.io-client + org.openhab.osgiify + io.socket.socket.io-client 1.0.0 provided - io.socket - engine.io-client + org.openhab.osgiify + io.socket.engine.io-client 1.0.0 provided diff --git a/bundles/org.openhab.io.openhabcloud/src/main/feature/feature.xml b/bundles/org.openhab.io.openhabcloud/src/main/feature/feature.xml index 003afd925ac8c..38f2948e155b0 100644 --- a/bundles/org.openhab.io.openhabcloud/src/main/feature/feature.xml +++ b/bundles/org.openhab.io.openhabcloud/src/main/feature/feature.xml @@ -4,12 +4,12 @@ openhab-runtime-base - wrap mvn:org.json/json/20180813 - mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okhttp/3.12.1_1 - mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okio/1.15.0_1 - 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.apache.servicemix.bundles/org.apache.servicemix.bundles.jsr305/3.0.2_1 + mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okhttp/3.8.1_1 + mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okio/1.13.0_1 + mvn:org.openhab.osgiify/io.socket.socket.io-client/1.0.0 + mvn:org.openhab.osgiify/io.socket.engine.io-client/1.0.0 mvn:org.openhab.addons.bundles/org.openhab.io.openhabcloud/${project.version} diff --git a/bundles/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 index 012df02e0fcda..b6898e36cbc35 100644 --- a/bundles/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 @@ -71,17 +71,6 @@ public class CloudClient { * Logger for this class */ private Logger logger = LoggerFactory.getLogger(CloudClient.class); - /* - * This constant defines maximum number of HTTP connections per peer - * address for HTTP client which performs local connections to openHAB - */ - private static final int HTTP_CLIENT_MAX_CONNECTIONS_PER_DEST = 200; - - /* - * This constant defines HTTP request timeout. It should be kept at about - * 30 seconds minimum to make it work for long polling requests - */ - private static final long HTTP_CLIENT_TIMEOUT = 30; /* * This variable holds base URL for the openHAB Cloud connections @@ -330,7 +319,7 @@ private void handleRequestEvent(JSONObject data) { if (data.has("protocol")) { proto = data.getString("protocol"); } - request.timeout(HTTP_CLIENT_TIMEOUT, TimeUnit.SECONDS).header("X-Forwarded-Proto", proto); + request.header("X-Forwarded-Proto", proto); if (requestMethod.equals("GET")) { request.method(HttpMethod.GET); diff --git a/bundles/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 index de876673e338d..fd0334925890c 100644 --- a/bundles/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 @@ -84,6 +84,9 @@ public class CloudService implements ActionService, CloudClientListener, EventSu private static final String CFG_MODE = "mode"; private static final String SECRET_FILE_NAME = "openhabcloud" + File.separator + "secret"; private static final String DEFAULT_URL = "https://myopenhab.org/"; + private static final int DEFAULT_LOCAL_OPENHAB_MAX_CONCURRENT_REQUESTS = 200; + private static final int DEFAULT_LOCAL_OPENHAB_REQUEST_TIMEOUT = 30000; + private static final String HTTPCLIENT_NAME = "openhabcloud"; private Logger logger = LoggerFactory.getLogger(CloudService.class); @@ -175,6 +178,11 @@ private void checkJavaVersion() { protected void deactivate() { logger.debug("openHAB Cloud connector deactivated"); cloudClient.shutdown(); + try { + httpClient.stop(); + } catch (Exception e) { + logger.debug("Could not stop Jetty http client", e); + } } @Modified @@ -216,6 +224,17 @@ protected void modified(Map config) { cloudClient.shutdown(); } + httpClient.setMaxConnectionsPerDestination(DEFAULT_LOCAL_OPENHAB_MAX_CONCURRENT_REQUESTS); + httpClient.setConnectTimeout(DEFAULT_LOCAL_OPENHAB_REQUEST_TIMEOUT); + + if (!httpClient.isRunning()) { + try { + httpClient.start(); + } catch (Exception e) { + logger.error("Could not start Jetty http client", e); + } + } + String localBaseUrl = "http://localhost:" + localPort; cloudClient = new CloudClient(httpClient, InstanceUUID.get(), getSecret(), cloudBaseUrl, localBaseUrl, remoteAccessEnabled, exposedItems); @@ -332,7 +351,8 @@ public void sendCommand(String itemName, String commandString) { @Reference protected void setHttpClientFactory(HttpClientFactory httpClientFactory) { - this.httpClient = httpClientFactory.getCommonHttpClient(); + this.httpClient = httpClientFactory.createHttpClient(HTTPCLIENT_NAME); + this.httpClient.setStopTimeout(0); } protected void unsetHttpClientFactory(HttpClientFactory httpClientFactory) { diff --git a/bundles/org.openhab.io.transport.modbus/pom.xml b/bundles/org.openhab.io.transport.modbus/pom.xml index ef45088cc03f6..f4ef30f594901 100644 --- a/bundles/org.openhab.io.transport.modbus/pom.xml +++ b/bundles/org.openhab.io.transport.modbus/pom.xml @@ -21,8 +21,8 @@ provided - net.wimpi - jamod + org.openhab.osgiify + net.wimpi.jamod 1.2.3.OH provided diff --git a/bundles/org.openhab.io.transport.modbus/src/main/feature/feature.xml b/bundles/org.openhab.io.transport.modbus/src/main/feature/feature.xml index 3b4b44828ff83..6d379010e2226 100644 --- a/bundles/org.openhab.io.transport.modbus/src/main/feature/feature.xml +++ b/bundles/org.openhab.io.transport.modbus/src/main/feature/feature.xml @@ -5,8 +5,7 @@ openhab-runtime-base openhab-transport-serial - 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.openhab.osgiify/net.wimpi.jamod/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} diff --git a/bundles/org.openhab.transform.bin2json/pom.xml b/bundles/org.openhab.transform.bin2json/pom.xml index 24e638e82b561..1d6bb4549db6d 100644 --- a/bundles/org.openhab.transform.bin2json/pom.xml +++ b/bundles/org.openhab.transform.bin2json/pom.xml @@ -15,8 +15,8 @@ - com.igormaznitsa - jbbp + org.openhab.osgiify + com.igormaznitsa.jbbp 1.4.1 provided diff --git a/bundles/org.openhab.transform.bin2json/src/main/feature/feature.xml b/bundles/org.openhab.transform.bin2json/src/main/feature/feature.xml index b088a22b8f3e3..f3934731d3911 100644 --- a/bundles/org.openhab.transform.bin2json/src/main/feature/feature.xml +++ b/bundles/org.openhab.transform.bin2json/src/main/feature/feature.xml @@ -4,8 +4,7 @@ openhab-runtime-base - wrap - wrap:mvn:com.igormaznitsa/jbbp/1.4.1$Bundle-Name=Java%20Binary%20Block%20Parser&Bundle-SymbolicName=com.igormaznitsa.jbbp&Bundle-Version=1.4.1 + mvn:org.openhab.osgiify/com.igormaznitsa.jbbp/1.4.1 mvn:org.openhab.addons.bundles/org.openhab.transform.bin2json/${project.version} diff --git a/bundles/org.openhab.transform.jinja/pom.xml b/bundles/org.openhab.transform.jinja/pom.xml index 8bb6614177a71..11b04d374a090 100644 --- a/bundles/org.openhab.transform.jinja/pom.xml +++ b/bundles/org.openhab.transform.jinja/pom.xml @@ -15,10 +15,35 @@ - com.hubspot.jinjava - jinjava + org.openhab.osgiify + com.hubspot.jinjava.jinjava 2.5.0 provided + + org.jsoup + jsoup + 1.8.3 + provided + + + com.google.guava + guava + 21.0 + provided + + + org.openhab.osgiify + com.google.re2j.re2j + 1.2 + provided + + + org.apache.commons + commons-lang3 + 3.4 + provided + + diff --git a/bundles/org.openhab.transform.jinja/src/main/feature/feature.xml b/bundles/org.openhab.transform.jinja/src/main/feature/feature.xml index 0603fc2e7acf2..d60f1049091f7 100644 --- a/bundles/org.openhab.transform.jinja/src/main/feature/feature.xml +++ b/bundles/org.openhab.transform.jinja/src/main/feature/feature.xml @@ -4,17 +4,17 @@ openhab-runtime-base - wrap 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/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 - wrap:mvn:de.odysseus.juel/juel-api/2.2.7$Bundle-Name=juel&Bundle-SymbolicName=de.odysseus.juel.juel-api&Bundle-Version=2.2.7 - wrap:mvn:de.odysseus.juel/juel-impl/2.2.7$Bundle-Name=juel&Bundle-SymbolicName=de.odysseus.juel.juel-impl&Bundle-Version=2.2.7 - wrap:mvn:com.hubspot.jinjava/jinjava/2.5.0$Bundle-Name=jinjava&Bundle-SymbolicName=com.hubspot.jinjava.jinjava&Bundle-Version=2.5.0 + mvn:org.openhab.osgiify/com.google.re2j.re2j/1.2 + mvn:de.odysseus.juel/juel-api/2.2.7 + mvn:de.odysseus.juel/juel-impl/2.2.7 + mvn:org.apache.commons/commons-lang3/3.4 + mvn:org.openhab.osgiify/com.hubspot.jinjava.jinjava/2.5.0 mvn:org.openhab.addons.bundles/org.openhab.transform.jinja/${project.version} diff --git a/bundles/org.openhab.voice.googletts/pom.xml b/bundles/org.openhab.voice.googletts/pom.xml index 1b0055a4fe573..a5c86b0271b8e 100644 --- a/bundles/org.openhab.voice.googletts/pom.xml +++ b/bundles/org.openhab.voice.googletts/pom.xml @@ -15,14 +15,26 @@ - com.google.api - api-common + org.openhab.osgiify + com.google.api.api-common 1.7.0 provided - com.google.api - gax + org.openhab.osgiify + com.google.auth.google-auth-library-credentials + 0.13.0 + provided + + + org.openhab.osgiify + com.google.auth.google-auth-library-oauth2-http + 0.13.0 + provided + + + org.openhab.osgiify + com.google.api.gax 1.42.0 provided diff --git a/bundles/org.openhab.voice.googletts/src/main/feature/feature.xml b/bundles/org.openhab.voice.googletts/src/main/feature/feature.xml index 2d45b37787216..ca87e0f2022b5 100644 --- a/bundles/org.openhab.voice.googletts/src/main/feature/feature.xml +++ b/bundles/org.openhab.voice.googletts/src/main/feature/feature.xml @@ -4,14 +4,13 @@ openhab-runtime-base - wrap - wrap:mvn:org.apache.httpcomponents/httpclient/4.5.5$Bundle-Name=Apache%20HttpComponents%20Client&Bundle-SymbolicName=org.apache.httpcomponents-httpclient&Bundle-Version=4.5.5 - wrap:mvn:org.apache.httpcomponents/httpcore/4.4.9$Bundle-Name=Apache%20HttpComponents%20Core&Bundle-SymbolicName=org.apache.httpcomponents-httpcore&Bundle-Version=4.4.9 - wrap:mvn:com.google.api/api-common/1.7.0$Bundle-Name=Google%20API%20Common&Bundle-SymbolicName=com.google.api.api-common&Bundle-Version=1.7.0 - wrap:mvn:com.google.api/gax/1.42.0$Bundle-Name=Google%20API%20GAX&Bundle-SymbolicName=com.google.api.gax&Bundle-Version=1.42.0 - wrap:mvn:com.google.auth/google-auth-library-credentials/0.13.0$Bundle-Name=Google%20Auth%20Library%20Credentials&Bundle-SymbolicName=com.google.auth.google-auth-library-credentials&Bundle-Version=0.13.0 - wrap:mvn:com.google.auth/google-auth-library-oauth2-http/0.13.0$Bundle-Name=Google%20Auth%20Library%20OAuth2%20HTTP&Bundle-SymbolicName=com.google.auth.google-auth-library-oauth2-http&Bundle-Version=0.13.0 - wrap:mvn:com.google.http-client/google-http-client-jackson2/1.27.0$Bundle-Name=Google%20HTTP%20Client%20Jackson%202&Bundle-SymbolicName=com.google.http-client.google-http-client-jackson2&Bundle-Version=1.27.0 + mvn:org.apache.httpcomponents/httpclient-osgi/4.5.5 + mvn:org.apache.httpcomponents/httpcore-osgi/4.4.9 + mvn:org.openhab.osgiify/com.google.api.api-common/1.7.0 + mvn:org.openhab.osgiify/com.google.api.gax/1.42.0 + mvn:org.openhab.osgiify/com.google.auth.google-auth-library-credentials/0.13.0 + mvn:org.openhab.osgiify/com.google.auth.google-auth-library-oauth2-http/0.13.0 + mvn:org.openhab.osgiify/com.google.http-client.google-http-client-jackson2/1.27.0 mvn:com.fasterxml.jackson.core/jackson-core/2.9.6 mvn:com.google.guava/guava/20.0 mvn:com.google.http-client/google-http-client/1.27.0 diff --git a/bundles/org.openhab.voice.pollytts/pom.xml b/bundles/org.openhab.voice.pollytts/pom.xml index 4b25a3adddd75..5e59b6bd8bdb3 100644 --- a/bundles/org.openhab.voice.pollytts/pom.xml +++ b/bundles/org.openhab.voice.pollytts/pom.xml @@ -33,14 +33,14 @@ provided - com.amazonaws - aws-java-sdk-core + org.openhab.osgiify + com.amazonaws.aws-java-sdk-core 1.11.490 provided - com.amazonaws - aws-java-sdk-polly + org.openhab.osgiify + com.amazonaws.aws-java-sdk-polly 1.11.490 provided diff --git a/bundles/org.openhab.voice.pollytts/src/main/feature/feature.xml b/bundles/org.openhab.voice.pollytts/src/main/feature/feature.xml index 46da62489f6e2..a859e07d2bdbf 100644 --- a/bundles/org.openhab.voice.pollytts/src/main/feature/feature.xml +++ b/bundles/org.openhab.voice.pollytts/src/main/feature/feature.xml @@ -4,14 +4,14 @@ openhab-runtime-base - wrap 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.9 mvn:com.fasterxml.jackson.core/jackson-core/2.9.9 mvn:com.fasterxml.jackson.core/jackson-databind/2.9.9 - wrap:mvn:com.amazonaws/aws-java-sdk-core/1.11.490$Bundle-Name=AWS%20SDK%20for%20Java%20-%20Core&Bundle-SymbolicName=com.amazonaws.aws-java-sdk-core&Bundle-Version=1.11.490 - wrap:mvn:com.amazonaws/aws-java-sdk-polly/1.11.490$Bundle-Name=AWS%20Java%20SDK%20for%20Amazon%20Polly&Bundle-SymbolicName=com.amazonaws.aws-java-sdk-polly&Bundle-Version=1.11.490 + mvn:com.fasterxml.jackson.dataformat/jackson-dataformat-cbor/2.9.9 + mvn:org.openhab.osgiify/com.amazonaws.aws-java-sdk-core/1.11.490 + mvn:org.openhab.osgiify/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/bundles/pom.xml b/bundles/pom.xml index dd57b7d15505c..19dac9996f14d 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -156,6 +156,7 @@ org.openhab.binding.plugwise org.openhab.binding.powermax org.openhab.binding.pulseaudio + org.openhab.binding.pushbullet org.openhab.binding.regoheatpump org.openhab.binding.rfxcom org.openhab.binding.rme diff --git a/features/openhab-addons/pom.xml b/features/openhab-addons/pom.xml index 7235214003a90..2be013a8b2d48 100644 --- a/features/openhab-addons/pom.xml +++ b/features/openhab-addons/pom.xml @@ -50,7 +50,7 @@ - +

    diff --git a/itests/org.openhab.binding.astro.tests/itest.bndrun b/itests/org.openhab.binding.astro.tests/itest.bndrun index f1feb27836276..70801066403b9 100644 --- a/itests/org.openhab.binding.astro.tests/itest.bndrun +++ b/itests/org.openhab.binding.astro.tests/itest.bndrun @@ -23,7 +23,6 @@ Fragment-Host: org.openhab.binding.astro 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.storage.json;version='[2.5.0,2.5.1)',\ - ch.qos.logback.classic;version='[1.2.3,1.2.4)',\ ch.qos.logback.core;version='[1.2.3,1.2.4)',\ org.apache.commons.collections;version='[3.2.1,3.2.2)',\ org.objenesis;version='[2.6.0,2.6.1)',\ @@ -40,4 +39,5 @@ Fragment-Host: org.openhab.binding.astro 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.specs.stax-api-1.2;version='[2.9.0,2.9.1)',\ + slf4j.simple;version='[1.7.21,1.7.22)' diff --git a/itests/org.openhab.binding.avmfritz.tests/itest.bndrun b/itests/org.openhab.binding.avmfritz.tests/itest.bndrun index c22e4e846adca..66952a09cf14c 100644 --- a/itests/org.openhab.binding.avmfritz.tests/itest.bndrun +++ b/itests/org.openhab.binding.avmfritz.tests/itest.bndrun @@ -53,7 +53,6 @@ Fragment-Host: org.openhab.binding.avmfritz net.bytebuddy.byte-buddy;version='[1.9.7,1.9.8)',\ net.bytebuddy.byte-buddy-agent;version='[1.9.7,1.9.8)',\ org.mockito.mockito-core;version='[2.25.0,2.25.1)',\ - org.openhab.core.storage.json;version='[2.5.0,2.5.1)',\ com.google.gson;version='[2.8.2,2.8.3)',\ org.apache.xbean.bundleutils;version='[4.12.0,4.12.1)',\ org.apache.xbean.finder;version='[4.12.0,4.12.1)',\ diff --git a/itests/org.openhab.binding.avmfritz.tests/src/main/java/org/openhab/binding/avmfritz/internal/discovery/AVMFritzDiscoveryServiceTest.java b/itests/org.openhab.binding.avmfritz.tests/src/main/java/org/openhab/binding/avmfritz/internal/discovery/AVMFritzDiscoveryServiceTest.java index 0dc3e2687a623..ddc3db6d19861 100644 --- a/itests/org.openhab.binding.avmfritz.tests/src/main/java/org/openhab/binding/avmfritz/internal/discovery/AVMFritzDiscoveryServiceTest.java +++ b/itests/org.openhab.binding.avmfritz.tests/src/main/java/org/openhab/binding/avmfritz/internal/discovery/AVMFritzDiscoveryServiceTest.java @@ -31,6 +31,7 @@ import org.eclipse.smarthome.core.thing.ThingUID; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.openhab.binding.avmfritz.internal.ahamodel.AVMFritzBaseModel; import org.openhab.binding.avmfritz.internal.ahamodel.DeviceListModel; @@ -42,6 +43,7 @@ * * @author Christoph Weitkamp - Initial contribution */ +@Ignore public class AVMFritzDiscoveryServiceTest extends AVMFritzThingHandlerOSGiTest { private static final ThingUID BRIGE_THING_ID = new ThingUID("avmfritz:fritzbox:1"); diff --git a/itests/org.openhab.binding.feed.tests/itest.bndrun b/itests/org.openhab.binding.feed.tests/itest.bndrun index d556a1fe2178c..0423df7de5b19 100644 --- a/itests/org.openhab.binding.feed.tests/itest.bndrun +++ b/itests/org.openhab.binding.feed.tests/itest.bndrun @@ -7,6 +7,7 @@ Fragment-Host: org.openhab.binding.feed bnd.identity;id='org.openhab.binding.feed.tests',\ bnd.identity;id='org.openhab.core.binding.xml',\ bnd.identity;id='org.openhab.core.thing.xml',\ + bnd.identity;id='org.apache.felix.configadmin',\ osgi.identity;filter:='(&(osgi.identity=org.ops4j.pax.web.pax-web-runtime)(version>=7.2.3))' @@ -31,7 +32,6 @@ Fragment-Host: org.openhab.binding.feed 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)',\ @@ -70,4 +70,5 @@ Fragment-Host: org.openhab.binding.feed 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 + org.ops4j.pax.web.pax-web-spi;version='[7.2.3,7.2.4)',\ + org.apache.felix.configadmin;version='[1.9.8,1.9.9)' \ No newline at end of file diff --git a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/DiscoverComponentsTests.java b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/DiscoverComponentsTests.java index 6499abbab30c3..b16a6d56263da 100644 --- a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/DiscoverComponentsTests.java +++ b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/DiscoverComponentsTests.java @@ -56,7 +56,7 @@ public class DiscoverComponentsTests extends JavaOSGiTest { @Before public void setUp() { initMocks(this); - CompletableFuture voidFutureComplete = new CompletableFuture(); + CompletableFuture voidFutureComplete = new CompletableFuture<>(); voidFutureComplete.complete(null); doReturn(voidFutureComplete).when(connection).unsubscribeAll(); doReturn(CompletableFuture.completedFuture(true)).when(connection).subscribe(any(), any()); diff --git a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/HomeAssistantMQTTImplementationTests.java b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/HomeAssistantMQTTImplementationTests.java index 491c503cdcd0b..e44389659c6e4 100644 --- a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/HomeAssistantMQTTImplementationTests.java +++ b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/HomeAssistantMQTTImplementationTests.java @@ -30,8 +30,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; @@ -53,9 +51,6 @@ import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents; import org.openhab.binding.mqtt.homeassistant.internal.DiscoverComponents.ComponentDiscovered; import org.openhab.binding.mqtt.homeassistant.internal.HaID; -import org.osgi.service.cm.ConfigurationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -67,12 +62,11 @@ * @author David Graeff - Initial contribution */ public class HomeAssistantMQTTImplementationTests extends JavaOSGiTest { - final Logger logger = LoggerFactory.getLogger(HomeAssistantMQTTImplementationTests.class); private MqttService mqttService; private MqttBrokerConnection embeddedConnection; private MqttBrokerConnection connection; private int registeredTopics = 100; - Throwable failure = null; + private Throwable failure = null; @Mock ChannelStateUpdateListener channelStateUpdateListener; @@ -84,12 +78,7 @@ public class HomeAssistantMQTTImplementationTests extends JavaOSGiTest { * Create an observer that fails the test as soon as the broker client connection changes its connection state * to something else then CONNECTED. */ - MqttConnectionObserver failIfChange = new MqttConnectionObserver() { - @Override - public void connectionStateChanged(@NonNull MqttConnectionState state, @Nullable Throwable error) { - assertThat(state, is(MqttConnectionState.CONNECTED)); - } - }; + private MqttConnectionObserver failIfChange = (state, error) -> assertThat(state, is(MqttConnectionState.CONNECTED)); private String testObjectTopic; @Before @@ -124,7 +113,7 @@ public void setUp() throws InterruptedException, ExecutionException, TimeoutExce futures.add(embeddedConnection.publish(testObjectTopic + "/state", "true".getBytes())); registeredTopics = futures.size(); - CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).get(200, TimeUnit.MILLISECONDS); + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(200, TimeUnit.MILLISECONDS); failure = null; @@ -141,7 +130,7 @@ public void tearDown() throws InterruptedException, ExecutionException, TimeoutE @Test public void reconnectTest() - throws InterruptedException, ExecutionException, TimeoutException, ConfigurationException { + throws InterruptedException, ExecutionException, TimeoutException { connection.removeConnectionObserver(failIfChange); connection.stop().get(2000, TimeUnit.MILLISECONDS); connection = new MqttBrokerConnection(embeddedConnection.getHost(), embeddedConnection.getPort(), @@ -208,9 +197,9 @@ public void parseHATree() throws InterruptedException, ExecutionException, Timeo haComponents.values().stream().map(e -> e.start(connection, scheduler, 100)) .reduce(CompletableFuture.completedFuture(null), (a, v) -> a.thenCompose(b -> v)).exceptionally(e -> { - failure = e; - return null; - }).get(); + failure = e; + return null; + }).get(); // We should have received the retained value, while subscribing to the channels MQTT state topic. verify(channelStateUpdateListener, times(1)).updateChannelState(any(), any()); @@ -219,6 +208,5 @@ public void parseHATree() throws InterruptedException, ExecutionException, Timeo value = haComponents.get(channelGroupId).channelTypes().get(ComponentSwitch.switchChannelID).getState() .getCache().getChannelState(); assertThat(value, is(OnOffType.ON)); - } } diff --git a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/ThingChannelConstants.java b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/ThingChannelConstants.java index 5b41e5b5eca11..330a5cef532c4 100644 --- a/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/ThingChannelConstants.java +++ b/itests/org.openhab.binding.mqtt.homeassistant.tests/src/main/java/org/openhab/binding/mqtt/ThingChannelConstants.java @@ -31,29 +31,5 @@ */ 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); - } - + public static final ThingUID testHomeAssistantThing = new ThingUID(HOMEASSISTANT_MQTT_THING, "device234"); } diff --git a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/generic/EmbeddedBrokerTools.java b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/generic/EmbeddedBrokerTools.java index aea53ede05454..84606a0f8fa4a 100644 --- a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/generic/EmbeddedBrokerTools.java +++ b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/generic/EmbeddedBrokerTools.java @@ -37,7 +37,7 @@ */ @NonNullByDefault public class EmbeddedBrokerTools { - final Logger logger = LoggerFactory.getLogger(EmbeddedBrokerTools.class); + private final Logger logger = LoggerFactory.getLogger(EmbeddedBrokerTools.class); public @Nullable MqttBrokerConnection embeddedConnection = null; /** diff --git a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/generic/HomieImplementationTests.java b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/generic/HomieImplementationTests.java index d5d3812be3126..e69dad33cf761 100644 --- a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/generic/HomieImplementationTests.java +++ b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/generic/HomieImplementationTests.java @@ -29,8 +29,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.types.UnDefType; @@ -67,7 +65,10 @@ * @author David Graeff - Initial contribution */ public class HomieImplementationTests extends JavaOSGiTest { - final Logger logger = LoggerFactory.getLogger(HomieImplementationTests.class); + private static final String BASE_TOPIC = "homie"; + private static final String DEVICE_ID = ThingChannelConstants.testHomieThing.getId(); + private static final String DEVICE_TOPIC = BASE_TOPIC + "/" + DEVICE_ID; + private MqttService mqttService; private MqttBrokerConnection embeddedConnection; private MqttBrokerConnection connection; @@ -81,26 +82,18 @@ public class HomieImplementationTests extends JavaOSGiTest { @Mock HomieThingHandler handler; - ScheduledExecutorService scheduler; + private ScheduledExecutorService scheduler; /** * Create an observer that fails the test as soon as the broker client connection changes its connection state * to something else then CONNECTED. */ - MqttConnectionObserver failIfChange = new MqttConnectionObserver() { - @Override - public void connectionStateChanged(@NonNull MqttConnectionState state, @Nullable Throwable error) { - assertThat(state, is(MqttConnectionState.CONNECTED)); - } - }; + private MqttConnectionObserver failIfChange = (state, error) -> assertThat(state, is(MqttConnectionState.CONNECTED)); - private final String baseTopic = "homie"; - private final String deviceID = ThingChannelConstants.testHomieThing.getId(); - private final String deviceTopic = baseTopic + "/" + deviceID; - String propertyTestTopic; + private String propertyTestTopic; @Before - public void setUp() throws InterruptedException, ConfigurationException, ExecutionException, TimeoutException { + public void setUp() throws InterruptedException, ExecutionException, TimeoutException { registerVolatileStorageService(); initMocks(this); mqttService = getService(MqttService.class); @@ -120,13 +113,13 @@ public void setUp() throws InterruptedException, ConfigurationException, Executi embeddedConnection.setQos(1); List> futures = new ArrayList<>(); - futures.add(embeddedConnection.publish(deviceTopic + "/$homie", "3.0".getBytes())); - futures.add(embeddedConnection.publish(deviceTopic + "/$name", "Name".getBytes())); - futures.add(embeddedConnection.publish(deviceTopic + "/$state", "ready".getBytes())); - futures.add(embeddedConnection.publish(deviceTopic + "/$nodes", "testnode".getBytes())); + futures.add(embeddedConnection.publish(DEVICE_TOPIC + "/$homie", "3.0".getBytes())); + futures.add(embeddedConnection.publish(DEVICE_TOPIC + "/$name", "Name".getBytes())); + futures.add(embeddedConnection.publish(DEVICE_TOPIC + "/$state", "ready".getBytes())); + futures.add(embeddedConnection.publish(DEVICE_TOPIC + "/$nodes", "testnode".getBytes())); // Add homie node topics - final String testNode = deviceTopic + "/testnode"; + final String testNode = DEVICE_TOPIC + "/testnode"; futures.add(embeddedConnection.publish(testNode + "/$name", "Testnode".getBytes())); futures.add(embeddedConnection.publish(testNode + "/$type", "Type".getBytes())); futures.add( @@ -154,7 +147,7 @@ public void setUp() throws InterruptedException, ConfigurationException, Executi futures.add(embeddedConnection.publish(propertyTestTopic + "/$datatype", "boolean".getBytes())); registeredTopics = futures.size(); - CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).get(200, TimeUnit.MILLISECONDS); + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(200, TimeUnit.MILLISECONDS); scheduler = new ScheduledThreadPoolExecutor(4); } @@ -171,14 +164,14 @@ public void tearDown() throws InterruptedException, ExecutionException, TimeoutE @Test public void retrieveAllTopics() throws InterruptedException, ExecutionException, TimeoutException { CountDownLatch c = new CountDownLatch(registeredTopics); - connection.subscribe(deviceTopic + "/#", (topic, payload) -> c.countDown()).get(200, TimeUnit.MILLISECONDS); + connection.subscribe(DEVICE_TOPIC + "/#", (topic, payload) -> c.countDown()).get(200, TimeUnit.MILLISECONDS); assertTrue("Connection " + connection.getClientId() + " not retrieving all topics", c.await(1000, TimeUnit.MILLISECONDS)); } @Test public void retrieveOneAttribute() throws InterruptedException, ExecutionException { - WaitForTopicValue watcher = new WaitForTopicValue(connection, deviceTopic + "/$homie"); + WaitForTopicValue watcher = new WaitForTopicValue(connection, DEVICE_TOPIC + "/$homie"); assertThat(watcher.waitForTopicValue(100), is("3.0")); } @@ -187,10 +180,10 @@ public void retrieveOneAttribute() throws InterruptedException, ExecutionExcepti public void retrieveAttributes() throws InterruptedException, ExecutionException { assertThat(connection.hasSubscribers(), is(false)); - Node node = new Node(deviceTopic, "testnode", ThingChannelConstants.testHomieThing, callback, + Node node = new Node(DEVICE_TOPIC, "testnode", ThingChannelConstants.testHomieThing, callback, new NodeAttributes()); Property property = spy( - new Property(deviceTopic + "/testnode", node, "temperature", callback, new PropertyAttributes())); + new Property(DEVICE_TOPIC + "/testnode", node, "temperature", callback, new PropertyAttributes())); // Create a scheduler ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(4); @@ -224,8 +217,7 @@ public void retrieveAttributes() throws InterruptedException, ExecutionException public Property createSpyProperty(InvocationOnMock invocation) { final Node node = (Node) invocation.getMock(); final String id = (String) invocation.getArguments()[0]; - Property property = spy(node.createProperty(id, spy(new PropertyAttributes()))); - return property; + return spy(node.createProperty(id, spy(new PropertyAttributes()))); } // Inject a spy'ed node @@ -252,7 +244,7 @@ public void parseHomieTree() throws InterruptedException, ExecutionException, Ti doAnswer(this::createSpyNode).when(device).createNode(any()); // initialize the device, subscribe and wait. - device.initialize(baseTopic, deviceID, Collections.emptyList()); + device.initialize(BASE_TOPIC, DEVICE_ID, Collections.emptyList()); device.subscribe(connection, scheduler, 200).get(); assertThat(device.isInitialized(), is(true)); diff --git a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/generic/ThingChannelConstants.java b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/generic/ThingChannelConstants.java index 55943f076d25d..b64998c888b96 100644 --- a/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/generic/ThingChannelConstants.java +++ b/itests/org.openhab.binding.mqtt.homie.tests/src/main/java/org/openhab/binding/mqtt/generic/ThingChannelConstants.java @@ -32,59 +32,5 @@ */ 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); - } - + public final static ThingUID testHomieThing = new ThingUID(HOMIE300_MQTT_THING, "device123"); } diff --git a/itests/org.openhab.binding.nest.tests/src/main/java/org/openhab/binding/nest/internal/data/GsonParsingTest.java b/itests/org.openhab.binding.nest.tests/src/main/java/org/openhab/binding/nest/internal/data/GsonParsingTest.java index 9bdc74a7ec1fa..304d6854b0a57 100644 --- a/itests/org.openhab.binding.nest.tests/src/main/java/org/openhab/binding/nest/internal/data/GsonParsingTest.java +++ b/itests/org.openhab.binding.nest.tests/src/main/java/org/openhab/binding/nest/internal/data/GsonParsingTest.java @@ -24,6 +24,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Test cases for gson parsing of model classes + * + * @author David Bennett - Initial contribution + * @author Wouter Born - Increase test coverage + */ public class GsonParsingTest { private final Logger logger = LoggerFactory.getLogger(GsonParsingTest.class); diff --git a/itests/org.openhab.binding.tradfri.tests/itest.bndrun b/itests/org.openhab.binding.tradfri.tests/itest.bndrun index 17c8aafd61af2..bd2270596c1fd 100644 --- a/itests/org.openhab.binding.tradfri.tests/itest.bndrun +++ b/itests/org.openhab.binding.tradfri.tests/itest.bndrun @@ -3,7 +3,15 @@ Bundle-SymbolicName: ${project.artifactId} Fragment-Host: org.openhab.binding.tradfri --runrequires: bnd.identity;id='org.openhab.binding.tradfri.tests' +-runrequires: \ + bnd.identity;id='org.openhab.binding.tradfri.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' # # done @@ -19,7 +27,6 @@ Fragment-Host: org.openhab.binding.tradfri tec.uom.se;version='[1.0.8,1.0.9)',\ org.openhab.core;version='[2.5.0,2.5.1)',\ org.openhab.core.config.core;version='[2.5.0,2.5.1)',\ - ch.qos.logback.classic;version='[1.2.3,1.2.4)',\ ch.qos.logback.core;version='[1.2.3,1.2.4)',\ org.apache.commons.collections;version='[3.2.1,3.2.2)',\ org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\ @@ -27,7 +34,6 @@ Fragment-Host: org.openhab.binding.tradfri org.openhab.core.thing;version='[2.5.0,2.5.1)',\ slf4j.api;version='[1.7.25,1.7.26)',\ org.eclipse.equinox.event;version='[1.4.300,1.4.301)',\ - org.openhab.core.storage.json;version='[2.5.0,2.5.1)',\ com.google.gson;version='[2.8.2,2.8.3)',\ javax.jmdns;version='[3.5.5,3.5.6)',\ org.apache.servicemix.specs.activation-api-1.1;version='[2.9.0,2.9.1)',\ @@ -40,4 +46,23 @@ Fragment-Host: org.openhab.binding.tradfri org.openhab.binding.tradfri;version='[2.5.0,2.5.1)',\ org.openhab.binding.tradfri.tests;version='[2.5.0,2.5.1)',\ org.openhab.core.config.discovery.mdns;version='[2.5.0,2.5.1)',\ - org.openhab.core.io.transport.mdns;version='[2.5.0,2.5.1)' + org.openhab.core.io.transport.mdns;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.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.mockito.mockito-core;version='[2.25.0,2.25.1)',\ + org.objenesis;version='[2.6.0,2.6.1)',\ + org.openhab.core.test;version='[2.5.0,2.5.1)',\ + osgi.enroute.hamcrest.wrapper;version='[1.3.0,1.3.1)',\ + osgi.enroute.junit.wrapper;version='[4.12.0,4.12.1)',\ + ch.qos.logback.classic;version='[1.2.3,1.2.4)',\ + 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)' diff --git a/itests/org.openhab.binding.tradfri.tests/src/test/java/org/openhab/binding/tradfri/internal/discovery/TradfriDiscoveryParticipantOSGITest.java b/itests/org.openhab.binding.tradfri.tests/src/main/java/org/openhab/binding/tradfri/internal/discovery/TradfriDiscoveryParticipantOSGITest.java similarity index 100% rename from itests/org.openhab.binding.tradfri.tests/src/test/java/org/openhab/binding/tradfri/internal/discovery/TradfriDiscoveryParticipantOSGITest.java rename to itests/org.openhab.binding.tradfri.tests/src/main/java/org/openhab/binding/tradfri/internal/discovery/TradfriDiscoveryParticipantOSGITest.java diff --git a/itests/org.openhab.binding.tradfri.tests/src/test/java/org/openhab/binding/tradfri/internal/handler/TradfriHandlerOSGiTest.java b/itests/org.openhab.binding.tradfri.tests/src/main/java/org/openhab/binding/tradfri/internal/handler/TradfriHandlerOSGiTest.java similarity index 100% rename from itests/org.openhab.binding.tradfri.tests/src/test/java/org/openhab/binding/tradfri/internal/handler/TradfriHandlerOSGiTest.java rename to itests/org.openhab.binding.tradfri.tests/src/main/java/org/openhab/binding/tradfri/internal/handler/TradfriHandlerOSGiTest.java diff --git a/itests/org.openhab.io.hueemulation.tests/itest.bndrun b/itests/org.openhab.io.hueemulation.tests/itest.bndrun index 5a94730bcffd8..323ec5bf7bd85 100644 --- a/itests/org.openhab.io.hueemulation.tests/itest.bndrun +++ b/itests/org.openhab.io.hueemulation.tests/itest.bndrun @@ -3,7 +3,16 @@ Bundle-SymbolicName: ${project.artifactId} Fragment-Host: org.openhab.io.hueemulation --runrequires: bnd.identity;id='org.openhab.io.hueemulation.tests' +-runrequires: \ + bnd.identity;id='org.openhab.io.hueemulation.tests',\ + bnd.identity;id='org.openhab.core.binding.xml',\ + bnd.identity;id='org.openhab.core.thing.xml',\ + bnd.identity;id='com.eclipsesource.jaxrs.jersey-all' + +# 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' # # done @@ -26,7 +35,6 @@ Fragment-Host: org.openhab.io.hueemulation 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)',\ @@ -35,4 +43,42 @@ Fragment-Host: org.openhab.io.hueemulation 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)' + tec.uom.se;version='[1.0.8,1.0.9)',\ + 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.commons.collections;version='[3.2.1,3.2.2)',\ + org.apache.commons.exec;version='[1.1.0,1.1.1)',\ + org.apache.felix.configadmin;version='[1.9.8,1.9.9)',\ + 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.xbean.bundleutils;version='[4.12.0,4.12.1)',\ + org.apache.xbean.finder;version='[4.12.0,4.12.1)',\ + org.eclipse.jetty.client;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.websocket.api;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.websocket.client;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.websocket.common;version='[9.4.11,9.4.12)',\ + org.eclipse.jetty.xml;version='[9.4.11,9.4.12)',\ + org.jupnp;version='[2.5.2,2.5.3)',\ + org.mockito.mockito-core;version='[2.25.0,2.25.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.objenesis;version='[2.6.0,2.6.1)',\ + org.openhab.core.automation;version='[2.5.0,2.5.1)',\ + org.openhab.core.io.console;version='[2.5.0,2.5.1)',\ + org.openhab.core.io.net;version='[2.5.0,2.5.1)',\ + org.openhab.core.thing;version='[2.5.0,2.5.1)',\ + org.ops4j.pax.swissbox.optional.jcl;version='[1.8.2,1.8.3)',\ + org.ops4j.pax.web.pax-web-api;version='[7.2.3,7.2.4)',\ + 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)',\ + 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)',\ + com.eclipsesource.jaxrs.jersey-all;version='[2.22.2,2.22.3)',\ + org.openhab.core.config.discovery;version='[2.5.0,2.5.1)',\ + org.openhab.core.config.discovery.upnp;version='[2.5.0,2.5.1)' 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 index 47e929945a103..9bdd295e70c2c 100644 --- 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 @@ -34,6 +34,7 @@ import org.junit.Before; import org.junit.Test; import org.jupnp.UpnpService; +import org.jupnp.mock.MockUpnpService; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.openhab.core.automation.RuleRegistry; @@ -47,7 +48,7 @@ */ public class HueEmulationServiceOSGiTest extends JavaOSGiTest { private HueEmulationService hueService; - VolatileStorageService volatileStorageService = new VolatileStorageService(); + private VolatileStorageService volatileStorageService = new VolatileStorageService(); private @Nullable RuleRegistry ruleRegistry; private @Nullable ItemRegistry itemRegistry; @@ -91,10 +92,10 @@ public void tearDown() { } @Test(timeout = 5000) - public void UpnpServiceTest() throws InterruptedException, ExecutionException, TimeoutException, IOException { + public void UpnpServiceTest() throws IOException { waitFor(() -> !hueService.cs.ds.config.ipaddress.isEmpty(), 5000, 100); host = "http://" + hueService.cs.ds.config.ipaddress + ":" - + String.valueOf(hueService.cs.config.discoveryHttpPort); + + hueService.cs.config.discoveryHttpPort; HttpURLConnection c = (HttpURLConnection) new URL(host + "/description.xml").openConnection(); assertThat(c.getResponseCode(), is(200)); @@ -114,13 +115,11 @@ private String read(HttpURLConnection urlConnection) throws IOException { _is = urlConnection.getErrorStream(); } try (InputStream in = new BufferedInputStream(_is)) { - if (in != null) { - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in)); - String line = ""; + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in)); + String line; - while ((line = bufferedReader.readLine()) != null) { - result += line; - } + while ((line = bufferedReader.readLine()) != null) { + result += line; } } return result; diff --git a/pom.xml b/pom.xml index b623483697f7b..fbce7e5cffa76 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,13 @@ + + + osgiify + https://dl.bintray.com/openhab/mvn/openhab-osgiify + + + UTF-8 UTF-8 @@ -67,7 +74,7 @@ ${oh.java.version} 4.2.0 - 4.2.4 + 4.2.6 0.6.1 1.7.21 diff --git a/travis-buddy-failure-template.md b/travis-buddy-failure-template.md new file mode 100644 index 0000000000000..a5b6c2ce0bf96 --- /dev/null +++ b/travis-buddy-failure-template.md @@ -0,0 +1,22 @@ +## Travis tests have failed +Hey @{{pullRequestAuthor}}, +please read the following log in order to understand the failure reason. There might also be some helpful tips along the way. +It'll be awesome if you fix what's wrong and commit the changes. + +{{#jobs}} +### {{displayName}} +{{#scripts}} +
    + + + Expand here + + + +``` +{{&contents}} +``` +
    +
    +{{/scripts}} +{{/jobs}} diff --git a/travis-buddy-success-template.md b/travis-buddy-success-template.md new file mode 100644 index 0000000000000..b78a9bfa17c40 --- /dev/null +++ b/travis-buddy-success-template.md @@ -0,0 +1,21 @@ +## Travis tests were successful +Hey @{{pullRequestAuthor}}, +we found no major flaws with your code. Still you might want to look at this logfile, as we usually suggest some optional improvements. + +{{#jobs}} +### {{displayName}} +{{#scripts}} +
    + + + {{command}} + + + +``` +{{&contents}} +``` +
    +
    +{{/scripts}} +{{/jobs}}
  • NameValue