diff --git a/addons/binding/org.openhab.binding.zoneminder/.classpath b/addons/binding/org.openhab.binding.zoneminder/.classpath index 3801e00168a71..e75fa0c6a7f70 100644 --- a/addons/binding/org.openhab.binding.zoneminder/.classpath +++ b/addons/binding/org.openhab.binding.zoneminder/.classpath @@ -3,6 +3,6 @@ - + diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/binding/binding.xml b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/binding/binding.xml index d6f42b96fc3ce..f85134801d585 100644 --- a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/binding/binding.xml +++ b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/binding/binding.xml @@ -1,11 +1,9 @@ - - ZoneMinder Binding - This binding interfaces a ZoneMinder Server - Martin S. Eskildsen - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:binding="http://eclipse.org/smarthome/schemas/binding/v1.0.0" + xsi:schemaLocation="http://eclipse.org/smarthome/schemas/binding/v1.0.0 http://eclipse.org/smarthome/schemas/binding-1.0.0.xsd"> + ZoneMinder Binding + This binding interfaces a ZoneMinder Server + Martin S. Eskildsen diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/monitor-channels.xml b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/monitor-channels.xml deleted file mode 100644 index 0a5f91128a78f..0000000000000 --- a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/monitor-channels.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - The ID of the monitor in ZoneMinder - - - - Timeout in seconds when activating alarm. Default is 60 seconds - 60 - - - - Event text in ZoneMinder - Triggered from openHAB - - - diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/monitor-config.xml b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/monitor-config.xml new file mode 100644 index 0000000000000..89a6b018fb62f --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/monitor-config.xml @@ -0,0 +1,87 @@ + + + + + + advancedConfig + + true + + + + imageConfig + + true + + + + + The ID of the monitor in ZoneMinder + + + + + Timeout in seconds when activating alarm, 0 disables timeout. Default is 60 seconds + 60 + true + + + + + Event text in ZoneMinder + Triggered from openHAB + true + + + + + + Refresh priority for still images when event state is idle + low + + + + + + + + + + Refresh priority for still images when event state is alarmed + normal + + + + + + + + + + + + Rescale image from ZoneMinder Monitor + 100 + + + + + + + + + + + + + + + + + + + + diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/server-config.xml b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/server-config.xml new file mode 100644 index 0000000000000..53736e93a0655 --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/server-config.xml @@ -0,0 +1,137 @@ + + + + + basicConfig + + + + credentials + + + + networkAdvConfig + + + + refreshConfig + + + + performanceConfig + + + + streamingConfig + + true + + + + advancedConfig + + + + + + network-address + + The IP address or hostname of the ZoneMinder Server + + + + Protocol to connect to the ZoneMinder Server API (http or https) + http + + + + + + + + User to access the ZoneMinder Server API + + + password + + Password to access the ZoneMinder Server API + + + + + Additional path on ZoneMinder Server to access ZoneMinder Portal page. In a standard installation this is' /zm' + /zm + true + + + + Additional path on ZoneMinder Server to access API. In a standard installation this is' /zm/api' + /zm/api + true + + + + + Port of the ZoneMinder Server API. If '0', then the port will be determined from the protocol + 0 + true + + + + Port to listen for events in (Telnet) + 6802 + true + + + + + Seconds between each call to ZoneMinder Server API to refresh values in openHAB + 10 + true + + + + Seconds between each call to ZoneMinder Server to refresh Server DiskUsage in ZoneMinder. Default value is '60' + 60 + true + + + + + Refresh disk usage counter + disabled + + + + + true + + + + + If enabled new monitors on the ZoneMinder Server will automatically be added to the Inbox in openHAB + true + true + + + + + Specific streaming user + false + true + + + + + Optional User to access image streams + + + + + Optional Password for streaming user + + + + + \ No newline at end of file diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/zoneminderserver.xml b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/zoneminderserver.xml deleted file mode 100644 index 2ecfa8cc1c433..0000000000000 --- a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/config/zoneminderserver.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - basic - - - - credentials - - - - network - - - - refreshConfig - - - - advancedSettings - - - - network-address - - The IP address or hostname of the ZoneMinder Server - - - - Protocol to connect to the ZoneMinder Server API (http or https) - http - - - - - - - - Additional path on ZoneMinder Server to access API. In a standard installation this is' /zm' - /zm - - - - User to access the ZoneMinder Server API - - - password - - Password to access the ZoneMinder Server API - - - - Port of the ZoneMinder Server API. If '0', then the port will be determined from the protocol - 0 - true - - - - Port to listen for events in (Telnet) - 6802 - true - - - - Seconds between each call to ZoneMinder Server API to refresh values in openHAB - 10 - true - - - - Minutes between each call to ZoneMinder Server to refresh Server DiskUsage in ZoneMinder. Default value is '0' (Disabled) - 0 - true - - - - If enabled new monitors on the ZoneMinder Server will automatically be added to the Inbox in openHAB - true - true - - - diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/bridge-server.xml b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/bridge-server.xml new file mode 100644 index 0000000000000..56c44a5b9c683 --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/bridge-server.xml @@ -0,0 +1,39 @@ + + + + + + ZoneMinder Server + + + + + + + + + + + Switch + + ZoneMinder Server Online Status + + + + + Number + + ZoneMinder Server CPU Load + + + + Number + + ZoneMinder Server Disk Usage + + + + \ No newline at end of file diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/monitor-thing.xml b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/monitor-thing.xml deleted file mode 100644 index 1f44e59a69745..0000000000000 --- a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/monitor-thing.xml +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - Camera in ZoneMinder - - - - - - - - - - - - - - - - - - - - - Switch - - Switch telling if the monitor is online - - - - - Switch - - Showing the value of the checkbox 'enabled' in ZoneMinder for the monitor - - - - - Switch - - Will force an alarm from openHAB in ZoneMinder - - - - - - Switch - - set to 'ON' when one of the following is true: Motion detected, Signal lost, Force Alarm pressed, External Alarm. Else set to 'OFF' - - - - - Switch - - set to 'ON' when either channel monitor-alarm set to 'ON', or montior function is 'Mocord' or 'Record'. Else set to 'OFF' - - - - - String - - Current Monitor Status: 0=Idle, 1=Pre-alarm, 2=Alarm, 3=Alert, 4=Recording - - - - - - - - - - - - - String - - Current Monitor Function: None, Monitor, Modect, Record, Mocord, Nodect - - - - - - - - - - - - - - String - - Cause of event: None, Signal, Motion, Forced Web, openHAB, Other - - - - - - - - - - - - - - Switch - - State of ZoneMinder Capture daemon for this monitor - - - - - Switch - - State of ZoneMinder Analysis daemon for this monitor - - - - - Switch - - State of ZoneMinder Frame daemon for this monitor - - - - diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/server-bridge.xml b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/server-bridge.xml deleted file mode 100644 index 4ce2511ea787e..0000000000000 --- a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/server-bridge.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - ZoneMinder Server - - - - - - - - - - - Switch - - ZoneMinder Server Online Status - - - - - Number - - ZoneMinder Server CPU Load - - - - Number - - ZoneMinder Server Disk Usage - - - - diff --git a/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/thing-monitor.xml b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/thing-monitor.xml new file mode 100644 index 0000000000000..2525d81424059 --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/ESH-INF/thing/thing-monitor.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + Camera in ZoneMinder + + + + + + + + + + + + + + + + + + + + + + + + Switch + + Switch telling if the monitor is online + + + + + Switch + + Showing the value of the checkbox 'enabled' in ZoneMinder for the monitor + + + + + Switch + + Will force an alarm from openHAB in ZoneMinder + + + + + + Switch + + set to 'ON' when one of the following is true: Motion detected, Signal lost, Force Alarm pressed, External Alarm. Else set to 'OFF' + + + + + Switch + + set to 'ON' when either channel monitor-alarm set to 'ON', or montior function is 'Mocord' or 'Record'. Else set to 'OFF' + + + + + Switch + + set to 'ON' when active alarm caused by motion. Else set to 'OFF' + + + + + String + + Current Monitor Status: 0=Idle, 1=Pre-alarm, 2=Alarm, 3=Alert, 4=Recording + + + + + + + + + + + + + String + + Current Monitor Function: None, Monitor, Modect, Record, Mocord, Nodect + + + + + + + + + + + + + + + String + + Cause of event: None, Signal, Motion, Forced Web, openHAB, Other + + + + + + + + + + + + + + Switch + + State of ZoneMinder Capture daemon for this monitor + + + + + Switch + + State of ZoneMinder Analysis daemon for this monitor + + + + + Switch + + State of ZoneMinder Frame daemon for this monitor + + + + + Image + + Image channel for Monitor + + + + + String + + Video URL for Monitor + + + + diff --git a/addons/binding/org.openhab.binding.zoneminder/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.zoneminder/META-INF/MANIFEST.MF index 6fa47be8643c9..53291ac5d9a75 100644 --- a/addons/binding/org.openhab.binding.zoneminder/META-INF/MANIFEST.MF +++ b/addons/binding/org.openhab.binding.zoneminder/META-INF/MANIFEST.MF @@ -3,10 +3,10 @@ Bundle-ClassPath: ., lib/gson-2.7.jar, lib/guava-17.0.jar, - lib/jersey-common-2.4.1.jar, lib/javax.ws.rs-api-2.0.1.jar, + lib/jersey-common-2.4.1.jar, lib/jsoup-1.10.1.jar, - lib/zoneminder4j-0.9.7.jar + lib/zoneminder4j-0.9.8.jar Bundle-ManifestVersion: 2 Bundle-Name: ZoneMinder Binding Bundle-RequiredExecutionEnvironment: JavaSE-1.8 @@ -18,13 +18,22 @@ Export-Package: org.openhab.binding.zoneminder.handler Import-Package: com.google.common.collect, + com.google.gson, javax.ws.rs.client, javax.ws.rs.core, org.apache.commons.lang, - org.apache.commons.net.util, org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.jetty.client, + org.eclipse.jetty.client.api, + org.eclipse.jetty.client.util, + org.eclipse.jetty.http, + org.eclipse.jetty.io, + org.eclipse.jetty.util, + org.eclipse.jetty.util.component, + org.eclipse.jetty.util.ssl, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.discovery, + org.eclipse.smarthome.config.discovery.inbox, org.eclipse.smarthome.core.library.types, org.eclipse.smarthome.core.thing, org.eclipse.smarthome.core.thing.binding, diff --git a/addons/binding/org.openhab.binding.zoneminder/OSGI-INF/ZoneMinderHandlerFactory.xml b/addons/binding/org.openhab.binding.zoneminder/OSGI-INF/ZoneMinderHandlerFactory.xml index 73421dcbc315a..a1faa02e54056 100644 --- a/addons/binding/org.openhab.binding.zoneminder/OSGI-INF/ZoneMinderHandlerFactory.xml +++ b/addons/binding/org.openhab.binding.zoneminder/OSGI-INF/ZoneMinderHandlerFactory.xml @@ -1,20 +1,15 @@ - - - - - - - + + + + diff --git a/addons/binding/org.openhab.binding.zoneminder/README.md b/addons/binding/org.openhab.binding.zoneminder/README.md index 8886cb6dbb114..fc9f37d957f92 100644 --- a/addons/binding/org.openhab.binding.zoneminder/README.md +++ b/addons/binding/org.openhab.binding.zoneminder/README.md @@ -3,7 +3,7 @@ This binding offers integration to a ZoneMinder Server. It currently only offers to integrate to monitors (eg. cameras in ZoneMinder). It also only offers access to a limited set of values, as well as a even more limited option to update values in ZoneMinder. It requires at least ZoneMinder 1.29 with API enabled (option 'OPT_USE_API' in ZoneMinder must be enabled). -The option 'OPT_TRIGGERS' must be anabled to allow openHAB to trip the ForceAlarm in ZoneMinder. +The option 'OPT_TRIGGERS' must be enabled to allow openHAB to trip the ForceAlarm in ZoneMinder. ## Supported Things @@ -31,8 +31,8 @@ When a new monitor is discovered it will appear in the Inbox. | Channel | Type | Description | |------------|--------|----------------------------------------------| | online | Switch | Parameter indicating if the server is online | -| CPU load | Text | Current CPU Load of server | -| Disk Usage | text | Current Disk Usage on server | +| cpu-load | Text | Current CPU Load of server | +| disk-usage | Text | Current Disk Usage on server | ### Thing @@ -49,15 +49,48 @@ When a new monitor is discovered it will appear in the Inbox. | capture-daemon | Switch | Run state of ZMC Daemon | | analysis-daemon | Switch | Run state of ZMA Daemon | | frame-daemon | Switch | Run state of ZMF Daemon | +| image | Image | Still image from Monitor | +| videourl | Text | Url to video stream | -## Manual configuration +## Configuration ### Things configuration +#### Bridge Configuration +| Parameter | Optional | Description | +|--------------------------|----------|--------------------------------------------------------------------------------------------| +| host | | Hostname or Ip address of ZoneMinder server | +| protocol | | Protocol used ('http' or 'https'). https can cause issues with certificates | +| user | X | Username to login to ZoneMidner server, if authentication is enabled | +| password | X | Password to login to ZoneMidner server, if authentication is enabled | +| urlSite | X | Path to ZoneMinder site (Default: '/zm') | +| urlApi | X | Path to ZoneMinder API (Default: '/zm/api') | +| portHttp | X | Port to access ZoneMinder site. (Default: 0 (is either 80 or 443 depending on protocol)) | +| portTelnet | X | Port to access ZoneMinder with Telnet (Default: 6802) | +| refreshNormal | X | Refresh rate in seconds for Normal priority (Default: 10) | +| refreshLow | X | Refresh rate in seconds for Low priority (Default: 60) | +| diskUsageRefresh | X | Either 'batch' or 'disabled' (Default: 'disabled') | +| autodiscover | X | Enable / Disable autodiscovery (Default: true) | +| useSpecificUserStreaming | X | Use specific user for streaming (Default: false) | +| streamingUser | X | If 'useSpecificUserStreaming' is true, username must be specified here | +| streamingPassword | X | If 'useSpecificUserStreaming' is true, password must be specified here | + +#### Monitor Configuration +| Parameter | Optional | Description | +|--------------------------|----------|-----------------------------------------------------------------------------------------------| +| id | | Id of the monitor. Must match id in ZoneMinder | +| triggerTimeout | x | Timeout in seconds of events generated from openHAB (Default: 60) | +| eventText | X | Event text of openHAB trigegred events (Default: 'Triggered from openHAB') | +| imageRefreshIdle | X | Refresh rate of image when monitor has no active event (normal, low, disabled) (Default: low) | +| imageRefreshEvent | X | Refresh rate when active event (alarm, normal, low, disabled), (Default: alarm) | +| imageScale | X | Size (scale) of image. Default: 100 | + + +### Things file ``` -Bridge zoneminder:server:ZoneMinderSample [ hostname="192.168.1.55", user="", password="", telnet_port=6802, refresh_interval_disk_usage=1 ] +Bridge zoneminder:server:ZoneMinderSample [ host="192.168.1.55", protocol="http", user="", password="", autodiscover=false, useSpecificUserStreaming=true, streamingUser="", streamingPassword="" ] { - Thing monitor monitor_1 [ monitorId=1, monitorTriggerTimeout=120, monitorEventText="Trigger activated from openHAB" ] + Thing monitor monitor_1 [ monitorId=1, monitorTriggerTimeout=120, monitorEventText="Trigger activated from openHAB", imageRefreshIdle="disabled", imageRefreshEvent="alarm" ] } ``` diff --git a/addons/binding/org.openhab.binding.zoneminder/build.properties b/addons/binding/org.openhab.binding.zoneminder/build.properties index 147def4630562..6b9bd7b2cc28a 100644 --- a/addons/binding/org.openhab.binding.zoneminder/build.properties +++ b/addons/binding/org.openhab.binding.zoneminder/build.properties @@ -7,3 +7,4 @@ bin.includes = META-INF/,\ lib/,\ about.html + diff --git a/addons/binding/org.openhab.binding.zoneminder/lib/zoneminder4j-0.9.7.jar b/addons/binding/org.openhab.binding.zoneminder/lib/zoneminder4j-0.9.7.jar deleted file mode 100644 index 5f202659325c0..0000000000000 Binary files a/addons/binding/org.openhab.binding.zoneminder/lib/zoneminder4j-0.9.7.jar and /dev/null differ diff --git a/addons/binding/org.openhab.binding.zoneminder/lib/zoneminder4j-0.9.8.jar b/addons/binding/org.openhab.binding.zoneminder/lib/zoneminder4j-0.9.8.jar new file mode 100644 index 0000000000000..5343a3a5fd5b7 Binary files /dev/null and b/addons/binding/org.openhab.binding.zoneminder/lib/zoneminder4j-0.9.8.jar differ diff --git a/addons/binding/org.openhab.binding.zoneminder/out.txt b/addons/binding/org.openhab.binding.zoneminder/out.txt new file mode 100644 index 0000000000000..ad412d4c5f5c4 --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/out.txt @@ -0,0 +1,300 @@ +[INFO] Scanning for projects... +Downloading: https://openhab.jfrog.io/openhab/libs-snapshot/org/openhab/pom-tycho/2.3.0-SNAPSHOT/maven-metadata.xml +599/599 B Downloaded: https://openhab.jfrog.io/openhab/libs-snapshot/org/openhab/pom-tycho/2.3.0-SNAPSHOT/maven-metadata.xml (599 B at 0.7 KB/sec) +[WARNING] Could not transfer metadata org.openhab:pom-tycho:2.3.0-SNAPSHOT/maven-metadata.xml from/to p2-smarthome-snapshot (https://openhab.jfrog.io/openhab/eclipse-smarthome-stable): Cannot access https://openhab.jfrog.io/openhab/eclipse-smarthome-stable with type p2 using the available connector factories: BasicRepositoryConnectorFactory +[WARNING] Could not transfer metadata org.openhab:pom-tycho:2.3.0-SNAPSHOT/maven-metadata.xml from/to p2-openhab-deps-repo (https://dl.bintray.com/openhab/p2/openhab-deps-repo/${ohdr.version}): Cannot access https://dl.bintray.com/openhab/p2/openhab-deps-repo/${ohdr.version} with type p2 using the available connector factories: BasicRepositoryConnectorFactory +Downloading: https://openhab.jfrog.io/openhab/libs-snapshot/org/openhab/pom/2.3.0-SNAPSHOT/maven-metadata.xml +593/593 B Downloaded: https://openhab.jfrog.io/openhab/libs-snapshot/org/openhab/pom/2.3.0-SNAPSHOT/maven-metadata.xml (593 B at 2.2 KB/sec) +[WARNING] Could not transfer metadata org.openhab:pom:2.3.0-SNAPSHOT/maven-metadata.xml from/to p2-smarthome-snapshot (https://openhab.jfrog.io/openhab/eclipse-smarthome-stable): Cannot access https://openhab.jfrog.io/openhab/eclipse-smarthome-stable with type p2 using the available connector factories: BasicRepositoryConnectorFactory +[WARNING] Could not transfer metadata org.openhab:pom:2.3.0-SNAPSHOT/maven-metadata.xml from/to p2-openhab-deps-repo (https://dl.bintray.com/openhab/p2/openhab-deps-repo/${ohdr.version}): Cannot access https://dl.bintray.com/openhab/p2/openhab-deps-repo/${ohdr.version} with type p2 using the available connector factories: BasicRepositoryConnectorFactory +[INFO] Computing target platform for MavenProject: org.openhab.binding:org.openhab.binding.zoneminder:2.3.0-SNAPSHOT @ D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\pom.xml +[INFO] Fetching p2.index from https://openhab.jfrog.io/openhab/eclipse-smarthome-stable/ (172B) +[INFO] Fetching p2.index from https://openhab.jfrog.io/openhab/eclipse-smarthome-stable/ (172B) +[INFO] Adding repository https://openhab.jfrog.io/openhab/eclipse-smarthome-stable +[INFO] Fetching p2.index from http://download.eclipse.org/smarthome/updates-release/0.9.0/ +[INFO] Fetching p2.index from http://download.eclipse.org/smarthome/updates-release/0.9.0/ +[INFO] Adding repository http://download.eclipse.org/smarthome/updates-release/0.9.0 +[INFO] Fetching p2.index from https://dl.bintray.com/openhab/p2/openhab-deps-repo/1.0.19/ (172B) +[INFO] Fetching p2.index from https://dl.bintray.com/openhab/p2/openhab-deps-repo/1.0.19/ (172B) +[INFO] Adding repository https://dl.bintray.com/openhab/p2/openhab-deps-repo/1.0.19 +[INFO] Resolving dependencies of MavenProject: org.openhab.binding:org.openhab.binding.zoneminder:2.3.0-SNAPSHOT @ D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\pom.xml +[INFO] Resolving class path of MavenProject: org.openhab.binding:org.openhab.binding.zoneminder:2.3.0-SNAPSHOT @ D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\pom.xml +[INFO] +[INFO] ------------------------------------------------------------------------ +[INFO] Building ZoneMinder Binding 2.3.0-SNAPSHOT +[INFO] ------------------------------------------------------------------------ +[INFO] +[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ org.openhab.binding.zoneminder --- +[INFO] Deleting D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\target +[INFO] +[INFO] --- tycho-packaging-plugin:1.0.0:build-qualifier (default-build-qualifier) @ org.openhab.binding.zoneminder --- +[INFO] The project's OSGi version is 2.3.0.201802031407 +[INFO] +[INFO] --- tycho-packaging-plugin:1.0.0:validate-id (default-validate-id) @ org.openhab.binding.zoneminder --- +[INFO] +[INFO] --- tycho-packaging-plugin:1.0.0:validate-version (default-validate-version) @ org.openhab.binding.zoneminder --- +[INFO] +[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ org.openhab.binding.zoneminder --- +[INFO] Using 'UTF-8' encoding to copy filtered resources. +[INFO] skip non existing resourceDirectory D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\resources +[INFO] +[INFO] --- tycho-compiler-plugin:1.0.0:compile (default-compile) @ org.openhab.binding.zoneminder --- +[WARNING] Parameter 'useProjectSettings' is set to true, but preferences file 'D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\.settings\org.eclipse.jdt.core.prefs' could not be found! +[INFO] Compiling 25 source files to D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\target\classes +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderBaseThingHandler.java:[215] + Channel ch = thing.getChannel(id); + ^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderBaseThingHandler.java:[216] + return ch.getUID(); + ^^ +Potential null pointer access: The variable ch may be null at this location +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderBaseThingHandler.java:[332] + updateState(channel, getChannelBoolAsOnOffState(isOnline())); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'State' needs unchecked conversion to conform to '@NonNull State' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderBaseThingHandler.java:[493] + updateStatus(thingStatus, statusDetail, statusDescription); + ^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'ThingStatusDetail' needs unchecked conversion to conform to '@NonNull ThingStatusDetail' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderBaseThingHandler.java:[497] + updateStatus(thingStatus); + ^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'ThingStatus' needs unchecked conversion to conform to '@NonNull ThingStatus' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\internal\discovery\ZoneMinderDiscoveryService.java:[62] + return ZoneMinderThingMonitorHandler.SUPPORTED_THING_TYPES; + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'Set' needs unchecked conversion to conform to '@NonNull Set' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\internal\discovery\ZoneMinderDiscoveryService.java:[101] + return serverHandler.getThingByUID(newThingUID) != null ? true : false; + ^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'ThingUID' needs unchecked conversion to conform to '@NonNull ThingUID' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\internal\discovery\ZoneMinderDiscoveryService.java:[129] + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) + ^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'Map' needs unchecked conversion to conform to '@Nullable Map<@NonNull String,@NonNull Object>' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[507] + Channel channel = this.getThing().getChannel(ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[508] + Channel chEventCause = this.getThing().getChannel(ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[640] + getLogIdentifier(), thing.getUID(), getBridge().getBridgeUID()); + ^^^^^^^^^^^ +Potential null pointer access: The method getBridge() may return null +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[644] + if (getBridge().getStatus() != ThingStatus.ONLINE) { + ^^^^^^^^^^^ +Potential null pointer access: The method getBridge() may return null +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[645] + msg = String.format("Bridge '%s' is OFFLINE", getBridge().getBridgeUID()); + ^^^^^^^^^^^ +Potential null pointer access: The method getBridge() may return null +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[654] + getLogIdentifier(), getBridge().getBridgeUID()); + ^^^^^^^^^^^ +Potential null pointer access: The method getBridge() may return null +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[802] + updateState(channel.getId(), state); + ^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[806] + getLogIdentifier(), channel.toString(), state.toString(), ex.getMessage()); + ^^^^^ +Potential null pointer access: The variable state may be null at this location +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[813] + updateState(ZoneMinderConstants.CHANNEL_ONLINE, + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[845] + session = null; + ^^^^^^^ +Redundant assignment: The variable session can only be null at this location +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[920] + if (isLinked(ZoneMinderConstants.CHANNEL_MONITOR_CAPTURE_DAEMON_STATE)) { + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[939] + if (isLinked(ZoneMinderConstants.CHANNEL_MONITOR_ANALYSIS_DAEMON_STATE)) { + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[961] + if (isLinked(ZoneMinderConstants.CHANNEL_MONITOR_FRAME_DAEMON_STATE)) { + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[981] + if (isLinked(ZoneMinderConstants.CHANNEL_MONITOR_STILL_IMAGE)) { + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[1044] + if (session != null) { + ^^^^^^^ +Redundant null check: The variable session cannot be null at this location +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[1124] + updateProperties(properties); + ^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'Map' needs unchecked conversion to conform to '@NonNull Map<@NonNull String,@NonNull String>' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingMonitorHandler.java:[1148] + updateState(channelUID.getId(), state); + ^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\ZoneMinderConstants.java:[42] + public static final ThingTypeUID THING_TYPE_BRIDGE_ZONEMINDER_SERVER = new ThingTypeUID(BINDING_ID, + ^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\ZoneMinderConstants.java:[43] + BRIDGE_ZONEMINDER_SERVER); + ^^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\ZoneMinderConstants.java:[89] + public static final ThingTypeUID THING_TYPE_THING_ZONEMINDER_MONITOR = new ThingTypeUID(BINDING_ID, + ^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\ZoneMinderConstants.java:[90] + THING_ZONEMINDER_MONITOR); + ^^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\ZoneMinderConstants.java:[128] + public static final ThingTypeUID THING_TYPE_THING_ZONEMINDER_PTZCONTROL = new ThingTypeUID(BINDING_ID, + ^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\ZoneMinderConstants.java:[129] + THING_ZONEMINDER_PTZCONTROL); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderThingPTZControlHandler.java:[78] + Channel channel = ChannelBuilder.create(new ChannelUID(channelIdString), "String").withDescription(description) + ^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderServerBridgeHandler.java:[314] + super.handleConfigurationUpdate(configurationParameters); + ^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type '@NonNull Map' needs unchecked conversion to conform to '@NonNull Map<@NonNull String,@NonNull Object>' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderServerBridgeHandler.java:[407] + if ((thingHandler.getZoneMinderId().equals(zoneMinderId)) + ^^^^^^^^^^^^ +Potential null pointer access: The variable thingHandler may be null at this location +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderServerBridgeHandler.java:[495] + } else if (hostLoad.getHttpStatus() != HttpStatus.OK_200) { + ^^^^^^^^ +Potential null pointer access: The variable hostLoad may be null at this location +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderServerBridgeHandler.java:[529] + if (isLinked(channel.getUID().getId())) { + ^^^^^^^^^^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderServerBridgeHandler.java:[544] + thingHandler.refreshThing(curPriority); + ^^^^^^^^^^^^ +Potential null pointer access: The variable thingHandler may be null at this location +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderServerBridgeHandler.java:[1057] + curSession = null; + ^^^^^^^^^^ +Redundant assignment: The variable curSession can only be null at this location +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderServerBridgeHandler.java:[1476] + updateStatus(newStatus, statusDetail, statusDescription); + ^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'ThingStatusDetail' needs unchecked conversion to conform to '@NonNull ThingStatusDetail' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderServerBridgeHandler.java:[1484] + updateStatus(newStatus); + ^^^^^^^^^ +Null type safety (type annotations): The expression of type 'ThingStatus' needs unchecked conversion to conform to '@NonNull ThingStatus' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderServerBridgeHandler.java:[1542] + updateState(channel.getId(), state); + ^^^^^^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String' +[WARNING] D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\main\java\org\openhab\binding\zoneminder\handler\ZoneMinderServerBridgeHandler.java:[1874] + updateProperties(properties); + ^^^^^^^^^^ +Null type safety (type annotations): The expression of type 'Map' needs unchecked conversion to conform to '@NonNull Map<@NonNull String,@NonNull String>' +42 problems (42 warnings) +[INFO] +[INFO] --- maven-compiler-plugin:3.6.1:compile (default) @ org.openhab.binding.zoneminder --- +[INFO] Changes detected - recompiling the module! +[INFO] Nothing to compile - all classes are up to date +[INFO] +[INFO] --- maven-scr-plugin:1.24.0:scr (generate-scr-scrdescriptor) @ org.openhab.binding.zoneminder --- +[INFO] +[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ org.openhab.binding.zoneminder --- +[INFO] Using 'UTF-8' encoding to copy filtered resources. +[INFO] skip non existing resourceDirectory D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\src\test\resources +[INFO] +[INFO] --- target-platform-configuration:1.0.0:target-platform (default-target-platform) @ org.openhab.binding.zoneminder --- +[INFO] +[INFO] --- tycho-packaging-plugin:1.0.0:package-plugin (default-package-plugin) @ org.openhab.binding.zoneminder --- +[INFO] Building jar: D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\target\org.openhab.binding.zoneminder-2.3.0-SNAPSHOT.jar +[INFO] +[INFO] --- tycho-p2-plugin:1.0.0:p2-metadata-default (default-p2-metadata-default) @ org.openhab.binding.zoneminder --- +[INFO] +[INFO] --- sat-plugin:0.4.1:checkstyle (default) @ org.openhab.binding.zoneminder --- +[INFO] Adding dependency to checkstyle:0.4.1 +[INFO] Adding dependency to checkstyle:8.1 +[INFO] There is 1 error reported by Checkstyle 8.1 with jar:file:/C:/Users/Martin.KAERVEJ/.m2/repository/org/openhab/tools/sat/sat-plugin/0.4.1/sat-plugin-0.4.1.jar!/rulesets/checkstyle/rules.xml ruleset. +[INFO] +[INFO] --- sat-plugin:0.4.1:pmd (default) @ org.openhab.binding.zoneminder --- +[INFO] Adding dependency to pmd:0.4.1 +[INFO] Adding dependency to pmd-core:5.8.1 +[INFO] Adding dependency to pmd-java:5.8.1 +[INFO] Adding dependency to pmd-javascript:5.8.1 +[INFO] Adding dependency to pmd-jsp:5.8.1 +[INFO] +[INFO] --- sat-plugin:0.4.1:findbugs (default) @ org.openhab.binding.zoneminder --- +[INFO] Adding dependency to findbugs:0.4.1 +[INFO] Adding dependency to bug-pattern:1.2.4 +[INFO] Adding dependency to spotbugs:3.1.0 +[INFO] Fork Value is false + [java] JVM args ignored when same JVM is used. +[INFO] Done FindBugs Analysis.... +[INFO] +[INFO] --- sat-plugin:0.4.1:report (default) @ org.openhab.binding.zoneminder --- +[INFO] Individual report appended to summary report. +[ERROR] Code Analysis Tool has found: + 1 error(s)! + 12 warning(s) + 214 info(s) +[WARNING] .binding.zoneminder\ESH-INF\binding\binding.xml:[3] +There were whitespace characters used for indentation. Please use tab characters instead +[WARNING] .binding.zoneminder\ESH-INF\config\monitor-config.xml:[3] +There were whitespace characters used for indentation. Please use tab characters instead +[WARNING] .binding.zoneminder\ESH-INF\config\server-config.xml:[3] +There were whitespace characters used for indentation. Please use tab characters instead +[WARNING] .binding.zoneminder\ESH-INF\thing\bridge-server.xml:[3] +There were whitespace characters used for indentation. Please use tab characters instead +[WARNING] .binding.zoneminder\ESH-INF\thing\thing-monitor.xml:[3] +There were whitespace characters used for indentation. Please use tab characters instead +[ERROR] .binding.zoneminder\META-INF\MANIFEST.MF:[0] +The jar file lib/javax.ws.rs-api-2.0.1.jar is present in the lib folder but is not present in the MANIFEST.MF file +[WARNING] .binding.zoneminder\OSGI-INF\ZoneMinderHandlerFactory.xml:[3] +There were whitespace characters used for indentation. Please use tab characters instead +[WARNING] .binding.zoneminder\pom.xml:[2] +There were whitespace characters used for indentation. Please use tab characters instead +[WARNING] org.openhab.binding.zoneminder.handler.ZoneMinderServerBridgeHandler.java:[254] +Avoid catching NullPointerException; consider removing the cause of the NPE. +[WARNING] org.openhab.binding.zoneminder.handler.ZoneMinderServerBridgeHandler.java:[547] +Avoid catching NullPointerException; consider removing the cause of the NPE. +[WARNING] org.openhab.binding.zoneminder.handler.ZoneMinderServerBridgeHandler.java:[937] +Avoid catching NullPointerException; consider removing the cause of the NPE. +[WARNING] org.openhab.binding.zoneminder.handler.ZoneMinderServerBridgeHandler.java:[1042] +Avoid catching NullPointerException; consider removing the cause of the NPE. +[WARNING] org.openhab.binding.zoneminder.handler.ZoneMinderServerBridgeHandler.java:[1161] +Avoid catching NullPointerException; consider removing the cause of the NPE. +[INFO] Detailed report can be found at: file:///D:\Development\openHAB\zoneminder\openhab2-zoneminder\git\openhab2-addons\addons\binding\org.openhab.binding.zoneminder\target\code-analysis\report.html +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD FAILURE +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 29.567 s +[INFO] Finished at: 2018-02-03T15:07:41+01:00 +[INFO] Final Memory: 62M/711M +[INFO] ------------------------------------------------------------------------ +[ERROR] Failed to execute goal org.openhab.tools.sat:sat-plugin:0.4.1:report (default) on project org.openhab.binding.zoneminder: +[ERROR] Code Analysis Tool has found 1 error(s)! +[ERROR] Please fix the errors and rerun the build. +[ERROR] -> [Help 1] +[ERROR] +[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. +[ERROR] Re-run Maven using the -X switch to enable full debug logging. +[ERROR] +[ERROR] For more information about the errors and possible solutions, please read the following articles: +[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException diff --git a/addons/binding/org.openhab.binding.zoneminder/pom.xml b/addons/binding/org.openhab.binding.zoneminder/pom.xml index 7ebb2c65ac7a9..e9c7c067ff16c 100644 --- a/addons/binding/org.openhab.binding.zoneminder/pom.xml +++ b/addons/binding/org.openhab.binding.zoneminder/pom.xml @@ -15,3 +15,4 @@ ZoneMinder Binding + \ No newline at end of file diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/ZoneMinderConstants.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/ZoneMinderConstants.java index 471b2b81f64a6..9c538f4564d65 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/ZoneMinderConstants.java +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/ZoneMinderConstants.java @@ -8,7 +8,7 @@ */ package org.openhab.binding.zoneminder; -import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.thing.ThingTypeUID; /** @@ -17,21 +17,28 @@ * * @author Martin S. Eskildsen - Initial contribution */ -@NonNullByDefault public class ZoneMinderConstants { + @NonNull public static final String BINDING_ID = "zoneminder"; // ZoneMinder Server Bridge + @NonNull public static final String BRIDGE_ZONEMINDER_SERVER = "server"; // ZoneMinder Monitor thing + @NonNull public static final String THING_ZONEMINDER_MONITOR = "monitor"; + // ZoneMinder PTZControl thing + @NonNull + public static final String THING_ZONEMINDER_PTZCONTROL = "ptzcontrol"; // ZoneMinder Server displayable name + @NonNull public static final String ZONEMINDER_SERVER_NAME = "ZoneMinder Server"; // ZoneMinder Monitor displayable name + @NonNull public static final String ZONEMINDER_MONITOR_NAME = "ZoneMinder Monitor"; /* @@ -39,10 +46,12 @@ public class ZoneMinderConstants { */ // Thing Type UID for Server + @NonNull public static final ThingTypeUID THING_TYPE_BRIDGE_ZONEMINDER_SERVER = new ThingTypeUID(BINDING_ID, BRIDGE_ZONEMINDER_SERVER); // Shared channel for all bridges / things + @NonNull public static final String CHANNEL_ONLINE = "online"; // Channel Id's for the ZoneMinder Server @@ -50,13 +59,52 @@ public class ZoneMinderConstants { public static final String CHANNEL_SERVER_CPULOAD = "cpu-load"; // Parameters for the ZoneMinder Server - public static final String PARAM_HOSTNAME = "hostname"; - public static final String PARAM_PORT = "port"; - public static final String PARAM_REFRESH_INTERVAL_ = "refresh_interval"; - public static final String PARAM_REFRESH_INTERVAL_DISKUSAGE = "refresh_interval_disk_usage"; + @NonNull + public static final String PARAM_PROTOCOL = "protocol"; + @NonNull + public static final String PARAM_HOST = "host"; + @NonNull + public static final String PARAM_HTTP_PORT = "portHttp"; + @NonNull + public static final String PARAM_TELNET_PORT = "portTelnet"; + + @NonNull + public static final String PARAM_USER = "user"; + @NonNull + public static final String PARAM_URL_PASSWORD = "password"; + + @NonNull + public static final String PARAM_URL_SITE = "urlSite"; + @NonNull + public static final String PARAM_URL_API = "urlApi"; + + @NonNull + public static final String PARAM_REFRESH_DISKUSAGE = "diskUsageRefresh"; + + @NonNull + public static final String PARAM_REFRESH_NORMAL = "refreshNormal"; + @NonNull + public static final String PARAM_REFRESH_LOW = "refreshLow"; + @NonNull + public static final String PARAM_AUTODICOVER = "autodiscover"; + + @NonNull + public static final String CONFIG_VALUE_REFRESH_BATCH = "batch"; + @NonNull + public static final String CONFIG_VALUE_REFRESH_LOW = "low"; + @NonNull + public static final String CONFIG_VALUE_REFRESH_NORMAL = "normal"; + @NonNull + public static final String CONFIG_VALUE_REFRESH_HIGH = "high"; + @NonNull + public static final String CONFIG_VALUE_REFRESH_ALARM = "alarm"; + @NonNull + public static final String CONFIG_VALUE_REFRESH_DISABLED = "disabled"; // Default values for Monitor parameters + @NonNull public static final Integer DEFAULT_HTTP_PORT = 80; + @NonNull public static final Integer DEFAULT_TELNET_PORT = 6802; /* @@ -64,38 +112,76 @@ public class ZoneMinderConstants { */ // Thing Type UID for Monitor + @NonNull public static final ThingTypeUID THING_TYPE_THING_ZONEMINDER_MONITOR = new ThingTypeUID(BINDING_ID, THING_ZONEMINDER_MONITOR); /* * Channel Id's for the ZoneMinder Monitor */ + @NonNull public static final String CHANNEL_MONITOR_ENABLED = "enabled"; + @NonNull public static final String CHANNEL_MONITOR_FORCE_ALARM = "force-alarm"; + @NonNull public static final String CHANNEL_MONITOR_EVENT_STATE = "alarm"; + @NonNull public static final String CHANNEL_MONITOR_EVENT_CAUSE = "event-cause"; + @NonNull public static final String CHANNEL_MONITOR_RECORD_STATE = "recording"; + @NonNull + public static final String CHANNEL_MONITOR_MOTION_EVENT = "motion-event"; + @NonNull public static final String CHANNEL_MONITOR_DETAILED_STATUS = "detailed-status"; + @NonNull public static final String CHANNEL_MONITOR_FUNCTION = "function"; + @NonNull public static final String CHANNEL_MONITOR_CAPTURE_DAEMON_STATE = "capture-daemon"; + @NonNull public static final String CHANNEL_MONITOR_ANALYSIS_DAEMON_STATE = "analysis-daemon"; + @NonNull public static final String CHANNEL_MONITOR_FRAME_DAEMON_STATE = "frame-daemon"; + @NonNull + public static final String CHANNEL_MONITOR_STILL_IMAGE = "image"; + @NonNull + public static final String CHANNEL_MONITOR_VIDEOURL = "videourl"; // Parameters for the ZoneMinder Monitor - public static final String PARAMETER_MONITOR_ID = "monitorId"; - public static final String PARAMETER_MONITOR_TRIGGER_TIMEOUT = "monitorTriggerTimeout"; - public static final String PARAMETER_MONITOR_EVENTTEXT = "monitorEventText"; + @NonNull + public static final String PARAMETER_MONITOR_ID = "id"; + @NonNull + public static final String PARAMETER_MONITOR_TRIGGER_TIMEOUT = "triggerTimeout"; + @NonNull + public static final String PARAMETER_MONITOR_EVENTTEXT = "eventText"; + @NonNull + public static final String PARAMETER_MONITOR_IMAGE_REFRESH = "imageRefresh"; // Default values for Monitor parameters + @NonNull public static final Integer PARAMETER_MONITOR_TRIGGER_TIMEOUT_DEFAULTVALUE = 60; public static final String PARAMETER_MONITOR_EVENTNOTE_DEFAULTVALUE = "openHAB triggered event"; // ZoneMinder Event types + @NonNull public static final String MONITOR_EVENT_NONE = ""; + @NonNull public static final String MONITOR_EVENT_SIGNAL = "Signal"; + @NonNull public static final String MONITOR_EVENT_MOTION = "Motion"; + @NonNull public static final String MONITOR_EVENT_FORCED_WEB = "Forced Web"; + @NonNull public static final String MONITOR_EVENT_OPENHAB = "openHAB"; + // Thing Type UID for PTZ Control + @NonNull + public static final ThingTypeUID THING_TYPE_THING_ZONEMINDER_PTZCONTROL = new ThingTypeUID(BINDING_ID, + THING_ZONEMINDER_PTZCONTROL); + /* + * Dyncamic Channel Id's for the ZoneMinder PTZ Control + */ + @NonNull + public static final String CHANNEL_PTZCONTROL_PRESET = "Presets"; + } diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/ZoneMinderProperties.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/ZoneMinderProperties.java index 0122921f5dc3e..2e8267b50d25a 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/ZoneMinderProperties.java +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/ZoneMinderProperties.java @@ -14,14 +14,16 @@ */ public class ZoneMinderProperties { public static final String PROPERTY_ID = "Id"; + public static final String PROPERTY_NAME = "Name"; public static final String PROPERTY_SERVER_VERSION = "Version"; public static final String PROPERTY_SERVER_API_VERSION = "API Version"; public static final String PROPERTY_SERVER_USE_API = "API Enabled"; public static final String PROPERTY_SERVER_USE_AUTHENTIFICATION = "Use Authentification"; + public static final String PROPERTY_SERVER_USE_AUTH_HASH = "Allow Auth. Hash"; public static final String PROPERTY_SERVER_TRIGGERS_ENABLED = "Triggers enabled"; + public static final String PROPERTY_SERVER_FRAME_SERVER = "Use Frame Server"; - public static final String PROPERTY_MONITOR_NAME = "Name"; public static final String PROPERTY_MONITOR_SOURCETYPE = "Sourcetype"; public static final String PROPERTY_MONITOR_ANALYSIS_FPS = "Analysis FPS"; diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderHandler.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/IZoneMinderHandler.java similarity index 79% rename from addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderHandler.java rename to addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/IZoneMinderHandler.java index 19cbceea5762c..96b1ac5b7557d 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderHandler.java +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/IZoneMinderHandler.java @@ -13,15 +13,15 @@ import org.eclipse.smarthome.core.thing.ChannelUID; -import name.eskildsen.zoneminder.IZoneMinderConnectionInfo; +import name.eskildsen.zoneminder.IZoneMinderConnectionHandler; import name.eskildsen.zoneminder.exception.ZoneMinderUrlNotFoundException; /** * Interface for ZoneMinder handlers. * - * @author Martin S. Eskildsen + * @author Martin S. Eskildsen - Initial contribution */ -public interface ZoneMinderHandler { +public interface IZoneMinderHandler { String getZoneMinderId(); @@ -30,11 +30,11 @@ public interface ZoneMinderHandler { */ String getLogIdentifier(); - void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection); + void updateAvaliabilityStatus(IZoneMinderConnectionHandler connection); void updateChannel(ChannelUID channel); - void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionInfo connection) + void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionHandler connection) throws IllegalArgumentException, GeneralSecurityException, IOException, ZoneMinderUrlNotFoundException; void onBridgeDisconnected(ZoneMinderServerBridgeHandler bridge); diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderBaseThingHandler.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderBaseThingHandler.java index ce049f2c8798c..01a1afb5463a0 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderBaseThingHandler.java +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderBaseThingHandler.java @@ -8,13 +8,12 @@ */ package org.openhab.binding.zoneminder.handler; -import java.io.IOException; import java.math.BigDecimal; -import java.security.GeneralSecurityException; import java.util.List; -import java.util.concurrent.locks.Lock; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.library.types.StringType; import org.eclipse.smarthome.core.thing.Bridge; @@ -30,15 +29,12 @@ import org.eclipse.smarthome.core.types.State; import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.zoneminder.ZoneMinderConstants; -import org.openhab.binding.zoneminder.internal.DataRefreshPriorityEnum; +import org.openhab.binding.zoneminder.internal.RefreshPriority; import org.openhab.binding.zoneminder.internal.config.ZoneMinderThingConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import name.eskildsen.zoneminder.IZoneMinderConnectionInfo; -import name.eskildsen.zoneminder.IZoneMinderSession; -import name.eskildsen.zoneminder.ZoneMinderFactory; -import name.eskildsen.zoneminder.exception.ZoneMinderUrlNotFoundException; +import name.eskildsen.zoneminder.IZoneMinderConnectionHandler; /** * The {@link ZoneMinderBaseThingHandler} is responsible for handling commands, which are @@ -46,46 +42,43 @@ * * @author Martin S. Eskildsen - Initial contribution */ -public abstract class ZoneMinderBaseThingHandler extends BaseThingHandler implements ZoneMinderHandler { +public abstract class ZoneMinderBaseThingHandler extends BaseThingHandler implements IZoneMinderHandler { + // https://www.eclipse.org/smarthome/documentation/development/bindings/thing-handler.html + private final ReentrantLock lockRefresh = new ReentrantLock(); + private final ReentrantLock lockAlarm = new ReentrantLock(); /** Logger for the Thing. */ private Logger logger = LoggerFactory.getLogger(ZoneMinderBaseThingHandler.class); /** Bridge Handler for the Thing. */ - public ZoneMinderServerBridgeHandler zoneMinderBridgeHandler = null; + public ZoneMinderServerBridgeHandler zoneMinderBridgeHandler; /** This refresh status. */ - private boolean thingRefreshed = false; + private AtomicInteger thingRefresh = new AtomicInteger(1); - /** Unique Id of the thing in zoneminder. */ - private String zoneMinderId; + private long alarmTimeoutTimestamp = 0; - /** ZoneMidner ConnectionInfo */ - private IZoneMinderConnectionInfo zoneMinderConnection = null; - - private Lock lockSession = new ReentrantLock(); - private IZoneMinderSession zoneMinderSession = null; + /** ZoneMinder ConnectionHandler */ + private IZoneMinderConnectionHandler zoneMinderConnection; /** Configuration from openHAB */ protected ZoneMinderThingConfig configuration; - private DataRefreshPriorityEnum _refreshPriority = DataRefreshPriorityEnum.SCHEDULED; - - protected boolean isOnline() { + private RefreshPriority refreshPriority = RefreshPriority.PRIORITY_NORMAL; - if (zoneMinderSession == null) { - return false; - } - - if (!zoneMinderSession.isConnected()) { - return false; + protected boolean isThingOnline() { + try { + if ((thing.getStatus() == ThingStatus.ONLINE) && getZoneMinderBridgeHandler().isOnline()) { + return true; + } + } catch (Exception ex) { + logger.error("{}: context='isThingOnline' Exception occurred", getLogIdentifier(), ex); } - - return true; + return false; } - public DataRefreshPriorityEnum getRefreshPriority() { - return _refreshPriority; + public RefreshPriority getThingRefreshPriority() { + return refreshPriority; } public ZoneMinderBaseThingHandler(Thing thing) { @@ -100,104 +93,179 @@ public ZoneMinderBaseThingHandler(Thing thing) { */ @Override public void initialize() { - - updateStatus(ThingStatus.ONLINE); - try { - - } catch (Exception ex) { - logger.error("{}: BridgeHandler failed to initialize. Exception='{}'", getLogIdentifier(), ex.getMessage()); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR); - } finally { - } + updateStatus(ThingStatus.OFFLINE); } protected boolean isConnected() { - if (zoneMinderSession == null) { + if ((getThing().getStatus() != ThingStatus.ONLINE) || (zoneMinderConnection == null) + || (getZoneMinderBridgeHandler() == null)) { return false; } - return zoneMinderSession.isConnected(); + return getZoneMinderBridgeHandler().isConnected(); + } + + protected IZoneMinderConnectionHandler aquireSession() { + return aquireSessionInternal(false); } - protected IZoneMinderSession aquireSession() { - lockSession.lock(); - return zoneMinderSession; + protected IZoneMinderConnectionHandler aquireSessionWait() { + return aquireSessionInternal(false); + } + + private IZoneMinderConnectionHandler aquireSessionInternal(boolean timeout) { + boolean result = true; + if (result) { + return zoneMinderConnection; + } + + return null; } protected void releaseSession() { - lockSession.unlock(); + // lockSession.unlock(); + } + + protected boolean forceStartAlarmRefresh() { + lockAlarm.lock(); + try { + if (refreshPriority != RefreshPriority.PRIORITY_ALARM) { + logger.debug("{}: context='startAlarmRefresh' Starting ALARM refresh...", getLogIdentifier()); + refreshPriority = RefreshPriority.PRIORITY_ALARM; + + // If already activated and called again, it is the event from ZoneMidner that was triggered from + // openHAB + alarmTimeoutTimestamp = -1; + } + } finally { + lockAlarm.unlock(); + } + return true; } /** * Method to start a priority data refresh task. */ - protected boolean startPriorityRefresh() { - - logger.info("[MONITOR-{}]: Starting High Priority Refresh", getZoneMinderId()); - _refreshPriority = DataRefreshPriorityEnum.HIGH_PRIORITY; + protected boolean startAlarmRefresh(long timeout) { + lockAlarm.lock(); + try { + if (refreshPriority != RefreshPriority.PRIORITY_ALARM) { + logger.debug("{}: context='startAlarmRefresh' Starting ALARM refresh...", getLogIdentifier()); + refreshPriority = RefreshPriority.PRIORITY_ALARM; + alarmTimeoutTimestamp = System.currentTimeMillis() + timeout * 1000; + } + } finally { + lockAlarm.unlock(); + } return true; } + protected void tryStopAlarmRefresh() { + lockAlarm.lock(); + try { + if ((alarmTimeoutTimestamp == -1) || (refreshPriority != RefreshPriority.PRIORITY_ALARM)) { + return; + } + if (alarmTimeoutTimestamp < System.currentTimeMillis()) { + logger.debug("{}: context='tryStopAlarmRefresh' - Alarm refresh timed out - stopping alarm refresh ...", + getLogIdentifier()); + refreshPriority = RefreshPriority.PRIORITY_NORMAL; + + alarmTimeoutTimestamp = 0; + } + } finally { + lockAlarm.unlock(); + } + } + /** * Method to stop the data Refresh task. */ - protected void stopPriorityRefresh() { - logger.info("{}: Stopping Priority Refresh for Monitor", getLogIdentifier()); - _refreshPriority = DataRefreshPriorityEnum.SCHEDULED; + protected void forceStopAlarmRefresh() { + lockAlarm.lock(); + try { + if (refreshPriority == RefreshPriority.PRIORITY_ALARM) { + logger.debug("{}: context='forceStopAlarmRefresh' Stopping ALARM refresh...", getLogIdentifier()); + refreshPriority = RefreshPriority.PRIORITY_NORMAL; + alarmTimeoutTimestamp = 0; + } + } finally { + lockAlarm.unlock(); + } + } + + protected void onThingStatusChanged(ThingStatus thingStatus) { } @Override public void dispose() { - } /** * Helper method for getting ChannelUID from ChannelId. * */ - public ChannelUID getChannelUIDFromChannelId(String id) { + + public ChannelUID getChannelUIDFromChannelId(@NonNull String id) { Channel ch = thing.getChannel(id); + if (ch == null) { + return null; + } return ch.getUID(); } - protected abstract void onFetchData(); + protected abstract void onFetchData(RefreshPriority refreshPriority); /** * Method to Refresh Thing Handler. */ - public final synchronized void refreshThing(IZoneMinderSession session, DataRefreshPriorityEnum refreshPriority) { - - if ((refreshPriority != getRefreshPriority()) && (!isConnected())) { - return; - } - - if (refreshPriority == DataRefreshPriorityEnum.HIGH_PRIORITY) { - logger.debug("{}: Performing HIGH PRIORITY refresh", getLogIdentifier()); - } else { - logger.debug("{}: Performing refresh", getLogIdentifier()); - } - - if (getZoneMinderBridgeHandler() != null) { - if (isConnected()) { + public final void refreshThing(RefreshPriority refreshPriority) { + boolean isLocked = false; + try { + if (!isConnected()) { + return; + } - logger.debug("{}: refreshThing(): Bridge '{}' Found for Thing '{}'!", getLogIdentifier(), - getThing().getUID(), this.getThing().getUID()); + if (refreshPriority == RefreshPriority.PRIORITY_ALARM) { + if (!lockRefresh.tryLock()) { + logger.warn( + "{}: context='refreshThing' Failed to obtain refresh lock for '{}' - refreshThing skipped!", + getLogIdentifier(), getThing().getUID()); + isLocked = false; + return; + } + } else { + lockRefresh.lock(); + } + isLocked = true; - onFetchData(); + if (getZoneMinderBridgeHandler() != null) { + onFetchData(refreshPriority); + } else { + logger.warn( + "{}: context='refreshThing' - BridgeHandler was not accessible for '{}', skipping refreshThing", + getLogIdentifier(), getThing().getUID()); } - } - Thing thing = getThing(); - List channels = thing.getChannels(); - logger.debug("{}: refreshThing(): Refreshing Thing - {}", getLogIdentifier(), thing.getUID()); + if (!isThingRefreshed()) { + Thing thing = getThing(); + List channels = thing.getChannels(); + logger.debug("{}: context=refreshThing': Refreshing Channels for '{}'", getLogIdentifier(), + thing.getUID()); - for (Channel channel : channels) { - updateChannel(channel.getUID()); + for (Channel channel : channels) { + updateChannel(channel.getUID()); + } + this.channelRefreshDone(); + } + } catch (Exception ex) { + logger.error("{}: context='refreshThing' - Exception when refreshing '{}' ", getLogIdentifier(), + getThing().getUID(), ex); + } finally { + if (isLocked) { + lockRefresh.unlock(); + } } - - this.setThingRefreshed(true); - logger.debug("[{}: refreshThing(): Thing Refreshed - {}", getLogIdentifier(), thing.getUID()); - } /** @@ -205,18 +273,16 @@ public final synchronized void refreshThing(IZoneMinderSession session, DataRefr * * @return zoneMinderBridgeHandler */ - public synchronized ZoneMinderServerBridgeHandler getZoneMinderBridgeHandler() { - + public /* synchronized */ZoneMinderServerBridgeHandler getZoneMinderBridgeHandler() { if (this.zoneMinderBridgeHandler == null) { - Bridge bridge = getBridge(); if (bridge == null) { - logger.debug("{}: getZoneMinderBridgeHandler(): Unable to get bridge!", getLogIdentifier()); + logger.warn("{}: context='getZoneMinderBridgeHandler' - Unable to get bridge!", getLogIdentifier()); return null; } - logger.debug("{}: getZoneMinderBridgeHandler(): Bridge for '{}' - '{}'", getLogIdentifier(), + logger.debug("{}: context='getZoneMinderBridgeHandler' Bridge for '{}' - '{}'", getLogIdentifier(), getThing().getUID(), bridge.getUID()); ThingHandler handler = null; try { @@ -229,7 +295,8 @@ public synchronized ZoneMinderServerBridgeHandler getZoneMinderBridgeHandler() { if (handler instanceof ZoneMinderServerBridgeHandler) { this.zoneMinderBridgeHandler = (ZoneMinderServerBridgeHandler) handler; } else { - logger.debug("{}: getZoneMinderBridgeHandler(): Unable to get bridge handler!", getLogIdentifier()); + logger.debug("{}: context='getZoneMinderBridgeHandler' Unable to get bridge handler!", + getLogIdentifier()); } } @@ -243,11 +310,9 @@ public synchronized ZoneMinderServerBridgeHandler getZoneMinderBridgeHandler() { */ @Override public void updateChannel(ChannelUID channel) { - OnOffType onOffType; - switch (channel.getId()) { case ZoneMinderConstants.CHANNEL_ONLINE: - updateState(channel, getChannelBoolAsOnOffState(isOnline())); + updateState(channel, getChannelBoolAsOnOffState(isThingOnline())); break; default: logger.error( @@ -261,33 +326,13 @@ public void handleCommand(ChannelUID channelUID, Command command) { } @Override - public void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionInfo connection) - throws IllegalArgumentException, GeneralSecurityException, IOException, ZoneMinderUrlNotFoundException { - lockSession.lock(); - try { - zoneMinderSession = ZoneMinderFactory.CreateSession(connection); - - } finally { - lockSession.unlock(); - } + public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) { + super.bridgeStatusChanged(bridgeStatusInfo); } @Override public void onBridgeDisconnected(ZoneMinderServerBridgeHandler bridge) { - - if (bridge.getThing().getUID().equals(getThing().getBridgeUID())) { - - this.setThingRefreshed(false); - } - - lockSession.lock(); - try { - zoneMinderSession = null; - - } finally { - lockSession.unlock(); - } - + zoneMinderConnection = null; } /** @@ -316,7 +361,8 @@ public Channel getChannel(ChannelUID channelUID) { * @return thingRefresh */ public boolean isThingRefreshed() { - return thingRefreshed; + return (thingRefresh.get() > 0) ? false : true; + // return (thingRefresh > 0) ? false : true; } /** @@ -324,8 +370,15 @@ public boolean isThingRefreshed() { * * @param {boolean} refreshed Sets status refreshed of thing */ - public void setThingRefreshed(boolean refreshed) { - this.thingRefreshed = refreshed; + public void requestChannelRefresh() { + thingRefresh.incrementAndGet(); + // this.thingRefresh = this.thingRefresh + 1; + } + + public void channelRefreshDone() { + if (thingRefresh.decrementAndGet() < 0) { + thingRefresh.set(0); + } } protected abstract String getZoneMinderThingType(); @@ -367,7 +420,8 @@ protected State getChannelStringAsStringState(String channelValue) { } } catch (Exception ex) { - logger.error("{}", ex.getMessage()); + + logger.error("{}: Exception occurred in 'getChannelStringAsStringState' ", getLogIdentifier(), ex); } return state; @@ -383,25 +437,28 @@ protected State getChannelBoolAsOnOffState(boolean value) { } } catch (Exception ex) { - logger.error("{}: Exception occurred in 'getChannelBoolAsOnOffState()' (Exception='{}')", - getLogIdentifier(), ex.getMessage()); + logger.error("{}: Exception occurred in 'getChannelBoolAsOnOffState()' ", getLogIdentifier(), ex); } return state; } + @Override + public void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionHandler connection) { + zoneMinderConnection = connection; + } + @Override public abstract String getLogIdentifier(); protected void updateThingStatus(ThingStatus thingStatus, ThingStatusDetail statusDetail, String statusDescription) { - ThingStatusInfo curStatusInfo = thing.getStatusInfo(); String curDescription = ((curStatusInfo.getDescription() == null) ? "" : curStatusInfo.getDescription()); - // Status changed - if ((curStatusInfo.getStatus() != thingStatus) || (curStatusInfo.getStatusDetail() != statusDetail) - || (curDescription != statusDescription)) { + // Status changed + if (!curStatusInfo.getStatus().equals(thingStatus) || !curStatusInfo.getStatusDetail().equals(statusDetail) + || !curDescription.equals(statusDescription)) { // Update Status correspondingly if ((thingStatus == ThingStatus.OFFLINE) && (statusDetail != ThingStatusDetail.NONE)) { logger.info("{}: Thing status changed from '{}' to '{}' (DetailedStatus='{}', Description='{}')", @@ -412,7 +469,8 @@ protected void updateThingStatus(ThingStatus thingStatus, ThingStatusDetail stat thingStatus); updateStatus(thingStatus); } + + onThingStatusChanged(thingStatus); } } - } diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderServerBridgeHandler.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderServerBridgeHandler.java index 3a68a6bd0c5b9..ea19d7a22e5b9 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderServerBridgeHandler.java +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderServerBridgeHandler.java @@ -10,6 +10,9 @@ import java.io.IOException; import java.math.BigDecimal; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Hashtable; @@ -19,9 +22,9 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import javax.security.auth.login.FailedLoginException; - import org.apache.commons.lang.StringUtils; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.smarthome.config.core.Configuration; import org.eclipse.smarthome.config.discovery.DiscoveryService; import org.eclipse.smarthome.core.library.types.DecimalType; import org.eclipse.smarthome.core.library.types.OnOffType; @@ -33,7 +36,6 @@ import org.eclipse.smarthome.core.thing.ThingStatusDetail; import org.eclipse.smarthome.core.thing.ThingStatusInfo; import org.eclipse.smarthome.core.thing.ThingTypeUID; -import org.eclipse.smarthome.core.thing.ThingUID; import org.eclipse.smarthome.core.thing.binding.BaseBridgeHandler; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.RefreshType; @@ -41,7 +43,8 @@ import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.zoneminder.ZoneMinderConstants; import org.openhab.binding.zoneminder.ZoneMinderProperties; -import org.openhab.binding.zoneminder.internal.DataRefreshPriorityEnum; +import org.openhab.binding.zoneminder.internal.RefreshPriority; +import org.openhab.binding.zoneminder.internal.ZoneMinderConnectionStatus; import org.openhab.binding.zoneminder.internal.config.ZoneMinderBridgeServerConfig; import org.openhab.binding.zoneminder.internal.discovery.ZoneMinderDiscoveryService; import org.osgi.framework.ServiceRegistration; @@ -50,28 +53,35 @@ import com.google.common.collect.Sets; -import name.eskildsen.zoneminder.IZoneMinderConnectionInfo; -import name.eskildsen.zoneminder.IZoneMinderDaemonStatus; -import name.eskildsen.zoneminder.IZoneMinderDiskUsage; -import name.eskildsen.zoneminder.IZoneMinderHostLoad; -import name.eskildsen.zoneminder.IZoneMinderHostVersion; -import name.eskildsen.zoneminder.IZoneMinderMonitorData; +import name.eskildsen.zoneminder.IZoneMinderConnectionHandler; +import name.eskildsen.zoneminder.IZoneMinderEventSession; import name.eskildsen.zoneminder.IZoneMinderServer; -import name.eskildsen.zoneminder.IZoneMinderSession; import name.eskildsen.zoneminder.ZoneMinderFactory; -import name.eskildsen.zoneminder.api.config.ZoneMinderConfig; -import name.eskildsen.zoneminder.api.config.ZoneMinderConfigEnum; +import name.eskildsen.zoneminder.common.ZoneMinderConfigEnum; +import name.eskildsen.zoneminder.data.IMonitorDataGeneral; +import name.eskildsen.zoneminder.data.IZoneMinderDaemonStatus; +import name.eskildsen.zoneminder.data.IZoneMinderDiskUsage; +import name.eskildsen.zoneminder.data.IZoneMinderHostLoad; +import name.eskildsen.zoneminder.data.IZoneMinderHostVersion; +import name.eskildsen.zoneminder.data.ZoneMinderConfig; +import name.eskildsen.zoneminder.exception.ZoneMinderApiNotEnabledException; +import name.eskildsen.zoneminder.exception.ZoneMinderAuthenticationException; +import name.eskildsen.zoneminder.exception.ZoneMinderException; +import name.eskildsen.zoneminder.exception.ZoneMinderGeneralException; +import name.eskildsen.zoneminder.exception.ZoneMinderInvalidData; +import name.eskildsen.zoneminder.exception.ZoneMinderResponseException; import name.eskildsen.zoneminder.exception.ZoneMinderUrlNotFoundException; /** * Handler for a ZoneMinder Server. * - * @author Martin S. Eskildsen + * @author Martin S. Eskildsen - Initial contribution * */ -public class ZoneMinderServerBridgeHandler extends BaseBridgeHandler implements ZoneMinderHandler { +public class ZoneMinderServerBridgeHandler extends BaseBridgeHandler implements IZoneMinderHandler { public static final int TELNET_TIMEOUT = 5000; + static final int HTTP_TIMEOUT = 5000; public static final Set SUPPORTED_THING_TYPES = Sets .newHashSet(ZoneMinderConstants.THING_TYPE_BRIDGE_ZONEMINDER_SERVER); @@ -79,31 +89,39 @@ public class ZoneMinderServerBridgeHandler extends BaseBridgeHandler implements /** * Logger */ - private final Logger logger = LoggerFactory.getLogger(getClass()); + private Logger logger = LoggerFactory.getLogger(getClass()); + + private ZoneMinderConnectionStatus zmConnectStatus = ZoneMinderConnectionStatus.UNINITIALIZED; + private ZoneMinderConnectionStatus lastSucceededStatus = ZoneMinderConnectionStatus.UNINITIALIZED; - private ZoneMinderDiscoveryService discoveryService = null; - private ServiceRegistration discoveryRegistration = null; + private RefreshPriority forcedPriority = RefreshPriority.UNKNOWN; - private ScheduledFuture taskWatchDog = null; - private int refreshFrequency = 0; - private int refreshCycleCount = 0; + private ZoneMinderDiscoveryService discoveryService; + private ServiceRegistration discoveryRegistration; + + private ScheduledFuture taskWatchDog; + private Integer refreshCycleCount = 0; /** Connection status for the bridge. */ private boolean connected = false; - private ThingStatus curBridgeStatus = ThingStatus.UNKNOWN; - - protected boolean _online = false; private Runnable watchDogRunnable = new Runnable() { - private int watchDogCount = -1; + private int watchDogCount = 0; @Override public void run() { - try { - updateAvaliabilityStatus(zoneMinderConnection); + updateAvaliabilityStatus(getZoneMinderConnection()); + + // Only Discover if Bridge is online + if (thing.getStatusInfo().getStatus() != ThingStatus.ONLINE) { + return; + } - if ((discoveryService != null) && (getBridgeConfig().getAutodiscoverThings() == true)) { + // Check if autodiscovery is enabled + boolean bAutoDiscover = getBridgeConfig().getAutodiscoverThings(); + + if ((discoveryService != null) && bAutoDiscover) { watchDogCount++; // Run every two minutes if ((watchDogCount % 8) == 0) { @@ -111,8 +129,11 @@ public void run() { watchDogCount = 0; } } + } catch (Exception exception) { - logger.error("[WATCHDOG]: Server run(): Exception: {}", exception.getMessage()); + StackTraceElement ste = exception.getStackTrace()[0]; + logger.error("[WATCHDOG]: Server run(): StackTrace: File='{}', Line='{}', Method='{}'", + ste.getFileName(), ste.getLineNumber(), ste.getMethodName(), exception); } } }; @@ -123,112 +144,70 @@ public void run() { private String channelCpuLoad = ""; private String channelDiskUsage = ""; - private Boolean isInitialized = false; + private boolean initialized = false; - private IZoneMinderSession zoneMinderSession = null; - private IZoneMinderConnectionInfo zoneMinderConnection = null; + private IZoneMinderEventSession zoneMinderEventSession; + private IZoneMinderConnectionHandler zoneMinderConnection; private ScheduledFuture taskRefreshData = null; - private ScheduledFuture taskPriorityRefreshData = null; - - private Runnable refreshDataRunnable = () -> { - try { - boolean fetchDiskUsage = false; - - if (!isOnline()) { - logger.debug("{}: Bridge '{}' is noit online skipping refresh", getLogIdentifier(), thing.getUID()); - } - - refreshCycleCount++; - int iMaxCycles; - boolean resetCount = false; - boolean doRefresh = false; + private IZoneMinderConnectionHandler getZoneMinderConnection() { + return zoneMinderConnection; + } - // Disk Usage is disabled - if (getBridgeConfig().getRefreshIntervalLowPriorityTask() == 0) { - iMaxCycles = getBridgeConfig().getRefreshInterval(); - resetCount = true; - doRefresh = true; - } else { - iMaxCycles = getBridgeConfig().getRefreshIntervalLowPriorityTask() * 60; - doRefresh = true; - if ((refreshCycleCount * refreshFrequency) >= (getBridgeConfig().getRefreshIntervalLowPriorityTask() - * 60)) { - fetchDiskUsage = true; - resetCount = true; + private Runnable refreshDataRunnable = new Runnable() { + @Override + public void run() { + try { + if (!isConnected()) { + return; + } + refreshCycleCount++; + int intervalBatch = 3600; + int intervalLow = getBridgeConfig().getRefreshIntervalLow(); + int intervalNormal = getBridgeConfig().getRefreshIntervalNormal(); + int intervalHigh = 5; + + RefreshPriority cyclePriority = RefreshPriority.PRIORITY_ALARM; + + // boolean isBatch = ((refreshCycleCount % intervalBatch) == 0); + boolean isLow = ((refreshCycleCount % intervalLow) == 0); + boolean isNormal = ((refreshCycleCount % intervalNormal) == 0); + boolean isHigh = ((refreshCycleCount % intervalHigh) == 0); + + if (isLow) { + cyclePriority = RefreshPriority.PRIORITY_BATCH; + } else if (isLow) { + cyclePriority = RefreshPriority.PRIORITY_LOW; + } else if (isNormal) { + cyclePriority = RefreshPriority.PRIORITY_NORMAL; + } else if (isHigh) { + cyclePriority = RefreshPriority.PRIORITY_HIGH; } - } - logger.debug( - "{}: Running Refresh data task count='{}', freq='{}', max='{}', interval='{}', intervalLow='{}'", - getLogIdentifier(), refreshCycleCount, refreshFrequency, iMaxCycles, - getBridgeConfig().getRefreshInterval(), getBridgeConfig().getRefreshIntervalLowPriorityTask()); + refreshThing(cyclePriority); - if (doRefresh) { - if (resetCount == true) { + if ((refreshCycleCount >= intervalLow) && (refreshCycleCount >= intervalNormal) + && (refreshCycleCount >= intervalHigh) && (refreshCycleCount >= intervalBatch)) { refreshCycleCount = 0; } - logger.debug("{}: 'refreshDataRunnable()': (diskUsage='{}')", getLogIdentifier(), fetchDiskUsage); - - refreshThing(zoneMinderSession, fetchDiskUsage); - } - } catch (Exception exception) { - logger.error("{}: monitorRunnable::run(): Exception: ", getLogIdentifier(), exception); - } - }; - - private Runnable refreshPriorityDataRunnable = () -> { - try { - // Make sure priority updates is done - for (Thing thing : getThing().getThings()) { - try { - if (thing.getThingTypeUID().equals(ZoneMinderConstants.THING_TYPE_THING_ZONEMINDER_MONITOR)) { - Thing thingMonitor = thing; - - ZoneMinderBaseThingHandler thingHandler = (ZoneMinderBaseThingHandler) thing.getHandler(); - if (thingHandler != null) { - - if (thingHandler.getRefreshPriority() == DataRefreshPriorityEnum.HIGH_PRIORITY) { - logger.debug("[MONITOR-{}]: RefreshPriority is High Priority", - thingHandler.getZoneMinderId()); - thingHandler.refreshThing(zoneMinderSession, DataRefreshPriorityEnum.HIGH_PRIORITY); - } - } else { - logger.debug( - "[MONITOR]: refreshThing not called for monitor, since thingHandler is 'null'"); - } - } - } catch (NullPointerException ex) { - // This isn't critical (unless it comes over and over). There seems to be a bug so that a - // null - // pointer exception is coming every now and then. - // HAve to find the reason for that. Until thenm, don't Spamm - logger.error("[MONITOR]: Method 'refreshThing()' for Bridge failed for thing='{}' - Exception: ", - thing.getUID(), ex); - } catch (Exception ex) { - logger.error("[MONITOR]: Method 'refreshThing()' for Bridge failed for thing='{}' - Exception: ", - thing.getUID(), ex); - } + } catch (Exception exception) { + logger.error("{}: monitorRunnable::run()", getLogIdentifier(), exception); } - } catch (Exception exception) { - logger.error("[MONITOR]: monitorRunnable::run(): Exception: ", exception); } }; /** * Constructor * - * - * @param bridge - * Bridge object representing a ZoneMinder Server + * @param bridge Bridge object representing a ZoneMinder Server */ public ZoneMinderServerBridgeHandler(Bridge bridge) { super(bridge); - logger.info("{}: Starting ZoneMinder Server Bridge Handler (Bridge='{}')", getLogIdentifier(), - bridge.getBridgeUID()); + logger.info("{}: context='constructor' Starting ZoneMinder Server Bridge Handler (Bridge='{}')", + getLogIdentifier(), bridge.getBridgeUID()); } /** @@ -236,65 +215,54 @@ public ZoneMinderServerBridgeHandler(Bridge bridge) { */ @Override public void initialize() { - logger.debug("[BRIDGE]: About to initialize bridge " + ZoneMinderConstants.BRIDGE_ZONEMINDER_SERVER); try { - updateStatus(ThingStatus.OFFLINE); - logger.info("BRIDGE: ZoneMinder Server Bridge Handler Initialized"); - logger.debug("BRIDGE: HostName: {}", getBridgeConfig().getHostName()); - logger.debug("BRIDGE: Protocol: {}", getBridgeConfig().getProtocol()); - logger.debug("BRIDGE: Port HTTP(S) {}", getBridgeConfig().getHttpPort()); - logger.debug("BRIDGE: Port Telnet {}", getBridgeConfig().getTelnetPort()); - logger.debug("BRIDGE: Server Path {}", getBridgeConfig().getServerBasePath()); - logger.debug("BRIDGE: User: {}", getBridgeConfig().getUserName()); - logger.debug("BRIDGE: Refresh interval: {}", getBridgeConfig().getRefreshInterval()); - logger.debug("BRIDGE: Low prio. refresh: {}", getBridgeConfig().getRefreshIntervalLowPriorityTask()); - logger.debug("BRIDGE: Autodiscovery: {}", getBridgeConfig().getAutodiscoverThings()); - - closeConnection(); - - zoneMinderConnection = ZoneMinderFactory.CreateConnection(getBridgeConfig().getProtocol(), - getBridgeConfig().getHostName(), getBridgeConfig().getHttpPort(), getBridgeConfig().getTelnetPort(), - getBridgeConfig().getServerBasePath(), getBridgeConfig().getUserName(), - getBridgeConfig().getPassword(), 3000); + zoneMinderConnection = null; taskRefreshData = null; - taskPriorityRefreshData = null; - } catch (Exception ex) { - logger.error("[BRIDGE]: 'ZoneMinderServerBridgeHandler' failed to initialize. Exception='{}'", - ex.getMessage()); - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR); - } finally { + updateStatus(ThingStatus.OFFLINE); + + ZoneMinderBridgeServerConfig config = getBridgeConfig(); + logger.info("{}: ZoneMinder Server Bridge Handler Initialized", getLogIdentifier()); + logger.debug("{}: HostName: {}", getLogIdentifier(), config.getHost()); + logger.debug("{}: Protocol: {}", getLogIdentifier(), config.getProtocol()); + logger.debug("{}: Port HTTP(S) {}", getLogIdentifier(), config.getHttpPort()); + logger.debug("{}: Port Telnet {}", getLogIdentifier(), config.getTelnetPort()); + logger.debug("{}: Portal Path {}", getLogIdentifier(), config.getServerBasePath()); + logger.debug("{}: API Path {}", getLogIdentifier(), config.getServerApiPath()); + logger.debug("{}: Refresh interval: {}", getLogIdentifier(), config.getRefreshIntervalNormal()); + logger.debug("{}: Low prio. refresh: {}", getLogIdentifier(), config.getRefreshIntervalLow()); + logger.debug("{}: Autodiscovery: {}", getLogIdentifier(), config.getAutodiscoverThings()); + startWatchDogTask(); - isInitialized = true; - } - } + initialized = true; + return; + } catch (Exception ex) { + if (zoneMinderConnection == null) { + logger.error( + "{}: 'ZoneMinderServerBridgeHandler' general configuration error occurred. Failed to initialize.", + getLogIdentifier(), ex); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR); + } else { + logger.error("{}: 'ZoneMinderServerBridgeHandler' failed to initialize", getLogIdentifier(), ex); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR); + } - /** - * Method to find the lowest possible refresh rate (based on configuration) - * - * @param refreshRate - * @return - */ - protected int calculateCommonRefreshFrequency(int refreshRate) { - // Check if 30, 15, 10 or 5 seconds is possible - if ((refreshRate % 30) == 0) { - return 30; - } else if ((refreshRate % 15) == 0) { - return 15; - } else if ((refreshRate % 10) == 0) { - return 10; - } else if ((refreshRate % 5) == 0) { - return 5; } - // Hmm, didn't find a obvious shared value. Run every second... - return 1; + initialized = false; + + } + protected IZoneMinderConnectionHandler aquireSession() { + return zoneMinderConnection; + } + + protected void releaseSession() { } protected void startWatchDogTask() { - taskWatchDog = startTask(watchDogRunnable, 0, 15, TimeUnit.SECONDS); + taskWatchDog = startTask(watchDogRunnable, 5, 1, TimeUnit.SECONDS); } protected void stopWatchDogTask() { @@ -302,32 +270,47 @@ protected void stopWatchDogTask() { taskWatchDog = null; } + @Override + public void handleConfigurationUpdate(Map configurationParameters) { + try { + setConnected(false); + zoneMinderConnection = null; + setConnectionStatus(ZoneMinderConnectionStatus.UNINITIALIZED); + } catch (IllegalArgumentException | GeneralSecurityException | IOException | ZoneMinderUrlNotFoundException e) { + logger.error("{}: context='handleConfigurationUpdate'", getLogIdentifier(), e.getCause()); + } + super.handleConfigurationUpdate(configurationParameters); + } + + @Override + protected void updateConfiguration(Configuration configuration) { + super.updateConfiguration(configuration); + // Inform thing handlers of connection + } + /** */ @Override public void dispose() { - try { - logger.debug("{}: Stop polling of ZoneMinder Server API", getLogIdentifier()); + logger.debug("{}: context='dispose' Stop polling of ZoneMinder Server API", getLogIdentifier()); - logger.info("{}: Stopping Discovery service", getLogIdentifier()); - // Remove the discovery service - if (discoveryService != null) { - discoveryService.deactivate(); - discoveryService = null; - } + logger.info("{}: context='dispose' Stopping Discovery service", getLogIdentifier()); + // Remove the discovery service + if (discoveryService != null) { + discoveryService.deactivate(); + discoveryService = null; + } - if (discoveryRegistration != null) { - discoveryRegistration.unregister(); - discoveryRegistration = null; - } + if (discoveryRegistration != null) { + discoveryRegistration.unregister(); + discoveryRegistration = null; + } - logger.info("{}: Stopping WatchDog task", getLogIdentifier()); - stopWatchDogTask(); + logger.info("{}: context='dispose' Stopping WatchDog task", getLogIdentifier()); + stopWatchDogTask(); - logger.info("{}: Stopping refresh data task", getLogIdentifier()); - stopTask(taskRefreshData); - } catch (Exception ex) { - } + logger.info("{}: context='dispose' Stopping refresh data task", getLogIdentifier()); + stopTask(taskRefreshData); } protected String getThingId() { @@ -336,33 +319,21 @@ protected String getThingId() { @Override public String getZoneMinderId() { - return getThing().getUID().getAsString(); } - @Override - public void channelLinked(ChannelUID channelUID) { - // can be overridden by subclasses - ThingUID s1 = getThing().getUID(); - ThingTypeUID s2 = getThing().getThingTypeUID(); - logger.debug("{}: Channel '{}' was linked to '{}'", getLogIdentifier(), channelUID.getAsString(), - this.thing.getThingTypeUID()); - } - - @Override - public void channelUnlinked(ChannelUID channelUID) { - // can be overridden by subclasses - logger.debug("{}: Channel '{}' was unlinked from '{}'", getLogIdentifier(), channelUID.getAsString(), - this.thing.getThingTypeUID()); - } - - protected ArrayList getMonitors(IZoneMinderSession session) { - + protected ArrayList getMonitors(IZoneMinderConnectionHandler session) + throws ZoneMinderAuthenticationException { if (isConnected()) { - return ZoneMinderFactory.getServerProxy(session).getMonitors(); - } + try { + return ZoneMinderFactory.getServerProxy(session).getMonitors(); + } catch (ZoneMinderGeneralException | ZoneMinderResponseException | ZoneMinderInvalidData ex) { + logger.error("{}: context='getMonitors' Exception {}", getLogIdentifier(), ex.getMessage(), + ex.getCause()); - return new ArrayList(); + } + } + return new ArrayList<>(); } @@ -375,12 +346,12 @@ protected ZoneMinderBridgeServerConfig getBridgeConfig() { */ public ZoneMinderBaseThingHandler getZoneMinderThingHandlerFromZoneMinderId(ThingTypeUID thingTypeUID, String zoneMinderId) { - // Inform thing handlers of connection List things = getThing().getThings(); for (Thing thing : things) { ZoneMinderBaseThingHandler thingHandler = (ZoneMinderBaseThingHandler) thing.getHandler(); + if ((thingHandler.getZoneMinderId().equals(zoneMinderId)) && (thing.getThingTypeUID().equals(thingTypeUID))) { return thingHandler; @@ -394,84 +365,121 @@ public void handleCommand(ChannelUID channelUID, Command command) { logger.debug("{}: Update '{}' with '{}'", getLogIdentifier(), channelUID.getAsString(), command.toString()); } - protected synchronized void refreshThing(IZoneMinderSession session, boolean fetchDiskUsage) { + protected /* synchronized */ void refreshThing(RefreshPriority refresh) { + IZoneMinderServer zoneMinderServerProxy = null; + RefreshPriority curPriority = RefreshPriority.DISABLED; - logger.debug("{}: 'refreshThing()': Thing='{}'!", getLogIdentifier(), this.getThing().getUID()); + // logger.debug("{}: 'refreshThing()': Thing='{}'!", getLogIdentifier(), this.getThing().getUID()); List channels = getThing().getChannels(); List things = getThing().getThings(); - IZoneMinderServer zoneMinderServerProxy = ZoneMinderFactory.getServerProxy(session); - if (zoneMinderServerProxy == null) { - logger.warn("{}: Could not obtain ZonerMinderServerProxy ", getLogIdentifier()); + try { + if (forcedPriority == RefreshPriority.UNKNOWN) { + return; + } else if (forcedPriority == RefreshPriority.DISABLED) { + curPriority = refresh; + } else { + curPriority = forcedPriority; + forcedPriority = RefreshPriority.DISABLED; + } - // Make sure old data is cleared - channelCpuLoad = ""; - channelDiskUsage = ""; + zoneMinderServerProxy = ZoneMinderFactory.getServerProxy(aquireSession()); + if (zoneMinderServerProxy == null) { + logger.warn("{}: Could not obtain ZonerMinderServerProxy ", getLogIdentifier()); - } else if (isConnected()) { - /* - * Fetch data for Bridge - */ - IZoneMinderHostLoad hostLoad = null; - try { - hostLoad = zoneMinderServerProxy.getHostCpuLoad(); - logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), - zoneMinderServerProxy.getHttpUrl(), zoneMinderServerProxy.getHttpResponseCode(), - zoneMinderServerProxy.getHttpResponseMessage()); + // Make sure old data is cleared + channelCpuLoad = ""; + channelDiskUsage = ""; - } catch (FailedLoginException | ZoneMinderUrlNotFoundException | IOException ex) { - logger.error("{}: Exception thrown in call to ZoneMinderHostLoad: ", getLogIdentifier(), ex); - } + } else if (isConnected()) { + /* + * Fetch data for Bridge + */ + if (curPriority.isPriorityActive(RefreshPriority.PRIORITY_NORMAL)) { + IZoneMinderHostLoad hostLoad = null; + try { + hostLoad = zoneMinderServerProxy.getHostCpuLoad(); + logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), + hostLoad.getHttpRequestUrl(), hostLoad.getHttpStatus(), + hostLoad.getHttpResponseMessage()); + + } catch (ZoneMinderUrlNotFoundException | IOException ex) { + logger.error("{}: Exception thrown in call to ZoneMinderHostLoad ('{}')", getLogIdentifier(), + ex.getMessage()); + } catch (ZoneMinderException ex) { + logger.error( + "{}: context='refreshThing' error in call to 'getHostCpuLoad' ExceptionClass='{}' - Message='{}'", + getLogIdentifier(), ex.getClass().getCanonicalName(), ex.getMessage(), ex.getCause()); + } - if (hostLoad == null) { - logger.warn("{}: ZoneMinderHostLoad dataset could not be obtained (received 'null')", - getLogIdentifier()); - } else if (hostLoad.getHttpResponseCode() != 200) { - logger.warn( - "BRIDGE [{}]: ZoneMinderHostLoad dataset could not be obtained (HTTP Response: Code='{}', Message='{}')", - getThingId(), hostLoad.getHttpResponseCode(), hostLoad.getHttpResponseMessage()); + if (hostLoad == null) { + logger.warn("{}: ZoneMinderHostLoad dataset could not be obtained (received 'null')", + getLogIdentifier()); + } else if (hostLoad.getHttpStatus() != HttpStatus.OK_200) { + logger.warn( + "{}: ZoneMinderHostLoad dataset could not be obtained (HTTP Response: Code='{}', Message='{}')", + getLogIdentifier(), hostLoad.getHttpStatus(), hostLoad.getHttpResponseMessage()); - } else { - channelCpuLoad = hostLoad.getCpuLoad().toString(); - } + } else { + channelCpuLoad = hostLoad.getCpuLoad().toString(); + } - if (fetchDiskUsage) { - IZoneMinderDiskUsage diskUsage = null; - try { - diskUsage = zoneMinderServerProxy.getHostDiskUsage(); - logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), - zoneMinderServerProxy.getHttpUrl(), zoneMinderServerProxy.getHttpResponseCode(), - zoneMinderServerProxy.getHttpResponseMessage()); - } catch (Exception ex) { - logger.error("{}: Exception thrown in call to ZoneMinderDiskUsage: ", getLogIdentifier(), ex); - } + if (curPriority.isPriorityActive(getBridgeConfig().getDiskUsageRefresh())) { + IZoneMinderDiskUsage diskUsage = null; + try { + diskUsage = zoneMinderServerProxy.getHostDiskUsage(); + logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), + diskUsage.getHttpRequestUrl(), diskUsage.getHttpStatus(), + diskUsage.getHttpResponseMessage()); + } catch (Exception ex) { + logger.error( + "{}: context='refreshThing' Exception thrown in call to ZoneMinderDiskUsage ('{}')", + getLogIdentifier(), ex.getMessage()); + } catch (ZoneMinderException ex) { + logger.error( + "{}: context='refreshThing' error in call to 'getHostDiskUsage' ExceptionClass='{}' - Message='{}'", + getLogIdentifier(), ex.getClass().getCanonicalName(), ex.getMessage(), + ex.getCause()); + } - if (diskUsage == null) { - logger.warn("{}: ZoneMinderDiskUsage dataset could not be obtained (received 'null')", - getLogIdentifier()); - } else if (hostLoad.getHttpResponseCode() != 200) { - logger.warn( - "{}: ZoneMinderDiskUsage dataset could not be obtained (HTTP Response: Code='{}', Message='{}')", - getLogIdentifier(), hostLoad.getHttpResponseCode(), hostLoad.getHttpResponseMessage()); + if (diskUsage == null) { + logger.warn( + "{}: context='refreshThing' ZoneMinderDiskUsage dataset could not be obtained (received 'null')", + getLogIdentifier()); + } else if (diskUsage.getHttpStatus() != HttpStatus.OK_200) { + logger.warn( + "{}: context='refreshThing' ZoneMinderDiskUsage dataset could not be obtained (HTTP Response: Code='{}', Message='{}')", + getLogIdentifier(), diskUsage.getHttpStatus(), diskUsage.getHttpResponseMessage()); - } else { - channelDiskUsage = diskUsage.getDiskUsage(); + } else { + channelDiskUsage = diskUsage.getDiskUsage(); + } + } } + } else { + // Make sure old data is cleared + channelCpuLoad = ""; + channelDiskUsage = ""; + } + } catch (Exception ex) { + logger.error("{}: context='refreshThing' tag='exception' Exception thrown when refreshing bridge='{}'", + getLogIdentifier(), getThing().getBridgeUID(), ex); + } finally { + if (zoneMinderServerProxy != null) { + releaseSession(); } - } else { - _online = false; - // Make sure old data is cleared - channelCpuLoad = ""; - channelDiskUsage = ""; } /* * Update all channels on Bridge */ for (Channel channel : channels) { - updateChannel(channel.getUID()); + + if (isLinked(channel.getUID().getId())) { + updateChannel(channel.getUID()); + } } /* @@ -479,26 +487,18 @@ protected synchronized void refreshThing(IZoneMinderSession session, boolean fet */ for (Thing thing : things) { try { - if (thing.getThingTypeUID().equals(ZoneMinderConstants.THING_TYPE_THING_ZONEMINDER_MONITOR)) { - Thing thingMonitor = thing; ZoneMinderBaseThingHandler thingHandler = (ZoneMinderBaseThingHandler) thing.getHandler(); - thingHandler.refreshThing(session, DataRefreshPriorityEnum.SCHEDULED); + if (thingHandler != null) { + thingHandler.refreshThing(curPriority); + } } - - } catch (NullPointerException ex) { - // This isn't critical (unless it comes over and over). There seems to be a bug so that a null - // pointer exception is coming every now and then. - // HAve to find the reason for that. Until thenm, don't Spamm - logger.debug("{}: Method 'refreshThing()' for Bridge {} failed for thing='{}' - Exception='{}'", - getLogIdentifier(), this.getZoneMinderId(), thing.getUID(), ex.getMessage()); - - // Other exceptions has to be shown as errors } catch (Exception ex) { - logger.error("{}: Method 'refreshThing()' for Bridge {} failed for thing='{}' - Exception='{}'", - getLogIdentifier(), this.getZoneMinderId(), thing.getUID(), ex.getMessage()); + logger.error("{}: context='refreshThing' tag='exception' Exception thrown when refreshing thing='{}'", + getLogIdentifier(), thing.getUID(), ex.getCause()); } + } } @@ -511,63 +511,50 @@ public synchronized Boolean isConnected() { } public boolean isOnline() { - return _online; - } - - private synchronized boolean getConnected() { - return this.connected; + ThingStatusInfo statusInfo = getThing().getStatusInfo(); + return (statusInfo.getStatus() == ThingStatus.ONLINE) ? true : false; } /** * Set connection status. * * @param connected + * @throws ZoneMinderUrlNotFoundException + * @throws IOException + * @throws GeneralSecurityException + * @throws IllegalArgumentException */ - private synchronized void setConnected(boolean connected) { - + private void setConnected(boolean connected) + throws IllegalArgumentException, GeneralSecurityException, IOException, ZoneMinderUrlNotFoundException { if (this.connected != connected) { - if (connected) { - try { - zoneMinderSession = ZoneMinderFactory.CreateSession(zoneMinderConnection); - } catch (FailedLoginException | IllegalArgumentException | IOException - | ZoneMinderUrlNotFoundException e) { - logger.error("BRIDGE [{}]: Call to setConencted failed with exception '{}'", getThingId(), - e.getMessage()); - } - } else { - zoneMinderSession = null; - } - this.connected = connected; - - } + try { + if (connected) { + try { + if (zoneMinderEventSession == null) { + zoneMinderEventSession = ZoneMinderFactory.CreateEventSession(zoneMinderConnection); + } - } + } catch (Exception ex) { + zoneMinderEventSession = null; + return; + } - /** - * Set channel 'bridge_connection'. - * - * @param connected - */ - private void setBridgeConnectionStatus(boolean connected) { - logger.debug(" {}: setBridgeConnection(): Set Bridge to {}", getLogIdentifier(), - connected ? ThingStatus.ONLINE : ThingStatus.OFFLINE); - - Bridge bridge = getBridge(); - if (bridge != null) { - ThingStatus status = bridge.getStatus(); - logger.debug("{}: Bridge ThingStatus is: {}", getLogIdentifier(), status); + } else { + if (zoneMinderEventSession != null) { + zoneMinderEventSession.unsubscribeAllMonitorEvents(); + } + zoneMinderConnection = null; + zoneMinderEventSession = null; + } + } finally { + this.connected = connected; + if (connected) { + onConnected(); + } else { + onDisconnected(); + } + } } - - setConnected(connected); - } - - /** - * Set channel 'bridge_connection'. - * - * @param connected - */ - private boolean getBridgeConnectionStatus() { - return getConnected(); } /** @@ -578,10 +565,10 @@ private boolean getBridgeConnectionStatus() { * @throws GeneralSecurityException * @throws IllegalArgumentException */ - public void onConnected() { - logger.debug("BRIDGE [{}]: onConnected(): Bridge Connected!", getThingId()); - setConnected(true); - onBridgeConnected(this, zoneMinderConnection); + public void onConnected() + throws IllegalArgumentException, GeneralSecurityException, IOException, ZoneMinderUrlNotFoundException { + logger.debug("{}: [{}]: onConnected(): Bridge Connected!", getLogIdentifier(), getThingId()); + onBridgeConnected(this, getZoneMinderConnection()); // Inform thing handlers of connection List things = getThing().getThings(); @@ -591,23 +578,24 @@ public void onConnected() { if (thingHandler != null) { try { - thingHandler.onBridgeConnected(this, zoneMinderConnection); - } catch (IllegalArgumentException | GeneralSecurityException | IOException - | ZoneMinderUrlNotFoundException e) { - logger.error("{}: onConnected() failed - Exceprion: {}", getLogIdentifier(), e.getMessage()); + thingHandler.onBridgeConnected(this, getZoneMinderConnection()); + } catch (IllegalArgumentException e) { + logger.error("{}: context='onConnected' failed - Exceprion: {}", getLogIdentifier(), + e.getMessage()); } - logger.debug("{}: onConnected(): Bridge - {}, Thing - {}, Thing Handler - {}", getLogIdentifier(), - thing.getBridgeUID(), thing.getUID(), thingHandler); + logger.debug("{}: context='onConnected': Bridge - {}, Thing - {}, Thing Handler - {}", + getLogIdentifier(), thing.getBridgeUID(), thing.getUID(), thingHandler); } } + } /** * Runs when disconnected. */ private void onDisconnected() { - logger.debug("{}: onDisconnected(): Bridge Disconnected!", getLogIdentifier()); - setConnected(false); + logger.debug("{}: context='onDisconnected': Bridge Disconnected!", getLogIdentifier()); + onBridgeDisconnected(this); // Inform thing handlers of disconnection @@ -618,276 +606,692 @@ private void onDisconnected() { if (thingHandler != null) { thingHandler.onBridgeDisconnected(this); - logger.debug("{}: onDisconnected(): Bridge - {}, Thing - {}, Thing Handler - {}", getLogIdentifier(), - thing.getBridgeUID(), thing.getUID(), thingHandler); + logger.debug("{}: context='onDisconnected': Bridge - {}, Thing - {}, Thing Handler - {}", + getLogIdentifier(), thing.getBridgeUID(), thing.getUID(), thingHandler); } } } - @Override - public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { + private int initRetriesCount = 0; + private static int initMaxRevoverableRetries = 5; + private static int initMaxUnrecoverableRetries = 5; + private ZoneMinderConnectionStatus verifyBindingConfiguration(ThingStatus currentStatus) { + String context = "verifyBindingConfiguration"; ThingStatus newStatus = ThingStatus.OFFLINE; ThingStatusDetail statusDetail = ThingStatusDetail.NONE; String statusDescription = ""; - boolean _isOnline = false; + ZoneMinderConnectionStatus status = ZoneMinderConnectionStatus.BINDING_CONFIG_INVALID; + ZoneMinderBridgeServerConfig config = null; + try { + // Is it a retry loop? (Is this step already verified?) + // Or is there an unrecoverable error? + if ((getLastSucceededStatus().greatherThanEqual(ZoneMinderConnectionStatus.BINDING_CONFIG_LOAD_PASSED)) + || (getLastSucceededStatus().hasUnrecoverableError())) { + return getLastSucceededStatus(); + } - ThingStatus prevStatus = getThing().getStatus(); + // get Bridge Config + config = getBridgeConfig(); + + // Check if server Bridge configuration is valid + if (config == null) { + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "Configuration not found"; + updateStatus(newStatus, statusDetail, statusDescription); + + logger.error("{}: context='{}' state='{}' check='FAILED' - {}", getLogIdentifier(), context, + newStatus.toString(), statusDescription); + + return status; + + } else if (config.getHost() == null) { + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "Host not configured"; + + updateStatus(newStatus, statusDetail, statusDescription); + + logger.error("{}: context='{}' state='{}' check='FAILED' - {}", getLogIdentifier(), context, + newStatus.toString(), statusDescription); + + return status; + } else if (config.getProtocol() == null) { + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "Unknown protocol in configuration"; + + updateStatus(newStatus, statusDetail, statusDescription); + + logger.error("{}: context='{}' state='{}' check='FAILED' - {}", getLogIdentifier(), context, + newStatus.toString(), statusDescription); + return status; + } + + else if (config.getHttpPort() == null) { + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "HTTP port invalid in configuration"; + + updateStatus(newStatus, statusDetail, statusDescription); + + logger.error("{}: context='{}' state='{}' check='FAILED' - {}", getLogIdentifier(), context, + newStatus.toString(), statusDescription); + return status; + + } + + else if (config.getTelnetPort() == null) { + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "Telnet port invalid in configuration"; + updateStatus(newStatus, statusDetail, statusDescription); + logger.error("{}: context='{}' state='{}' check='FAILED' - {}", getLogIdentifier(), context, + newStatus.toString(), statusDescription); + return status; + + } + + // Configuration verified + status = ZoneMinderConnectionStatus.BINDING_CONFIG_LOAD_PASSED; + + logger.debug("{}: context='{}' state='{}' check='PASSED'", getLogIdentifier(), context, + newStatus.toString()); + + } catch (Exception ex) { + logger.error("{}: context='{}' state='{}' check='FAILED' tag='exception'", getLogIdentifier(), context, + newStatus.toString(), ex); + + } + return status; + } + + private ZoneMinderConnectionStatus validateConfig(ZoneMinderBridgeServerConfig config) { + String context = "validateConfig"; + ThingStatus newStatus = ThingStatus.OFFLINE; + ThingStatusDetail statusDetail = ThingStatusDetail.NONE; + String statusDescription = ""; + + ZoneMinderConnectionStatus status = ZoneMinderConnectionStatus.GENERAL_ERROR; + + // Is it a retry loop? (Is this step already verified?) + // Or is there an unrecoverable error? + if ((getLastSucceededStatus().greatherThanEqual(ZoneMinderConnectionStatus.BINDING_CONFIG_VALIDATE_PASSED)) + || (getLastSucceededStatus().hasUnrecoverableError())) { + return getLastSucceededStatus(); + } + + // Check if something is responding give host and port HTTP try { - // Just perform a health check to see if we are still connected - if (prevStatus == ThingStatus.ONLINE) { - if (zoneMinderSession == null) { - newStatus = ThingStatus.ONLINE; - statusDetail = ThingStatusDetail.NONE; - statusDescription = ""; - updateBridgeStatus(newStatus, statusDetail, statusDescription); - return; - } else if (!zoneMinderSession.isConnected()) { - newStatus = ThingStatus.OFFLINE; - statusDetail = ThingStatusDetail.COMMUNICATION_ERROR; - statusDescription = "Session lost connection to ZoneMinder Server"; - updateBridgeStatus(newStatus, statusDetail, statusDescription); + // Check Socket + Socket socket = new Socket(); + socket.connect(new InetSocketAddress(config.getHost(), config.getHttpPort()), 5000); + if (socket.isConnected()) { + socket.close(); + } - return; - } + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.NONE; + statusDescription = "Connecting to ZoneMinder Server"; + updateStatus(newStatus, statusDetail, statusDescription); - IZoneMinderServer serverProxy = ZoneMinderFactory.getServerProxy(zoneMinderSession); - IZoneMinderDaemonStatus daemonStatus = serverProxy.getHostDaemonCheckState(); + status = ZoneMinderConnectionStatus.BINDING_CONFIG_VALIDATE_PASSED; + logger.debug("{}: context='{}' previousState='OFFLINE' Socket connection to ZM Website (PASSED)", + getLogIdentifier(), context); - // If service isn't running OR we revceived a http responsecode other than 200, assume we are offline - if ((!daemonStatus.getStatus()) || (daemonStatus.getHttpResponseCode() != 200)) { - newStatus = ThingStatus.OFFLINE; - statusDetail = ThingStatusDetail.COMMUNICATION_ERROR; - statusDescription = "ZoneMinder Server Daemon not running"; + } catch (UnknownHostException e) { + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "Unknown host - Check configuration"; + status = ZoneMinderConnectionStatus.BINDING_CONNECTION_INVALID; + logger.warn( + "{}: context='{}' tag='exception' previousState='OFFLINE' UnknowHostException when connecting to ZoneMinder Server.", + getLogIdentifier(), context, e); + } catch (IOException e) { + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "Could not contact host - Check configuration"; + updateStatus(newStatus, statusDetail, statusDescription); + + status = ZoneMinderConnectionStatus.BINDING_CONNECTION_TIMEOUT; + logger.warn( + "{}: context='validateConfig' tag='exception' previousState='OFFLINE' Socket connection Timeout.", + getLogIdentifier(), e); + } + return status; + } - logger.debug("{}: {} (state='{}' and ResponseCode='{}')", getLogIdentifier(), statusDescription, - daemonStatus.getStatus(), daemonStatus.getHttpResponseCode()); - updateBridgeStatus(newStatus, statusDetail, statusDescription); - return; + private ZoneMinderConnectionStatus getLastSucceededStatus() { + return lastSucceededStatus; + } + + private ZoneMinderConnectionStatus getConnectionStatus() { + return zmConnectStatus; + } + + private void setConnectionStatus(ZoneMinderConnectionStatus newStatus) { + zmConnectStatus = newStatus; + if (!newStatus.isErrorState()) { + lastSucceededStatus = newStatus; + } + } + + private ZoneMinderConnectionStatus validateConnection(ZoneMinderBridgeServerConfig config) { + String context = "validateConnection"; + ThingStatus newStatus = ThingStatus.OFFLINE; + ThingStatusDetail statusDetail = ThingStatusDetail.NONE; + String statusDescription = ""; + + ZoneMinderConnectionStatus status = ZoneMinderConnectionStatus.BINDING_CONNECTION_INVALID; + + // Is it a retry loop? (Is this step already verified?) + // Or is there an unrecoverable error? + if ((getLastSucceededStatus().greatherThanEqual(ZoneMinderConnectionStatus.ZONEMINDER_CONNECTION_CREATED)) + || (getLastSucceededStatus().hasUnrecoverableError())) { + return getLastSucceededStatus(); + } + + try { + aquireSession(); + + if (getZoneMinderConnection() == null) { + zoneMinderConnection = ZoneMinderFactory.CreateConnection(config.getProtocol(), config.getHost(), + config.getHttpPort(), config.getTelnetPort(), config.getUserName(), config.getPassword(), + config.getStreamingUser(), config.getStreamingPassword(), config.getServerBasePath(), + config.getServerApiPath(), HTTP_TIMEOUT); + + logger.debug( + "{}: context='{}' - ZoneMinderFactory.CreateConnection() called (Protocol='{}', Host='{}', HttpPort='{}', SocketPort='{}', Path='{}', API='{}')", + getLogIdentifier(), context, config.getProtocol(), config.getHost(), config.getHttpPort(), + config.getTelnetPort(), config.getServerBasePath(), config.getServerApiPath()); + + } + + } catch (ZoneMinderAuthenticationException authenticationException) { + String detailedMessage = ""; + setConnectionStatus(ZoneMinderConnectionStatus.BINDING_CONFIG_INVALID); + + if (authenticationException.getStackTrace() != null) { + if (authenticationException.getStackTrace().length > 0) { + StackTraceElement ste = authenticationException.getStackTrace()[0]; + detailedMessage = String.format(" StackTrace='%s'", ste.toString()); } - // TODO:: Check other things without being harsh???? + } + logger.error( + "{}: context='{}' check='FAILED' - Failed to login to ZoneMinder Server. Check provided usercredentials (Exception='{}', {})", + getLogIdentifier(), context, authenticationException.getMessage(), detailedMessage, + authenticationException); + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "Login to ZoneMinder Server failed. Check provided usercredentials"; + updateStatus(newStatus, statusDetail, statusDescription); + status = ZoneMinderConnectionStatus.SERVER_CREDENTIALS_INVALID; + return status; + + } catch (ZoneMinderApiNotEnabledException e) { + setConnectionStatus(ZoneMinderConnectionStatus.SERVER_API_DISABLED); + logger.error( + "{}: context='{}' check='FAILED' - ZoneMinder Server API is not enabled. Enable option in ZoneMinder Server Settings and restart openHAB Binding.", + getLogIdentifier(), context, e); + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "ZoneMinder Server 'OPT_USE_API' not enabled"; + updateStatus(newStatus, statusDetail, statusDescription); + status = ZoneMinderConnectionStatus.SERVER_API_DISABLED; + return status; + + } catch (ZoneMinderException | Exception e) { + logger.error( + "{}: context='{}' check='FAILED' - General error when creating ConnectionInfo. Retrying next cycle...", + getLogIdentifier(), context, e); + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "ZoneMinder Server Connection error"; + updateStatus(newStatus, statusDetail, statusDescription); + status = ZoneMinderConnectionStatus.GENERAL_ERROR; + return status; - newStatus = ThingStatus.ONLINE; + } finally { + releaseSession(); + } + + // Check that we have a connection + if (getZoneMinderConnection() != null) { + logger.debug("{}: context='{}' previousState='OFFLINE' ZoneMinder Connection check (PASSED)", + getLogIdentifier(), context); + status = ZoneMinderConnectionStatus.ZONEMINDER_CONNECTION_CREATED; + } else { + zoneMinderConnection = null; + status = ZoneMinderConnectionStatus.BINDING_CONNECTION_INVALID; + logger.warn("{}: context='{}' check='FAILED' - Failed to obtain ZoneMinder Connection. Retrying next cycle", + getLogIdentifier(), context); + } + + return status; + } + + public ZoneMinderConnectionStatus validateZoneMinderServerConfig() { + IZoneMinderConnectionHandler curSession = null; + String context = "validateZoneMinderServerConfig"; + ThingStatus newStatus = ThingStatus.OFFLINE; + ThingStatusDetail statusDetail = ThingStatusDetail.NONE; + String statusDescription = ""; + ZoneMinderConnectionStatus status = ZoneMinderConnectionStatus.GENERAL_ERROR; + + IZoneMinderServer serverProxy = null; + try { + curSession = aquireSession(); + if (curSession == null) { + logger.error( + "{}: context='{}' check='FAILED' - Could not verify ZoneMinder Server Config. Session failed to connect.", + getLogIdentifier(), context); + + status = ZoneMinderConnectionStatus.GENERAL_ERROR; + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "Session not available"; + updateStatus(newStatus, statusDetail, statusDescription); + return status; + } + + serverProxy = ZoneMinderFactory.getServerProxy(curSession); + + IZoneMinderDaemonStatus daemonStatus = serverProxy.getHostDaemonCheckState(); + + // Check if server API can be accessed + if (!daemonStatus.getStatus()) { + logger.error("{}: context='{}' check='FAILED' - Bridge OFFLINE because server Daemon is not running", + getLogIdentifier(), context); + status = ZoneMinderConnectionStatus.SERVER_DAEMON_NOT_RUNNING; + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "ZoneMinder Server Daemon not running"; + updateStatus(newStatus, statusDetail, statusDescription); + + } + + // Verify that 'OPT_TRIGGER' is set to true in ZoneMinder + else if (!serverProxy.isTriggerOptionEnabled()) { + logger.error( + "{}: context='{}' check='FAILED' - Bridge OFFLINE because ZoneMinder Server option 'OPT_TRIGGERS' not enabled", + getLogIdentifier(), context); + status = ZoneMinderConnectionStatus.SERVER_OPT_TRIGGERS_DISABLED; + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "ZoneMinder Server option 'OPT_TRIGGERS' not enabled"; + updateStatus(newStatus, statusDetail, statusDescription); + + } else { + // Seems like everything is as we want it :-) + logger.debug("{}: context='{}' check='PASSED' - ZoneMinder ", getLogIdentifier(), context); + status = ZoneMinderConnectionStatus.ZONEMINDER_SERVER_CONFIG_PASSED; + + } + + } catch (ZoneMinderException | Exception ex) { + if (ex.getMessage() != null) { + logger.error( + "{}: context='{}' check='FAILED' - ZoneMinder Server configuration failed to verify. (Exception='{}')", + getLogIdentifier(), context, ex.getMessage(), ex.getCause()); + } else { + logger.error("{}: context='{}' check='FAILED' - ZoneMinder Server configuration failed to verify.", + getLogIdentifier(), context, ex.getCause()); + } + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "ZoneMinder Server configuration not verified "; + updateStatus(newStatus, statusDetail, statusDescription); + + } finally { + releaseSession(); + } + return status; + } + + private void initializeAvaliabilityStatus(IZoneMinderConnectionHandler conn) { + String context = "initializeAvaliabilityStatus"; + ZoneMinderBridgeServerConfig config = null; + + ThingStatus newStatus = ThingStatus.OFFLINE; + ThingStatusDetail statusDetail = ThingStatusDetail.NONE; + String statusDescription = ""; + ThingStatus currentStatus = getThing().getStatus(); + + // Only continue if handler is initialized and status is OFFLINE + if ((currentStatus != ThingStatus.OFFLINE) || (!initialized)) { + return; + } + + /******************************** + * + * Retry handling + * + *******************************/ + // An unrecoverable error in the Binding Configuration was found. OR a recoverable has failed and is no + // unrecoverable (try again later) + if (getConnectionStatus().hasUnrecoverableError() || (initRetriesCount > initMaxRevoverableRetries)) { + initRetriesCount++; + + // Reset unrecoverable error and try from beginning + if (initRetriesCount > initMaxRevoverableRetries + initMaxUnrecoverableRetries) { + initRetriesCount = 0; + zoneMinderConnection = null; + zoneMinderEventSession = null; + setConnectionStatus(ZoneMinderConnectionStatus.UNINITIALIZED); + + // Clear old error information + newStatus = ThingStatus.OFFLINE; statusDetail = ThingStatusDetail.NONE; - statusDescription = ""; + statusDescription = "Retrying to connect"; + updateStatus(newStatus, statusDetail, statusDescription); + + logger.debug("{}: context='{}' state='{}' - Retrying initialization after unrecoverable error", + getLogIdentifier(), context, newStatus.toString()); + } - // If we are OFFLINE, check everything - else if (prevStatus == ThingStatus.OFFLINE) { + return; - // Just wait until we are finished initializing - if (isInitialized == false) { - _online = _isOnline; - return; - } + } else if (getConnectionStatus().hasRecoverableError()) { + initRetriesCount++; + logger.debug("{}: context='{}' state='{}' - Retrying initialization (Last Error='{}', Retries='{}')", + getLogIdentifier(), context, newStatus.toString(), getConnectionStatus().toString(), + initRetriesCount); - ZoneMinderBridgeServerConfig config = getBridgeConfig(); + } - // Check if server Bridge configuration is valid - if (config == null) { - newStatus = ThingStatus.OFFLINE; - statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; - statusDescription = "Configuration not found"; - updateBridgeStatus(newStatus, statusDetail, statusDescription); - return; + /*********************************** + * + * Verify Binding Configuration + * + ***********************************/ + setConnectionStatus(verifyBindingConfiguration(currentStatus)); + if (!getConnectionStatus().hasPassed(ZoneMinderConnectionStatus.BINDING_CONFIG_LOAD_PASSED)) { + return; + } - } else if (config.getHostName() == null) { - newStatus = ThingStatus.OFFLINE; - statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; - statusDescription = "Host not found in configuration"; - updateBridgeStatus(newStatus, statusDetail, statusDescription); - return; - } else if (config.getProtocol() == null) { - newStatus = ThingStatus.OFFLINE; - statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; - statusDescription = "Unknown protocol in configuration"; - updateBridgeStatus(newStatus, statusDetail, statusDescription); - return; - } + // Great the Bridge Config is valid get started + config = getBridgeConfig(); - else if (config.getHttpPort() == null) { - newStatus = ThingStatus.OFFLINE; - statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; - statusDescription = "Invalid HTTP port"; - updateBridgeStatus(newStatus, statusDetail, statusDescription); - return; - } + /*********************************** + * + * Validate Binding Configuration + * + ***********************************/ + setConnectionStatus(validateConfig(config)); - else if (config.getTelnetPort() == null) { - newStatus = ThingStatus.OFFLINE; - statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; - statusDescription = "Invalid telnet port"; - updateBridgeStatus(newStatus, statusDetail, statusDescription); - return; - } else if (!ZoneMinderFactory.isZoneMinderUrl(connection)) { - newStatus = ThingStatus.OFFLINE; - statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; - statusDescription = "URL not a ZoneMinder Server"; - updateBridgeStatus(newStatus, statusDetail, statusDescription); - return; - } + // Check that Status corresponds actual state + if (!getConnectionStatus().hasPassed(ZoneMinderConnectionStatus.BINDING_CONFIG_VALIDATE_PASSED)) { + return; + } - if (!isZoneMinderLoginValid(connection)) { - newStatus = ThingStatus.OFFLINE; - statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; - statusDescription = "Cannot access ZoneMinder Server. Check provided usercredentials"; - updateBridgeStatus(newStatus, statusDetail, statusDescription); - return; - } + /*********************************** + * + * VALIDATE ZONEMINDER CONNECTION (API + AUTHENTICATION) + * + ***********************************/ + setConnectionStatus(validateConnection(config)); - /* - * Now we will try to establish a session - */ + // A previous step failed. + if (!getConnectionStatus().hasPassed(ZoneMinderConnectionStatus.ZONEMINDER_CONNECTION_CREATED)) { + return; + } + + /*********************************** + * + * VALIDATE ZONEMINDER HTTP Session + * + ***********************************/ + zmConnectStatus = ZoneMinderConnectionStatus.ZONEMINDER_SESSION_CREATED; + // A previous step failed. + if (!getConnectionStatus().hasPassed(ZoneMinderConnectionStatus.ZONEMINDER_SESSION_CREATED)) { + return; + } + + /*********************************** + * + * VALIDATE ZONEMINDER HTTP Session + * + ***********************************/ + setConnectionStatus(validateZoneMinderServerConfig()); + if (!getConnectionStatus().hasPassed(ZoneMinderConnectionStatus.ZONEMINDER_SERVER_CONFIG_PASSED)) { + return; + } + + /*********************************** + * + * Everything looks fine -> GO ONLINE + * + ***********************************/ - IZoneMinderSession curSession = null; + zmConnectStatus = ZoneMinderConnectionStatus.INITIALIZED; + // _zoneMinderSession = curSession; + newStatus = ThingStatus.ONLINE; + statusDetail = ThingStatusDetail.NONE; + statusDescription = ""; + + updateBridgeStatus(newStatus, statusDetail, statusDescription, true); + logger.debug("{}: context='{}' Successfully established session to ZoneMinder Server.", getLogIdentifier(), + context); + + } + + @Override + public void updateAvaliabilityStatus(IZoneMinderConnectionHandler conn) { + String context = "updateAvaliabilityStatus"; + IZoneMinderConnectionHandler curSession = null; + ThingStatus newStatus = ThingStatus.OFFLINE; + ThingStatusDetail statusDetail = ThingStatusDetail.NONE; + String statusDescription = ""; + + ThingStatus prevStatus = getThing().getStatus(); + try { + // Just perform a health check to see if we are still connected + if (prevStatus == ThingStatus.ONLINE) { try { - curSession = ZoneMinderFactory.CreateSession(connection); - } catch (FailedLoginException | IllegalArgumentException | IOException - | ZoneMinderUrlNotFoundException ex) { - logger.error("{}: Create Session failed with exception {}", getLogIdentifier(), ex.getMessage()); - - newStatus = ThingStatus.OFFLINE; - statusDetail = ThingStatusDetail.COMMUNICATION_ERROR; - statusDescription = "Failed to connect. (Check Log)"; - if (curBridgeStatus != ThingStatus.OFFLINE) { - logger.error("{}: Bridge OFFLINE because of '{}' Exception='{}'", getLogIdentifier(), - statusDescription, ex.getMessage()); + curSession = aquireSession(); + + if (curSession == null) { + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.COMMUNICATION_ERROR; + statusDescription = "Session lost connection to ZoneMinder Server"; + updateBridgeStatus(newStatus, statusDetail, statusDescription, false); + return; + } else if (!curSession.isConnected()) { + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.COMMUNICATION_ERROR; + statusDescription = "Session lost connection to ZoneMinder Server"; + updateBridgeStatus(newStatus, statusDetail, statusDescription, false); + return; } - updateBridgeStatus(newStatus, statusDetail, statusDescription); - return; - } - IZoneMinderServer serverProxy = ZoneMinderFactory.getServerProxy(curSession); - - // Check if server API can be accessed - if (!serverProxy.isApiEnabled()) { - newStatus = ThingStatus.OFFLINE; - statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; - statusDescription = "ZoneMinder Server 'OPT_USE_API' not enabled"; - updateBridgeStatus(newStatus, statusDetail, statusDescription); - return; - } else if (!serverProxy.getHostDaemonCheckState().getStatus()) { - newStatus = ThingStatus.OFFLINE; - statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; - statusDescription = "ZoneMinder Server Daemon not running"; - updateBridgeStatus(newStatus, statusDetail, statusDescription); - return; + else if (!curSession.isAuthenticated()) { + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.COMMUNICATION_ERROR; + statusDescription = "Not authenticated"; + updateBridgeStatus(newStatus, statusDetail, statusDescription, false); + return; + } + + IZoneMinderServer serverProxy = ZoneMinderFactory.getServerProxy(curSession); + IZoneMinderHostVersion hostVersion = null; + try { + hostVersion = serverProxy.getHostVersion(); + } catch (ZoneMinderException ex) { + hostVersion = null; + } + + if ((hostVersion == null) || (hostVersion.getHttpStatus() >= 400)) { + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.COMMUNICATION_ERROR; + statusDescription = "Connection to ZoneMinder Server was lost"; + updateBridgeStatus(newStatus, statusDetail, statusDescription, false); + + logger.error("{}: Lost connection to ZoneMinder server.", getLogIdentifier()); + + setConnected(false); + } + + // Check if ZoneMinder Server Daemon is running + if (!serverProxy.isDaemonRunning()) { + logger.error("{}: context='{}' Bridge OFFLINE because ZoneMinder Server Daemon stopped", + getLogIdentifier(), context); + zmConnectStatus = ZoneMinderConnectionStatus.SERVER_DAEMON_NOT_RUNNING; + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "ZoneMinder Server Daemon stopped"; + updateBridgeStatus(newStatus, statusDetail, statusDescription, false); + // setConnected(false); + return; + } + // Verify that 'OPT_TRIGGER' is set to true in ZoneMinder + else if (!serverProxy.isTriggerOptionEnabled()) { + logger.error( + "{}: context='{}' Bridge OFFLINE because ZoneMinder Server OPT_TRIGGER was disabled", + getLogIdentifier(), context); + zmConnectStatus = ZoneMinderConnectionStatus.SERVER_OPT_TRIGGERS_DISABLED; + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "Option external triggers 'OPT_TRIGGERS' was disabled"; + updateBridgeStatus(newStatus, statusDetail, statusDescription, false); + return; + } + // Check if ZoneMinder Server API can be accessed + else if (!serverProxy.isApiEnabled()) { + logger.error("{}: context='{}' Bridge OFFLINE because ZoneMinder Server API was disabled", + getLogIdentifier(), context); + zmConnectStatus = ZoneMinderConnectionStatus.SERVER_API_DISABLED; + newStatus = ThingStatus.OFFLINE; + statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; + statusDescription = "ZoneMinder Server API was disabled"; + updateBridgeStatus(newStatus, statusDetail, statusDescription, false); + return; + } + + } finally { + releaseSession(); } - // Verify that 'OPT_TRIGGER' is set to true in ZoneMinder - else if (!serverProxy.isTriggerOptionEnabled()) { - newStatus = ThingStatus.OFFLINE; - statusDetail = ThingStatusDetail.CONFIGURATION_ERROR; - statusDescription = "ZoneMinder Server option 'OPT_TRIGGERS' not enabled"; - updateBridgeStatus(newStatus, statusDetail, statusDescription); - return; - } else { - // Seems like everything is as we want it :-) - _isOnline = true; + + newStatus = ThingStatus.ONLINE; + statusDetail = ThingStatusDetail.NONE; + statusDescription = ""; + updateBridgeStatus(newStatus, statusDetail, statusDescription, true); + + // Ask all child things to update their Availability Status + for (Thing thing : getThing().getThings()) { + ZoneMinderBaseThingHandler thingHandler = (ZoneMinderBaseThingHandler) thing.getHandler(); + if (thingHandler instanceof ZoneMinderThingMonitorHandler) { + try { + thingHandler.updateAvaliabilityStatus(getZoneMinderConnection()); + } catch (Exception ex) { + logger.debug("{}: Failed to call 'updateAvailabilityStatus()' for '{}'", getLogIdentifier(), + thingHandler.getThing().getUID()); + } + } } - if (_isOnline == true) { - zoneMinderSession = curSession; - _online = _isOnline; + } else if (prevStatus == ThingStatus.OFFLINE) { + initializeAvaliabilityStatus(conn); + + if (getConnectionStatus() == ZoneMinderConnectionStatus.INITIALIZED) { newStatus = ThingStatus.ONLINE; statusDetail = ThingStatusDetail.NONE; statusDescription = ""; - - } else { - zoneMinderSession = null; - _online = _isOnline; - newStatus = ThingStatus.OFFLINE; + updateBridgeStatus(newStatus, statusDetail, statusDescription, true); } } - } catch (Exception ex) { + // Hmmm We shouldn't really end here. + logger.error( + "{}: context='updateAvailablilityStatus' Exception occurred in updateAvailabilityStatus Exception='{}'", + getLogIdentifier(), ex.getMessage(), ex.getCause()); + zmConnectStatus = ZoneMinderConnectionStatus.GENERAL_ERROR; newStatus = ThingStatus.OFFLINE; statusDetail = ThingStatusDetail.COMMUNICATION_ERROR; - logger.error("{}: Exception occurred in updateAvailabilityStatus Exception='{}'", getLogIdentifier(), - ex.getMessage()); - statusDescription = "Error occurred (Check log)"; - - } - updateBridgeStatus(newStatus, statusDetail, statusDescription); - - // Ask child things to update their Availability Status - for (Thing thing : getThing().getThings()) { - ZoneMinderBaseThingHandler thingHandler = (ZoneMinderBaseThingHandler) thing.getHandler(); - if (thingHandler instanceof ZoneMinderThingMonitorHandler) { - try { - thingHandler.updateAvaliabilityStatus(connection); - } catch (Exception ex) { - logger.debug("{}: Failed to call 'updateAvailabilityStatus()' for '{}'", getLogIdentifier(), - thingHandler.getThing().getUID()); - } - } - + statusDescription = "General error occurred (Check log)"; + updateBridgeStatus(newStatus, statusDetail, statusDescription, true); } } - protected void updateBridgeStatus(ThingStatus newStatus, ThingStatusDetail statusDetail, String statusDescription) { + protected void updateBridgeStatus(ThingStatus newStatus, ThingStatusDetail statusDetail, String statusDescription, + boolean updateConnection) { ThingStatusInfo curStatusInfo = thing.getStatusInfo(); String curDescription = StringUtils.isBlank(curStatusInfo.getDescription()) ? "" : curStatusInfo.getDescription(); // Status changed if ((curStatusInfo.getStatus() != newStatus) || (curStatusInfo.getStatusDetail() != statusDetail) - || (curDescription != statusDescription)) { - - // if (thing.getStatus() != newStatus) { - logger.info("{}: Bridge status changed from '{}' to '{}'", getLogIdentifier(), thing.getStatus(), - newStatus); - - if ((newStatus == ThingStatus.ONLINE) && (curStatusInfo.getStatus() != ThingStatus.ONLINE)) { - try { - setBridgeConnectionStatus(true); - onConnected(); - } catch (IllegalArgumentException e) { - // Just ignore that here - } - } else if ((newStatus == ThingStatus.OFFLINE) && (curStatusInfo.getStatus() != ThingStatus.OFFLINE)) { - try { - setBridgeConnectionStatus(false); - onDisconnected(); - } catch (IllegalArgumentException e) { - // Just ignore that here - } - + || (!curDescription.equals(statusDescription))) { + if (!curStatusInfo.getStatus().equals(newStatus)) { + logger.info("{}: context='updateBridgeStatus' Bridge status changed from '{}' to '{}'", + getLogIdentifier(), thing.getStatus(), newStatus); } + // Update Status correspondingly if ((newStatus == ThingStatus.OFFLINE) && (statusDetail != ThingStatusDetail.NONE)) { updateStatus(newStatus, statusDetail, statusDescription); + + forcedPriority = RefreshPriority.UNKNOWN; + if (updateConnection) { + try { + setConnected(false); + } catch (IllegalArgumentException | GeneralSecurityException | IOException + | ZoneMinderUrlNotFoundException e) { + logger.error( + "{}: context='updateBridgeStatus' Exception occurred when changing connected status", + getLogIdentifier(), e); + } + } } else { updateStatus(newStatus); + forcedPriority = RefreshPriority.PRIORITY_BATCH; + if (updateConnection) { + try { + setConnected(true); + } catch (IllegalArgumentException | GeneralSecurityException | IOException + | ZoneMinderUrlNotFoundException e) { + logger.error( + "{}: context='updateBridgeStatus' Exception occurred when changing connected status", + getLogIdentifier(), e); + } + } } - curBridgeStatus = newStatus; - } - } - - protected boolean isZoneMinderLoginValid(IZoneMinderConnectionInfo connection) { - try { - return ZoneMinderFactory.validateLogin(connection); - } catch (Exception e) { - return false; + // Ask all child things to update their Availability Status, since Bridge has changed + for (Thing thing : getThing().getThings()) { + ZoneMinderBaseThingHandler thingHandler = (ZoneMinderBaseThingHandler) thing.getHandler(); + if (thingHandler instanceof ZoneMinderThingMonitorHandler) { + try { + thingHandler.updateAvaliabilityStatus(getZoneMinderConnection()); + } catch (Exception ex) { + logger.debug( + "{}: context='updateBridgeStatus' Failed to call 'updateAvailabilityStatus' for '{}' (Exception='{}')", + getLogIdentifier(), thingHandler.getThing().getUID(), ex.getMessage()); + } + } + } } - } @Override public void updateChannel(ChannelUID channel) { State state = null; try { - switch (channel.getId()) { case ZoneMinderConstants.CHANNEL_ONLINE: updateState(channel, (isOnline() ? OnOffType.ON : OnOffType.OFF)); break; case ZoneMinderConstants.CHANNEL_SERVER_DISKUSAGE: - state = getServerDiskUsageState(); + if (getBridgeConfig().getDiskUsageRefresh() != RefreshPriority.DISABLED) { + state = getServerDiskUsageState(); + } else { + state = UnDefType.UNDEF; + } break; case ZoneMinderConstants.CHANNEL_SERVER_CPULOAD: @@ -901,58 +1305,91 @@ public void updateChannel(ChannelUID channel) { } if (state != null) { - logger.debug("{}: BridgeHandler.updateChannel(): Updating channel '{}' to state='{}'", - getLogIdentifier(), channel.getId(), state.toString()); updateState(channel.getId(), state); } } catch (Exception ex) { - - logger.error("{}: Error when 'updateChannel()' was called for thing='{}' (Exception='{}'", + logger.error( + "{}: context='updateChannel' Error when 'updateChannel()' was called for thing='{}' (Exception='{}'", getLogIdentifier(), channel.getId(), ex.getMessage()); + } + } + + public void subscribeMonitorEvents(ZoneMinderThingMonitorHandler monitorHandler) { + try { + if (zoneMinderEventSession != null) { + logger.info("{}: context='SubscribeMonitorEvents' thing='monitor' id='{}'", getLogIdentifier(), + monitorHandler.getZoneMinderId()); + + zoneMinderEventSession.subscribeMonitorEvents(zoneMinderConnection, monitorHandler.getZoneMinderId(), + monitorHandler); + } else { + logger.warn( + "{}: context='SubscribeMonitorEvents' thing='monitor' id='{}' - Could not subscribe to monitor events, because EventSession not initialisaed", + getLogIdentifier(), monitorHandler.getZoneMinderId()); + } + } catch (IllegalArgumentException | GeneralSecurityException | IOException | ZoneMinderUrlNotFoundException e) { + logger.error( + "{}: context='SubscribeMonitorEvents' - Exception occurred when subscribing for MonitorEvents. Exception='{}'", + getLogIdentifier(), e.getMessage()); } } - protected boolean openConnection() { - boolean connected = false; - if (isConnected() == false) { - logger.debug("{}: Connecting Bridge to ZoneMinder Server", getLogIdentifier()); + public void unsubscribeMonitorEvents(ZoneMinderThingMonitorHandler monitorHandler) { + try { + if (zoneMinderEventSession != null) { + zoneMinderEventSession.unsubscribeMonitorEvents(monitorHandler.getZoneMinderId(), monitorHandler); - try { - if (isConnected()) { - closeConnection(); - } - setConnected(connected); + logger.info("{}: context='UnsubscribeMonitorEvents' thing='monitor' id='{}'", getLogIdentifier(), + monitorHandler.getZoneMinderId()); - logger.info("{}: Connecting to ZoneMinder Server (result='{}'", getLogIdentifier(), connected); + } else { + logger.warn( + "{}: context='UnsubscribeMonitorEvents' thing='monitor' id='{}' - Could not unsubscribe to monitor events, because EventSession not initialisaed", + getLogIdentifier(), monitorHandler.getZoneMinderId()); + } + } catch (Exception ex) { + logger.error( + "{}: context='SubscribeMonitorEvents' - Exception occurred when subscribing for MonitorEvents.", + getLogIdentifier(), ex); + } - } catch (Exception exception) { - logger.error("{}: openConnection(): Exception: ", getLogIdentifier(), exception); - setConnected(false); - } finally { - if (isConnected() == false) { - closeConnection(); - } + } + + public void activateForceAlarm(String monitorId, Integer priority, String reason, String note, String showText, + Integer timeoutSeconds) { + try { + if (zoneMinderEventSession != null) { + zoneMinderEventSession.activateForceAlarm(monitorId, priority, reason, note, showText, timeoutSeconds); + } else { + logger.error("{}: context='activateForceAlarm' No EventSession active for Monitor with Id='{}'", + getLogIdentifier(), monitorId); } + } catch (IOException ex) { + logger.error("{}: context='activateForceAlarm' tag='exception' - Call to activeForceAlarm failed", + getLogIdentifier(), ex); } - return isConnected(); + } - synchronized void closeConnection() { + public void deactivateForceAlarm(String monitorId) { try { - logger.debug("{}: closeConnection(): Closed HTTP Connection!", getLogIdentifier()); - setConnected(false); + if (zoneMinderEventSession != null) { + zoneMinderEventSession.deactivateForceAlarm(monitorId); + } else { + logger.error("{}: context='deactivateForceAlarm' No EventSession active for Monitor with Id='{}'", + getLogIdentifier(), monitorId); + } - } catch (Exception exception) { - logger.error("{}: closeConnection(): Error closing connection - {}", getLogIdentifier(), - exception.getMessage()); + } catch (Exception ex) { + logger.error("{}: context='deactivateForceAlarm' tag='exception' - Call to deactiveForceAlarm failed", + getLogIdentifier(), ex); } } protected State getServerCpuLoadState() { - State state = UnDefType.UNDEF; try { @@ -962,14 +1399,13 @@ protected State getServerCpuLoadState() { } catch (Exception ex) { // Deliberately kept as debug info! - logger.debug("{}: Exception='{}'", getLogIdentifier(), ex.getMessage()); + logger.debug("{}: context='getServerCpuLoadState' Exception='{}'", getLogIdentifier(), ex.getMessage()); } return state; } protected State getServerDiskUsageState() { - State state = UnDefType.UNDEF; try { @@ -978,21 +1414,56 @@ protected State getServerDiskUsageState() { } } catch (Exception ex) { // Deliberately kept as debug info! - logger.debug("{}: Exception {}", getLogIdentifier(), ex.getMessage()); + logger.debug("{}: context='getServerDiskUsageState' Exception {}", getLogIdentifier(), ex.getMessage()); } return state; } @Override - public void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionInfo connection) { - logger.info("{}: Brigde went ONLINE", getLogIdentifier()); + public void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionHandler connection) { + IZoneMinderConnectionHandler session = null; + try { + session = aquireSession(); + + IZoneMinderServer serverProxy = ZoneMinderFactory.getServerProxy(session); + ZoneMinderConfig cfgPathZms = serverProxy.getConfig(ZoneMinderConfigEnum.ZM_PATH_ZMS); + ZoneMinderConfig cfgOptFrameServer = serverProxy.getConfig(ZoneMinderConfigEnum.ZM_OPT_FRAME_SERVER); + logger.debug("{}: context='onBridgeConnected' Api Enabled : {}", getLogIdentifier(), + zoneMinderConnection.isApiEnabled()); + logger.debug("{}: context='onBridgeConnected' Authentication Enabled : {}", getLogIdentifier(), + zoneMinderConnection.isAuthenticationEnabled()); + logger.debug("{}: context='onBridgeConnected' AuthHash Allowed : {}", getLogIdentifier(), + zoneMinderConnection.getAuthenticationHashAllowed()); + if (zoneMinderConnection.getAuthenticationHashAllowed()) { + logger.debug("{}: context='onBridgeConnected' AuthHash Relay : {}", getLogIdentifier(), + zoneMinderConnection.getAuthenticationHashReleayMethod().toString()); + } + logger.debug("{}: context='onBridgeConnected' Portal URI: {}", getLogIdentifier(), + zoneMinderConnection.getPortalUri().toString()); + logger.debug("{}: context='onBridgeConnected' API URI: {}", getLogIdentifier(), + zoneMinderConnection.getApiUri().toString()); + logger.debug("{}: context='onBridgeConnected' ZMS URI: {}", getLogIdentifier(), + cfgPathZms.getValueAsString()); + logger.debug("{}: context='onBridgeConnected' FrameServer: {}", getLogIdentifier(), + cfgOptFrameServer.getvalueAsBoolean()); + } catch (ZoneMinderException | Exception ex) { + logger.error( + "{}: context='onBridgeConnected' Exception occurred when calling 'onBridgeConencted()' Message='{}'", + getLogIdentifier(), ex.getMessage(), ex.getCause()); + + } finally { + if (session != null) { + releaseSession(); + } + } try { // Start the discovery service if (discoveryService == null) { discoveryService = new ZoneMinderDiscoveryService(this, 30); } + discoveryService.activate(); if (discoveryRegistration == null) { @@ -1001,45 +1472,29 @@ public void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderC discoveryService, new Hashtable()); } } catch (Exception e) { - logger.error("BRIDGE [{}]: Exception occurred when starting discovery service Exception='{}'", getThingId(), - e.getMessage()); + logger.error("{}: context='onBridgeConnected' Exception occurred when starting discovery service", + getLogIdentifier(), e.getCause()); } - if (taskRefreshData == null) { - - // Perform first refresh manually (we want to force update of DiskUsage) - boolean updateDiskUsage = (getBridgeConfig().getRefreshIntervalLowPriorityTask() > 0) ? true : false; - refreshThing(zoneMinderSession, updateDiskUsage); - - if (getBridgeConfig().getRefreshIntervalLowPriorityTask() != 0) { - refreshFrequency = calculateCommonRefreshFrequency(getBridgeConfig().getRefreshInterval()); - } else { - refreshFrequency = getBridgeConfig().getRefreshInterval(); - } - logger.info("BRIDGE [{}]: Calculated refresh inetrval to '{}'", getThingId(), refreshFrequency); - - if (taskRefreshData != null) { - taskRefreshData.cancel(true); - taskRefreshData = null; - } - - // Start job to handle next updates - taskRefreshData = startTask(refreshDataRunnable, refreshFrequency, refreshFrequency, TimeUnit.SECONDS); + try { + // Update properties + updateServerProperties(); + } catch (Exception e) { + logger.error( + "{}: method='onBridgeConnected' context='updateServerProperties' Exception occurred when starting discovery service", + getLogIdentifier(), e.getCause()); - if (taskPriorityRefreshData != null) { - taskPriorityRefreshData.cancel(true); - taskPriorityRefreshData = null; - } + } - // Only start if Priority Frequency is higher than ordinary - if (refreshFrequency > 1) { - taskPriorityRefreshData = startTask(refreshPriorityDataRunnable, 0, 1, TimeUnit.SECONDS); - } + if (taskRefreshData != null) { + taskRefreshData.cancel(true); + taskRefreshData = null; } - // Update properties - updateMonitorProperties(zoneMinderSession); + // Start job to handle next updates + taskRefreshData = startTask(refreshDataRunnable, 1, 1, TimeUnit.SECONDS); + } @Override @@ -1056,13 +1511,6 @@ public void onBridgeDisconnected(ZoneMinderServerBridgeHandler bridge) { logger.debug("{}: Stopping DataRefresh task", getLogIdentifier()); } - // Stopping High priority thread while OFFLINE - if (taskPriorityRefreshData != null) { - taskPriorityRefreshData.cancel(true); - taskPriorityRefreshData = null; - logger.debug("{}: Stopping Priority DataRefresh task", getLogIdentifier()); - } - // Make sure everything gets refreshed for (Channel ch : getThing().getChannels()) { handleCommand(ch.getUID(), RefreshType.REFRESH); @@ -1085,7 +1533,7 @@ public void onBridgeDisconnected(ZoneMinderServerBridgeHandler bridge) { * Method to start a data refresh task. */ protected ScheduledFuture startTask(Runnable command, long delay, long interval, TimeUnit unit) { - logger.debug("BRIDGE [{}]: Starting ZoneMinder Bridge Monitor Task. Command='{}'", getThingId(), + logger.debug("{}: Starting ZoneMinder Bridge Monitor Task. Command='{}'", getLogIdentifier(), command.toString()); if (interval == 0) { return null; @@ -1106,56 +1554,69 @@ protected void stopTask(ScheduledFuture task) { } } catch (Exception ex) { } - } - public ArrayList getMonitors() { - if (isOnline()) { - - IZoneMinderServer serverProxy = ZoneMinderFactory.getServerProxy(zoneMinderSession); - ArrayList result = serverProxy.getMonitors(); + public ArrayList getMonitors() { + if (isConnected()) { + IZoneMinderServer serverProxy = null; + try { + serverProxy = ZoneMinderFactory.getServerProxy(aquireSession()); + ArrayList result = serverProxy.getMonitors(); + return result; + } catch (ZoneMinderGeneralException | ZoneMinderResponseException | ZoneMinderInvalidData + | ZoneMinderAuthenticationException ex) { + logger.error("{}: context='getMonitors' Exception occurred", getLogIdentifier(), ex.getCause()); - return result; + } finally { + if (serverProxy != null) { + releaseSession(); + } + } } - return new ArrayList(); + return new ArrayList<>(); } - /* - * This is experimental - * Try to add different properties - */ - private void updateMonitorProperties(IZoneMinderSession session) { + private void updateServerProperties() { + if (!isConnected()) { + return; + } + // Update property information about this device Map properties = editProperties(); - IZoneMinderServer serverProxy = ZoneMinderFactory.getServerProxy(session); + IZoneMinderConnectionHandler session = null; IZoneMinderHostVersion hostVersion = null; try { + session = aquireSession(); + IZoneMinderServer serverProxy = ZoneMinderFactory.getServerProxy(session); + hostVersion = serverProxy.getHostVersion(); - logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), - serverProxy.getHttpUrl(), serverProxy.getHttpResponseCode(), serverProxy.getHttpResponseMessage()); + if (hostVersion.getHttpStatus() != HttpStatus.OK_200) { + return; + } ZoneMinderConfig configUseApi = serverProxy.getConfig(ZoneMinderConfigEnum.ZM_OPT_USE_API); - logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), - serverProxy.getHttpUrl(), serverProxy.getHttpResponseCode(), serverProxy.getHttpResponseMessage()); ZoneMinderConfig configUseAuth = serverProxy.getConfig(ZoneMinderConfigEnum.ZM_OPT_USE_AUTH); - logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), - serverProxy.getHttpUrl(), serverProxy.getHttpResponseCode(), serverProxy.getHttpResponseMessage()); - ZoneMinderConfig configTrigerrs = serverProxy.getConfig(ZoneMinderConfigEnum.ZM_OPT_TRIGGERS); - logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), - configUseApi.getHttpUrl(), configUseApi.getHttpResponseCode(), - configUseApi.getHttpResponseMessage()); + ZoneMinderConfig configAllowHashLogin = serverProxy.getConfig(ZoneMinderConfigEnum.ZM_AUTH_HASH_LOGINS); + ZoneMinderConfig configFrameServer = serverProxy.getConfig(ZoneMinderConfigEnum.ZM_OPT_FRAME_SERVER); properties.put(ZoneMinderProperties.PROPERTY_SERVER_VERSION, hostVersion.getVersion()); properties.put(ZoneMinderProperties.PROPERTY_SERVER_API_VERSION, hostVersion.getApiVersion()); properties.put(ZoneMinderProperties.PROPERTY_SERVER_USE_API, configUseApi.getValueAsString()); properties.put(ZoneMinderProperties.PROPERTY_SERVER_USE_AUTHENTIFICATION, configUseAuth.getValueAsString()); + properties.put(ZoneMinderProperties.PROPERTY_SERVER_USE_AUTH_HASH, configAllowHashLogin.getValueAsString()); properties.put(ZoneMinderProperties.PROPERTY_SERVER_TRIGGERS_ENABLED, configTrigerrs.getValueAsString()); - } catch (FailedLoginException | ZoneMinderUrlNotFoundException | IOException e) { - logger.warn("{}: Exception occurred when updating monitor properties (Exception='{}'", getLogIdentifier(), - e.getMessage()); + properties.put(ZoneMinderProperties.PROPERTY_SERVER_FRAME_SERVER, configFrameServer.getValueAsString()); + + } catch (ZoneMinderUrlNotFoundException | IOException | ZoneMinderGeneralException | ZoneMinderResponseException + | ZoneMinderInvalidData | ZoneMinderAuthenticationException e) { + logger.warn("{}: Exception occurred when updating monitor properties", getLogIdentifier(), e); + } finally { + if (session != null) { + releaseSession(); + } } // Must loop over the new properties since we might have added data diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderThingMonitorHandler.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderThingMonitorHandler.java index 306ad59298796..0217b73920ab6 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderThingMonitorHandler.java +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderThingMonitorHandler.java @@ -8,19 +8,13 @@ */ package org.openhab.binding.zoneminder.handler; -import java.io.IOException; import java.math.BigDecimal; -import java.security.GeneralSecurityException; +import java.net.MalformedURLException; import java.util.Map; import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.security.auth.login.FailedLoginException; import org.eclipse.smarthome.core.library.types.OnOffType; import org.eclipse.smarthome.core.thing.Bridge; -import org.eclipse.smarthome.core.thing.Channel; import org.eclipse.smarthome.core.thing.ChannelUID; import org.eclipse.smarthome.core.thing.Thing; import org.eclipse.smarthome.core.thing.ThingStatus; @@ -32,26 +26,36 @@ import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.zoneminder.ZoneMinderConstants; import org.openhab.binding.zoneminder.ZoneMinderProperties; -import org.openhab.binding.zoneminder.internal.DataRefreshPriorityEnum; +import org.openhab.binding.zoneminder.internal.RefreshPriority; import org.openhab.binding.zoneminder.internal.config.ZoneMinderThingMonitorConfig; +import org.openhab.binding.zoneminder.internal.state.ChannelStateChangeSubscriber; +import org.openhab.binding.zoneminder.internal.state.MonitorThingState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Sets; -import name.eskildsen.zoneminder.IZoneMinderConnectionInfo; -import name.eskildsen.zoneminder.IZoneMinderDaemonStatus; -import name.eskildsen.zoneminder.IZoneMinderEventData; +import name.eskildsen.zoneminder.IZoneMinderConnectionHandler; import name.eskildsen.zoneminder.IZoneMinderEventSubscriber; import name.eskildsen.zoneminder.IZoneMinderMonitor; -import name.eskildsen.zoneminder.IZoneMinderMonitorData; -import name.eskildsen.zoneminder.IZoneMinderSession; +import name.eskildsen.zoneminder.IZoneMinderServer; import name.eskildsen.zoneminder.ZoneMinderFactory; -import name.eskildsen.zoneminder.api.event.ZoneMinderEvent; +import name.eskildsen.zoneminder.api.monitor.ZoneMinderMonitorStatus; import name.eskildsen.zoneminder.api.telnet.ZoneMinderTriggerEvent; +import name.eskildsen.zoneminder.common.ZoneMinderConfigEnum; import name.eskildsen.zoneminder.common.ZoneMinderMonitorFunctionEnum; -import name.eskildsen.zoneminder.common.ZoneMinderMonitorStatusEnum; -import name.eskildsen.zoneminder.exception.ZoneMinderUrlNotFoundException; +import name.eskildsen.zoneminder.data.IMonitorDataGeneral; +import name.eskildsen.zoneminder.data.IMonitorDataStillImage; +import name.eskildsen.zoneminder.data.IZoneMinderDaemonStatus; +import name.eskildsen.zoneminder.data.IZoneMinderEventData; +import name.eskildsen.zoneminder.data.ZoneMinderConfig; +import name.eskildsen.zoneminder.exception.ZoneMinderAuthHashNotEnabled; +import name.eskildsen.zoneminder.exception.ZoneMinderAuthenticationException; +import name.eskildsen.zoneminder.exception.ZoneMinderException; +import name.eskildsen.zoneminder.exception.ZoneMinderGeneralException; +import name.eskildsen.zoneminder.exception.ZoneMinderInvalidData; +import name.eskildsen.zoneminder.exception.ZoneMinderResponseException; +import name.eskildsen.zoneminder.internal.ZoneMinderContentResponse; /** * The {@link ZoneMinderThingMonitorHandler} is responsible for handling commands, which are @@ -59,7 +63,8 @@ * * @author Martin S. Eskildsen - Initial contribution */ -public class ZoneMinderThingMonitorHandler extends ZoneMinderBaseThingHandler implements IZoneMinderEventSubscriber { +public class ZoneMinderThingMonitorHandler extends ZoneMinderBaseThingHandler + implements ChannelStateChangeSubscriber, IZoneMinderEventSubscriber { public static final Set SUPPORTED_THING_TYPES = Sets .newHashSet(ZoneMinderConstants.THING_TYPE_THING_ZONEMINDER_MONITOR); @@ -68,32 +73,19 @@ public class ZoneMinderThingMonitorHandler extends ZoneMinderBaseThingHandler im private static final int MAX_MONITOR_STATUS_WATCH_COUNT = 3; /** Make sure we can log errors, warnings or what ever somewhere */ - private Logger logger = LoggerFactory.getLogger(ZoneMinderThingMonitorHandler.class); + private final Logger logger = LoggerFactory.getLogger(ZoneMinderThingMonitorHandler.class); + private RefreshPriority forcedPriority = RefreshPriority.DISABLED; private String lastMonitorStatus = MONITOR_STATUS_NOT_INIT; private Integer monitorStatusMatchCount = 3; private ZoneMinderThingMonitorConfig config; - private Boolean _running = false; - - private ZoneMinderEvent curEvent = null; + MonitorThingState dataConverter = new MonitorThingState(this); - /** - * Channels - */ - private ZoneMinderMonitorFunctionEnum channelFunction = ZoneMinderMonitorFunctionEnum.NONE; - private Boolean channelEnabled = false; - private boolean channelRecordingState = false; - private boolean channelAlarmedState = false; - private String channelEventCause = ""; - private ZoneMinderMonitorStatusEnum channelMonitorStatus = ZoneMinderMonitorStatusEnum.UNKNOWN; - private boolean channelDaemonCapture = false; - private boolean channelDaemonAnalysis = false; - private boolean channelDaemonFrame = false; - private boolean channelForceAlarm = false; - - private int forceAlarmManualState = -1; + private long lastRefreshGeneralData = 0; + private long lastRefreshStillImage = 0; + private boolean frameDaemonActive = false; public ZoneMinderThingMonitorHandler(Thing thing) { super(thing); @@ -103,6 +95,15 @@ public ZoneMinderThingMonitorHandler(Thing thing) { @Override public void dispose() { + try { + ZoneMinderServerBridgeHandler bridge = getZoneMinderBridgeHandler(); + logger.info("{}: Unsubscribing from Monitor Events: {}", getLogIdentifier(), + bridge.getThing().getUID().getAsString()); + bridge.unsubscribeMonitorEvents(this); + + } catch (Exception ex) { + logger.error("{}: Exception occurred when calling 'onBridgeDisonnected()'.", getLogIdentifier(), ex); + } } @Override @@ -117,63 +118,119 @@ public String getZoneMinderId() { } @Override - public void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionInfo connection) - throws IllegalArgumentException, GeneralSecurityException, IOException, ZoneMinderUrlNotFoundException { - + public void onBridgeConnected(ZoneMinderServerBridgeHandler bridge, IZoneMinderConnectionHandler connection) { try { - logger.info("{}: Bridge '{}' connected", getLogIdentifier(), bridge.getThing().getUID().getAsString()); + logger.debug("{}: Bridge '{}' connected", getLogIdentifier(), bridge.getThing().getUID().getAsString()); super.onBridgeConnected(bridge, connection); - ZoneMinderFactory.SubscribeMonitorEvents(connection, config.getZoneMinderId(), this); - IZoneMinderSession session = aquireSession(); - IZoneMinderMonitor monitor = ZoneMinderFactory.getMonitorProxy(session, config.getZoneMinderId()); - IZoneMinderMonitorData monitorData = monitor.getMonitorData(); + logger.info("{}: Add subsription for Monitor Events: {}", getLogIdentifier(), + bridge.getThing().getUID().getAsString()); + bridge.subscribeMonitorEvents(this); + + IZoneMinderServer serverProxy = ZoneMinderFactory.getServerProxy(connection); + ZoneMinderConfig cfg = serverProxy.getConfig(ZoneMinderConfigEnum.ZM_OPT_FRAME_SERVER); + frameDaemonActive = cfg.getvalueAsBoolean(); + } catch (ZoneMinderGeneralException | ZoneMinderResponseException | ZoneMinderAuthenticationException + | ZoneMinderInvalidData ex) { + logger.error("{}: context='onBridgeConnected' error in call to 'getServerProxy' - Message='{}'", + getLogIdentifier(), ex.getMessage(), ex.getCause()); + + } catch (MalformedURLException e) { + logger.error("{}: context='onBridgeConnected' error in call to 'getServerProxy' - Message='{}' (Exception)", + getLogIdentifier(), e.getMessage(), e.getCause()); + } - logger.debug("{}: SourceType: {}", getLogIdentifier(), monitorData.getSourceType().name()); - logger.debug("{}: Format: {}", getLogIdentifier(), monitorData.getFormat()); - logger.debug("{}: AlarmFrameCount: {}", getLogIdentifier(), monitorData.getAlarmFrameCount()); - logger.debug("{}: AlarmMaxFPS: {}", getLogIdentifier(), monitorData.getAlarmMaxFPS()); - logger.debug("{}: AnalysisFPS: {}", getLogIdentifier(), monitorData.getAnalysisFPS()); - logger.debug("{}: Height x Width: {} x {}", getLogIdentifier(), monitorData.getHeight(), - monitorData.getWidth()); + } - updateMonitorProperties(session); + @Override + public void onBridgeDisconnected(ZoneMinderServerBridgeHandler bridge) { + try { + logger.debug("{}: Bridge '{}' disconnected", getLogIdentifier(), bridge.getThing().getUID().getAsString()); - } catch (Exception ex) { - logger.error("{}: Exception occurred when calling 'onBridgeConencted()'. Exception='{}'", - getLogIdentifier(), ex.getMessage()); + super.onBridgeDisconnected(bridge); - } finally { - releaseSession(); + } catch (Exception ex) { + logger.error("{}: Exception occurred when calling 'onBridgeDisonencted()'.", getLogIdentifier(), ex); } } @Override - public void onBridgeDisconnected(ZoneMinderServerBridgeHandler bridge) { - try { - logger.info("{}: Bridge '{}' disconnected", getLogIdentifier(), bridge.getThing().getUID().getAsString()); + public void onThingStatusChanged(ThingStatus thingStatus) { + if (thingStatus == ThingStatus.ONLINE) { + IZoneMinderConnectionHandler connection = null; + try { + connection = aquireSessionWait(); + IZoneMinderMonitor monitor = ZoneMinderFactory.getMonitorProxy(connection, config.getZoneMinderId()); + IMonitorDataGeneral monitorData = monitor.getMonitorData(); + + logger.debug("{}: SourceType: {}", getLogIdentifier(), monitorData.getSourceType().name()); + logger.debug("{}: Format: {}", getLogIdentifier(), monitorData.getFormat()); + logger.debug("{}: AlarmFrameCount: {}", getLogIdentifier(), monitorData.getAlarmFrameCount()); + logger.debug("{}: AlarmMaxFPS: {}", getLogIdentifier(), monitorData.getAlarmMaxFPS()); + logger.debug("{}: AnalysisFPS: {}", getLogIdentifier(), monitorData.getAnalysisFPS()); + logger.debug("{}: Height x Width: {} x {}", getLogIdentifier(), monitorData.getHeight(), + monitorData.getWidth()); + } catch (ZoneMinderInvalidData | ZoneMinderAuthenticationException | ZoneMinderGeneralException + | ZoneMinderResponseException ex) { + logger.error("{}: context='onThingStatusChanged' error in call to 'getMonitorData' - Message='{}'", + getLogIdentifier(), ex.getMessage(), ex.getCause()); + + } finally { + if (connection != null) { + releaseSession(); + } + } - logger.info("{}: Unsubscribing from Monitor Events: {}", getLogIdentifier(), - bridge.getThing().getUID().getAsString()); - ZoneMinderFactory.UnsubscribeMonitorEvents(config.getZoneMinderId(), this); + try { + updateMonitorProperties(); - logger.debug("{}: Calling parent onBridgeConnected()", getLogIdentifier()); - super.onBridgeDisconnected(bridge); + } catch (Exception ex) { + logger.error( + "{}: context='onThingStatusChanged' - Exception occurred when calling 'updateMonitorPropoerties()'. Exception='{}'", + getLogIdentifier(), ex.getMessage()); + + } + } + } + @Override + public void channelLinked(ChannelUID channelUID) { + try { + if (!channelUID.getId().equals(ZoneMinderConstants.CHANNEL_ONLINE)) { + dataConverter.subscribe(channelUID); + } + super.channelLinked(channelUID); + + logger.info("{}: context='channelLinked' - Unlinking from channel '{}'", getLogIdentifier(), + channelUID.getAsString()); } catch (Exception ex) { - logger.error("{}: Exception occurred when calling 'onBridgeDisonencted()'. Exception='{}'", - getLogIdentifier(), ex.getMessage()); + logger.info("{}: context='channelUnlinked' - Exception when Unlinking from channel '{}' - EXCEPTION)'{}'", + getLogIdentifier(), channelUID.getAsString(), ex.getMessage()); } } + @Override + public void channelUnlinked(ChannelUID channelUID) { + try { + dataConverter.unsubscribe(channelUID); + super.channelUnlinked(channelUID); + logger.info("{}: context='channelUnlinked' - Unlinking from channel '{}'", getLogIdentifier(), + channelUID.getAsString()); + } catch (Exception ex) { + logger.info("{}: context='channelUnlinked' - Exception when Unlinking from channel '{}' - EXCEPTION)'{}'", + getLogIdentifier(), channelUID.getAsString(), ex.getMessage()); + + } + } + @Override public void handleCommand(ChannelUID channelUID, Command command) { + IZoneMinderMonitor monitorProxy = null; try { - logger.debug("{}: Channel '{}' in monitor '{}' received command='{}'", getLogIdentifier(), channelUID, getZoneMinderId(), command); @@ -185,112 +242,193 @@ public void handleCommand(ChannelUID channelUID, Command command) { // Communication TO Monitor switch (channelUID.getId()) { - // Done via Telnet connection case ZoneMinderConstants.CHANNEL_MONITOR_FORCE_ALARM: - logger.debug( - "{}: 'handleCommand' => CHANNEL_MONITOR_FORCE_ALARM: Command '{}' received for monitor '{}'", - getLogIdentifier(), command, channelUID.getId()); - - if ((command == OnOffType.OFF) || (command == OnOffType.ON)) { - String eventText = getConfigValueAsString(ZoneMinderConstants.PARAMETER_MONITOR_EVENTTEXT); - - BigDecimal eventTimeout = getConfigValueAsBigDecimal( - ZoneMinderConstants.PARAMETER_MONITOR_TRIGGER_TIMEOUT); - - ZoneMinderServerBridgeHandler bridge = getZoneMinderBridgeHandler(); - if (bridge == null) { - logger.warn("'handleCommand()': Bridge is 'null'!"); - } - - IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(), - getZoneMinderId()); - try { - if (command == OnOffType.ON) { - forceAlarmManualState = 1; - logger.info("{}: Activate 'ForceAlarm' to '{}' (Reason='{}', Timeout='{}')", - getLogIdentifier(), command, eventText, eventTimeout.intValue()); - - monitorProxy.activateForceAlarm(255, ZoneMinderConstants.MONITOR_EVENT_OPENHAB, - eventText, "", eventTimeout.intValue()); - - } - - else if (command == OnOffType.OFF) { - forceAlarmManualState = 0; - logger.info("{}: Cancel 'ForceAlarm'", getLogIdentifier()); - monitorProxy.deactivateForceAlarm(); + IZoneMinderConnectionHandler connection = null; + try { + // Force Alarm can only be activated when Function is either NODECT or MODECT + if ((dataConverter.getMonitorFunction() == ZoneMinderMonitorFunctionEnum.MODECT) + || (dataConverter.getMonitorFunction() == ZoneMinderMonitorFunctionEnum.NODECT)) { + logger.debug( + "{}: 'handleCommand' => CHANNEL_MONITOR_FORCE_ALARM: Command '{}' received for monitor '{}'", + getLogIdentifier(), command, channelUID.getId()); + + if ((command == OnOffType.OFF) || (command == OnOffType.ON)) { + dataConverter.setMonitorForceAlarmInternal((command == OnOffType.ON) ? true : false); + String eventText = getConfigValueAsString( + ZoneMinderConstants.PARAMETER_MONITOR_EVENTTEXT); + + BigDecimal eventTimeout = getConfigValueAsBigDecimal( + ZoneMinderConstants.PARAMETER_MONITOR_TRIGGER_TIMEOUT); + + try { + connection = aquireSession(); + if (connection == null) { + logger.error( + "{}: context='handleCommand' tags='ForceAlarm' - Command='{}' failed to obtain session", + getLogIdentifier(), command); + return; + } + monitorProxy = ZoneMinderFactory.getMonitorProxy(connection, getZoneMinderId()); + + if (command == OnOffType.ON) { + logger.info("{}: Activate 'ForceAlarm' to '{}' (Reason='{}', Timeout='{}')", + getLogIdentifier(), command, eventText, eventTimeout.intValue()); + + getZoneMinderBridgeHandler().activateForceAlarm(getZoneMinderId(), 255, + ZoneMinderConstants.MONITOR_EVENT_OPENHAB, eventText, "", + eventTimeout.intValue()); + + dataConverter.setMonitorForceAlarmInternal(true); + + // Force a refresh + startAlarmRefresh(eventTimeout.intValue()); + + } + + else if (command == OnOffType.OFF) { + logger.debug("{}: Cancel 'ForceAlarm'", getLogIdentifier()); + + getZoneMinderBridgeHandler().deactivateForceAlarm(getZoneMinderId()); + dataConverter.setMonitorForceAlarmInternal(false); + // Stop Alarm Refresh + forceStopAlarmRefresh(); + + } + fetchMonitorGeneralData(monitorProxy); + + } catch (Exception ex) { + logger.error( + "{}: Context='handleCommand' Channel='{}' EXCEPTION: Call to 'ForceAlarm' Command='{}' failed", + getLogIdentifier(), channelUID.getId(), command, ex); + } } + } else { + logger.warn( + "{}: context='handleCommand' tag='CHANNEL_MONITOR_FORCE_ALARM' is inactive when function is not 'MODECT' or 'NODECT'", + getLogIdentifier()); - } finally { + } + } catch (Exception ex) { + logger.error("{}: context='handleCommand' tag='CHANNEL_MONITOR_FORCE_ALARM'", + getLogIdentifier()); + } finally { + if (monitorProxy != null) { + monitorProxy = null; releaseSession(); } - RecalculateChannelStates(); - - handleCommand(channelUID, RefreshType.REFRESH); - handleCommand(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_EVENT_STATE), - RefreshType.REFRESH); - handleCommand(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_RECORD_STATE), - RefreshType.REFRESH); - - // Force a refresh - startPriorityRefresh(); - + requestChannelRefresh(); } break; case ZoneMinderConstants.CHANNEL_MONITOR_ENABLED: - logger.debug( - "{}: 'handleCommand' => CHANNEL_MONITOR_ENABLED: Command '{}' received for monitor '{}'", - getLogIdentifier(), command, channelUID.getId()); - - if ((command == OnOffType.OFF) || (command == OnOffType.ON)) { - boolean newState = ((command == OnOffType.ON) ? true : false); - - IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(), - getZoneMinderId()); - try { - monitorProxy.SetEnabled(newState); - } finally { - releaseSession(); - } + try { + logger.debug( + "{}:context='handleCommand' tag='CHANNEL_MONITOR_ENABLED' Command '{}' received for monitor '{}'", + getLogIdentifier(), command, channelUID.getId()); + + if ((command == OnOffType.OFF) || (command == OnOffType.ON)) { + boolean newState = ((command == OnOffType.ON) ? true : false); + + ZoneMinderContentResponse zmcr = null; + try { + monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(), getZoneMinderId()); + if (monitorProxy == null) { + logger.error( + "{}: Connection to ZoneMinder Server was lost when handling command '{}'. Restart openHAB", + getLogIdentifier(), command); + return; + } + zmcr = monitorProxy.SetEnabled(newState); + logger.debug("{}: ResponseCode='{}' ResponseMessage='{}' URL='{}'", getLogIdentifier(), + zmcr.getHttpStatus(), zmcr.getHttpResponseMessage(), zmcr.getHttpRequestUrl()); + + } catch (ZoneMinderException ex) { + logger.error( + "{}: context='handleCommand' error in call to 'SetEnabled' ExceptionClass='{}' - Message='{}'", + getLogIdentifier(), ex.getClass().getCanonicalName(), ex.getMessage(), + ex.getCause()); + } finally { + if (monitorProxy != null) { + monitorProxy = null; + releaseSession(); + } + } - channelEnabled = newState; + dataConverter.setMonitorEnabled(newState); - logger.info("{}: Setting enabled to '{}'", getLogIdentifier(), command); - } + logger.info( + "{}: context='handleCommand' tags='enabled' - Successfully changed function setting to '{}'", + getLogIdentifier(), command); + } + } catch (Exception ex) { + logger.error("{}: Exception in 'handleCommand' => 'CHANNEL_MONITOR_ENABLE' Exception='{}'", + getLogIdentifier(), ex.getMessage()); - handleCommand(channelUID, RefreshType.REFRESH); + } finally { + requestChannelRefresh(); + } break; case ZoneMinderConstants.CHANNEL_MONITOR_FUNCTION: - String commandString = ""; - if (ZoneMinderMonitorFunctionEnum.isValid(command.toString())) { + try { + logger.debug( + "{}: context='handleCommand' tag='CHANNEL_MONITOR_FUNCTION' Command '{}' received for monitor '{}'", + getLogIdentifier(), command, channelUID.getId()); + + String commandString = ""; + if (ZoneMinderMonitorFunctionEnum.isValid(command.toString())) { + commandString = ZoneMinderMonitorFunctionEnum.getEnum(command.toString()).toString(); + ZoneMinderContentResponse zmcr = null; + try { + // Change Function for camera in ZoneMinder + monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(), getZoneMinderId()); + if (monitorProxy == null) { + logger.error( + "{}: Connection to ZoneMinder Server was lost when handling command '{}'. Restart openHAB", + getLogIdentifier(), command); + return; + } + + zmcr = monitorProxy.SetFunction(commandString); + + logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), + zmcr.getHttpRequestUrl(), zmcr.getHttpStatus(), zmcr.getHttpResponseMessage()); + + fetchMonitorGeneralData(monitorProxy); + fetchMonitorDaemonStatus(true, true, monitorProxy); + + } catch (ZoneMinderAuthenticationException | ZoneMinderGeneralException + | ZoneMinderResponseException ex) { + } finally { + if (monitorProxy != null) { + monitorProxy = null; + releaseSession(); + } + } - commandString = ZoneMinderMonitorFunctionEnum.getEnum(command.toString()).toString(); - ZoneMinderServerBridgeHandler bridge = getZoneMinderBridgeHandler(); + dataConverter.setMonitorFunction(ZoneMinderMonitorFunctionEnum.getEnum(command.toString())); - IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(), - getZoneMinderId()); - try { - monitorProxy.SetFunction(commandString); - } finally { - releaseSession(); - } + logger.debug( + "{}: context='handleCommand' tags='function' - Successfully changed function setting to '{}'", + getLogIdentifier(), commandString); - // Make sure local copy is set to new value - channelFunction = ZoneMinderMonitorFunctionEnum.getEnum(command.toString()); + } else { + logger.error( + "{}: Value '{}' for monitor channel is not valid. Accepted values is: 'None', 'Monitor', 'Modect', Record', 'Mocord', 'Nodect'", + getLogIdentifier(), commandString); + } + } catch (Exception ex) { + logger.error("{}: Exception in 'handleCommand' => 'CHANNEL_MONITOR_FUNCTION'", + getLogIdentifier(), ex.getCause()); - logger.info("{}: Setting function to '{}'", getLogIdentifier(), commandString); + } finally { + requestChannelRefresh(); - } else { - logger.error( - "{}: Value '{}' for monitor channel is not valid. Accepted values is: 'None', 'Monitor', 'Modect', Record', 'Mocord', 'Nodect'", - getLogIdentifier(), commandString); } - handleCommand(channelUID, RefreshType.REFRESH); + break; // They are all readonly in the channel config. @@ -310,19 +448,28 @@ else if (command == OnOffType.OFF) { break; } } catch (Exception ex) { - logger.error("{}: handleCommand: Command='{}' failed for channel='{}' Exception='{}'", getLogIdentifier(), - command, channelUID.getId(), ex.getMessage()); + logger.error("{}: handleCommand: Command='{}' failed for channel='{}'", getLogIdentifier(), command, + channelUID.getId(), ex); } } @Override public void initialize() { - try { - super.initialize(); this.config = getMonitorConfig(); - logger.info("{}: ZoneMinder Monitor Handler Initialized", getLogIdentifier()); - logger.debug("{}: Monitor Id: {}", getLogIdentifier(), config.getZoneMinderId()); + + super.initialize(); + logger.info("{}: context='initialize' Monitor Handler Initialized", getLogIdentifier()); + + dataConverter.addChannel(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_FORCE_ALARM)); + dataConverter.addChannel(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE)); + dataConverter.addChannel(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_RECORD_STATE)); + dataConverter.addChannel(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_MOTION_EVENT)); + dataConverter.addChannel(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS)); + dataConverter.addChannel(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_ENABLED)); + dataConverter.addChannel(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_FUNCTION)); + dataConverter.addChannel(getChannelUIDFromChannelId(ZoneMinderConstants.CHANNEL_MONITOR_EVENT_STATE)); + } catch (Exception ex) { logger.error("{}: Exception occurred when calling 'initialize()'. Exception='{}'", getLogIdentifier(), ex.getMessage()); @@ -332,24 +479,73 @@ public void initialize() { @Override public void onTrippedForceAlarm(ZoneMinderTriggerEvent event) { try { - logger.info("{}: Received forceAlarm for monitor {}", getLogIdentifier(), event.getMonitorId()); - Channel channel = this.getThing().getChannel(ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS); - Channel chEventCause = this.getThing().getChannel(ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE); + logger.debug("{}: context='onTrippedForceAlarm' Received forceAlarm for monitor {}", getLogIdentifier(), + event.getMonitorId()); + + if (!isThingOnline()) { + logger.warn("{}: context='onTrippedForceAlarm' Skipping event '{}', because Thing is 'OFFLINE'", + getLogIdentifier(), event.toString()); + return; + } + + IZoneMinderEventData eventData = null; // Set Current Event to actual event if (event.getState()) { - startPriorityRefresh(); + IZoneMinderConnectionHandler connection = null; + try { + connection = aquireSession(); + IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(connection, getZoneMinderId()); + eventData = monitorProxy.getEventById(event.getEventId()); + + logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), + eventData.getHttpRequestUrl(), eventData.getHttpStatus(), + eventData.getHttpResponseMessage()); + + } catch (Exception ex) { + logger.error( + "{}: context='onTrippedForceAlarm' tag='session' Exception occurred when aquiring session - Exception='{}'", + getLogIdentifier(), ex.getMessage()); + + } catch (ZoneMinderInvalidData | ZoneMinderAuthenticationException | ZoneMinderGeneralException + | ZoneMinderResponseException ex) { + logger.error( + "{}: context='onTrippedForceAlarm' error in call to 'getMonitorProxy' ExceptionClass='{}' - Message='{}'", + getLogIdentifier(), ex.getClass().getCanonicalName(), ex.getMessage(), ex.getCause()); + } finally { + if (connection != null) { + releaseSession(); + } + + } + dataConverter.disableRefresh(); + dataConverter.setMonitorForceAlarmExternal(event.getState()); + dataConverter.setMonitorEventData(eventData); + dataConverter.enableRefresh(); + + forceStartAlarmRefresh(); } else { - curEvent = null; + dataConverter.disableRefresh(); + + dataConverter.setMonitorForceAlarmExternal(event.getState()); + dataConverter.setMonitorEventData(null); + dataConverter.enableRefresh(); + forceStopAlarmRefresh(); } + } catch (Exception ex) { - logger.error("{}: Exception occurred inTrippedForceAlarm() Exception='{}'", getLogIdentifier(), - ex.getMessage()); + logger.error("{}: context='onTrippedForceAlarm' Exception occurred inTrippedForceAlarm() Exception='{}'", + getLogIdentifier(), ex.getMessage()); } } + @Override + protected void updateState(ChannelUID channelUID, State state) { + super.updateState(channelUID, state); + } + protected ZoneMinderThingMonitorConfig getMonitorConfig() { return this.getConfigAs(ZoneMinderThingMonitorConfig.class); } @@ -359,41 +555,9 @@ protected String getZoneMinderThingType() { return ZoneMinderConstants.THING_ZONEMINDER_MONITOR; } - private Boolean isDaemonRunning(Boolean daemonStatus, String daemonStatusText) { - Boolean result = false; - - Pattern pattern = Pattern - .compile("[0-9]{2}/[0-9]{2}/[0-9]{2}\\s+([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]"); - - Matcher matcher = pattern.matcher(daemonStatusText); - - if (matcher.find()) { - - String currentMonitorStatus = daemonStatusText.substring(matcher.start(), matcher.end()); - if (lastMonitorStatus.equals(currentMonitorStatus)) { - monitorStatusMatchCount++; - } else if (lastMonitorStatus.equals(MONITOR_STATUS_NOT_INIT)) { - // We have just started, so we will assume that the monitor is running (don't set match count - // to Zero) - monitorStatusMatchCount++; - lastMonitorStatus = daemonStatusText.substring(matcher.start(), matcher.end()); - } else { - monitorStatusMatchCount = 0; - lastMonitorStatus = daemonStatusText.substring(matcher.start(), matcher.end()); - } - } - - else { - monitorStatusMatchCount = 0; - lastMonitorStatus = ""; - logger.debug("MONITOR-{}: Online(): No match found in status text.", getLogIdentifier()); - } - return daemonStatus; - } - @Override - public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { - // Assume succes + public void updateAvaliabilityStatus(IZoneMinderConnectionHandler connection) { + // Assume success ThingStatus newThingStatus = ThingStatus.ONLINE; ThingStatusDetail thingStatusDetailed = ThingStatusDetail.NONE; String thingStatusDescription = ""; @@ -401,20 +565,9 @@ public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { ThingStatus curThingStatus = this.getThing().getStatus(); boolean connectionStatus = false; + // Is connected to ZoneMinder and thing is ONLINE if (isConnected() && curThingStatus == ThingStatus.ONLINE) { - updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription); - return; - } - - try { - connectionStatus = ZoneMinderFactory.validateConnection(connection); - } catch (IllegalArgumentException e) { - logger.error("{}: validateConnection failed with exception='{}'", getLogIdentifier(), e.getMessage()); - newThingStatus = ThingStatus.OFFLINE; - thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR; - thingStatusDescription = "Could not connect to thing"; - updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription); return; } @@ -425,14 +578,14 @@ public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { // 1. Is there a Bridge assigned? if (getBridge() == null) { msg = String.format("No Bridge assigned to monitor '%s'", thing.getUID()); - logger.error("{}: {}", getLogIdentifier(), msg); + logger.error("{}: context='updateAvailabilityStatus' {}", getLogIdentifier(), msg); newThingStatus = ThingStatus.OFFLINE; thingStatusDetailed = ThingStatusDetail.BRIDGE_OFFLINE; thingStatusDescription = "No Bridge assigned to monitor"; - updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription); return; } else { - logger.debug("{}: ThingAvailability: Thing '{}' has Bridge '{}' defined (Check PASSED)", + logger.debug( + "{}: context='updateAvailabilityStatus' ThingAvailability: Thing '{}' has Bridge '{}' defined (Check PASSED)", getLogIdentifier(), thing.getUID(), getBridge().getBridgeUID()); } @@ -442,12 +595,12 @@ public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { newThingStatus = ThingStatus.OFFLINE; thingStatusDetailed = ThingStatusDetail.BRIDGE_OFFLINE; thingStatusDescription = msg; - updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription); - logger.error("{}: {}", getLogIdentifier(), msg); + logger.error("{}: context='updateAvailabilityStatus' {}", getLogIdentifier(), msg); return; } else { - logger.debug("{}: ThingAvailability: Bridge '{}' is ONLINE (Check PASSED)", getLogIdentifier(), - getBridge().getBridgeUID()); + logger.debug( + "{}: context='updateAvailabilityStatus' ThingAvailability: Bridge '{}' is ONLINE (Check PASSED)", + getLogIdentifier(), getBridge().getBridgeUID()); } // 3. Is Configuration OK? @@ -456,12 +609,12 @@ public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { newThingStatus = ThingStatus.OFFLINE; thingStatusDetailed = ThingStatusDetail.CONFIGURATION_ERROR; thingStatusDescription = msg; - updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription); - logger.error("{}: {}", getLogIdentifier(), msg); + logger.error("{}: context='updateAvailabilityStatus' {}", getLogIdentifier(), msg); return; } else { - logger.debug("{}: ThingAvailability: Thing '{}' has valid configuration (Check PASSED)", + logger.debug( + "{}: context='updateAvailabilityStatus' ThingAvailability: Thing '{}' has valid configuration (Check PASSED)", getLogIdentifier(), thing.getUID()); } @@ -471,39 +624,27 @@ public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { newThingStatus = ThingStatus.OFFLINE; thingStatusDetailed = ThingStatusDetail.CONFIGURATION_ERROR; thingStatusDescription = msg; - updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription); logger.error("{}: {}", getLogIdentifier(), msg); return; } else { - logger.debug("{}: ThingAvailability: ZoneMinder Id for Thing '{}' defined (Check PASSED)", + logger.debug( + "{}: context='updateAvailabilityStatus' ThingAvailability: ZoneMinder Id for Thing '{}' defined (Check PASSED)", getLogIdentifier(), thing.getUID()); } IZoneMinderMonitor monitorProxy = null; IZoneMinderDaemonStatus captureDaemon = null; - // TODO:: Also look at Analysis and Frame Daemons (only if they are supposed to be running) - // IZoneMinderSession session = aquireSession(); - - IZoneMinderSession curSession = null; - try { - curSession = ZoneMinderFactory.CreateSession(connection); - } catch (FailedLoginException | IllegalArgumentException | IOException - | ZoneMinderUrlNotFoundException ex) { - logger.error("{}: Create Session failed with exception {}", getLogIdentifier(), ex.getMessage()); - - newThingStatus = ThingStatus.OFFLINE; - thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR; - thingStatusDescription = "Failed to connect. (Check Log)"; - - updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription); - return; - } - + // Consider also looking at Analysis and Frame Daemons (only if they are supposed to be running) + IZoneMinderConnectionHandler curSession = connection; if (curSession != null) { monitorProxy = ZoneMinderFactory.getMonitorProxy(curSession, getZoneMinderId()); captureDaemon = monitorProxy.getCaptureDaemonStatus(); + logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), + captureDaemon.getHttpRequestUrl(), captureDaemon.getHttpStatus(), + captureDaemon.getHttpResponseMessage()); + } if (captureDaemon == null) { @@ -511,7 +652,6 @@ public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { newThingStatus = ThingStatus.OFFLINE; thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR; thingStatusDescription = msg; - updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription); logger.error("{}: {}", getLogIdentifier(), msg); return; } else if (!captureDaemon.getStatus()) { @@ -519,25 +659,24 @@ public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { newThingStatus = ThingStatus.OFFLINE; thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR; thingStatusDescription = msg; - updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription); logger.error("{}: {}", getLogIdentifier(), msg); return; } newThingStatus = ThingStatus.ONLINE; - - } catch (Exception exception) { + forcedPriority = RefreshPriority.PRIORITY_BATCH; + } catch (ZoneMinderException | Exception exception) { newThingStatus = ThingStatus.OFFLINE; thingStatusDetailed = ThingStatusDetail.COMMUNICATION_ERROR; thingStatusDescription = "Error occurred (Check log)"; updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription); - logger.error("{}: 'ThingMonitorHandler.updateAvailabilityStatus()': Exception occurred '{}'", - getLogIdentifier(), exception.getMessage()); + logger.error("{}: context='updateAvailabilityStatus' Exception occurred '{}'", getLogIdentifier(), + exception.getMessage()); return; + } finally { + updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription); } - - updateThingStatus(newThingStatus, thingStatusDetailed, thingStatusDescription); } /* @@ -549,53 +688,32 @@ public void updateAvaliabilityStatus(IZoneMinderConnectionInfo connection) { */ @Override public void updateChannel(ChannelUID channel) { - State state = null; + State state = UnDefType.UNDEF; try { switch (channel.getId()) { - case ZoneMinderConstants.CHANNEL_MONITOR_ENABLED: - state = getChannelBoolAsOnOffState(channelEnabled); - break; - case ZoneMinderConstants.CHANNEL_ONLINE: - // Ask super class to handle, because this channel is shared for all things super.updateChannel(channel); - break; + return; + + case ZoneMinderConstants.CHANNEL_MONITOR_ENABLED: case ZoneMinderConstants.CHANNEL_MONITOR_FORCE_ALARM: - state = getChannelBoolAsOnOffState(channelForceAlarm); - break; case ZoneMinderConstants.CHANNEL_MONITOR_EVENT_STATE: - state = getChannelBoolAsOnOffState(channelAlarmedState); - break; - case ZoneMinderConstants.CHANNEL_MONITOR_RECORD_STATE: - state = getChannelBoolAsOnOffState(channelRecordingState); - break; - + case ZoneMinderConstants.CHANNEL_MONITOR_MOTION_EVENT: case ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS: - state = getDetailedStatus(); - break; - case ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE: - state = getChannelStringAsStringState(channelEventCause); - break; - case ZoneMinderConstants.CHANNEL_MONITOR_FUNCTION: - state = getChannelStringAsStringState(channelFunction.toString()); - break; - case ZoneMinderConstants.CHANNEL_MONITOR_CAPTURE_DAEMON_STATE: - state = getChannelBoolAsOnOffState(channelDaemonCapture); - break; - case ZoneMinderConstants.CHANNEL_MONITOR_ANALYSIS_DAEMON_STATE: - state = getChannelBoolAsOnOffState(channelDaemonAnalysis); - break; - case ZoneMinderConstants.CHANNEL_MONITOR_FRAME_DAEMON_STATE: - state = getChannelBoolAsOnOffState(channelDaemonFrame); + case ZoneMinderConstants.CHANNEL_MONITOR_STILL_IMAGE: + state = null; break; + case ZoneMinderConstants.CHANNEL_MONITOR_VIDEOURL: + state = dataConverter.getVideoUrl(); + break; default: logger.warn("{}: updateChannel(): Monitor '{}': No handler defined for channel='{}'", getLogIdentifier(), thing.getLabel(), channel.getAsString()); @@ -605,14 +723,11 @@ public void updateChannel(ChannelUID channel) { } if (state != null) { - - logger.debug("{}: Setting channel '{}' to '{}'", getLogIdentifier(), channel.toString(), - state.toString()); updateState(channel.getId(), state); } } catch (Exception ex) { - logger.error("{}: Error when 'updateChannel' was called (channelId='{}'state='{}', exception'{}')", - getLogIdentifier(), channel.toString(), state.toString(), ex.getMessage()); + logger.error("{}: context='updateChannel' Error when updating channelId='{}' state='{}'", + getLogIdentifier(), channel.toString(), state.toString(), ex); } } @@ -624,265 +739,451 @@ public void updateStatus(ThingStatus status) { } - protected void RecalculateChannelStates() { - boolean recordingFunction = false; - boolean recordingDetailedState = false; - boolean alarmedFunction = false; - boolean alarmedDetailedState = false; - - // Calculate based on state of Function - switch (channelFunction) { - case NONE: - case MONITOR: - alarmedFunction = false; - recordingFunction = false; - break; - - case MODECT: - alarmedFunction = true; - recordingFunction = true; - break; - case RECORD: - alarmedFunction = false; - recordingFunction = true; - break; - case MOCORD: - alarmedFunction = true; - recordingFunction = true; - break; - case NODECT: - alarmedFunction = false; - recordingFunction = true; - break; - default: - recordingFunction = (curEvent != null) ? true : false; - } - logger.debug( - "{}: Recalculate channel states based on Function: Function='{}' -> alarmState='{}', recordingState='{}'", - getLogIdentifier(), channelFunction.name(), alarmedFunction, recordingFunction); - - // Calculated based on detailed Monitor Status - switch (channelMonitorStatus) { - case IDLE: - alarmedDetailedState = false; - recordingDetailedState = false; - channelForceAlarm = false; - channelEventCause = ""; - break; + private long getLastRefreshGeneralData() { + return lastRefreshGeneralData; + } - case PRE_ALARM: - alarmedDetailedState = true; - recordingDetailedState = true; - channelForceAlarm = false; - break; + private long getLastRefreshStillImage() { + return lastRefreshStillImage; + } - case ALARM: - alarmedDetailedState = true; - recordingDetailedState = true; - channelForceAlarm = true; - break; + private boolean refreshGeneralData() { + long now = System.currentTimeMillis(); + long lastUpdate = getLastRefreshGeneralData(); - case ALERT: - alarmedDetailedState = true; - recordingDetailedState = true; - channelForceAlarm = false; - break; + // Normal refresh interval + int interval = 10000; - case RECORDING: - alarmedDetailedState = false; - recordingDetailedState = true; - channelForceAlarm = false; - break; + if (!isInitialized()) { + return true; } - logger.debug( - "{}: Recalculate channel states based on Detailed State: DetailedState='{}' -> alarmState='{}', recordingState='{}'", - getLogIdentifier(), channelMonitorStatus.name(), alarmedDetailedState, recordingDetailedState); + if (dataConverter.isAlarmed()) { + // Alarm refresh interval + interval = 1000; + } + return ((now - lastUpdate) > interval) ? true : false; + } - // Check if Force alarm was initialed from openHAB - if (forceAlarmManualState == 0) { - if (channelForceAlarm) { - channelForceAlarm = false; - } else { - forceAlarmManualState = -1; - } - } else if (forceAlarmManualState == 1) { + private boolean refreshStillImage() { + RefreshPriority priority; + long now = System.currentTimeMillis(); + long lastUpdate = getLastRefreshStillImage(); - if (channelForceAlarm == false) { - channelForceAlarm = true; - } else { - forceAlarmManualState = -1; - } + // Normal refresh interval + int interval = 10000; + if (!isInitialized()) { + return true; + } + if (dataConverter.isAlarmed()) { + priority = getMonitorConfig().getImageRefreshEvent(); + } else { + priority = getMonitorConfig().getImageRefreshIdle(); } + switch (priority) { + case DISABLED: + return false; + + case PRIORITY_BATCH: + interval = 1000 * 60 * 60; + break; + + case PRIORITY_LOW: + interval = 1000 * 60; + break; + + case PRIORITY_NORMAL: + interval = 1000 * 10; + break; - // Now we can conclude on the Alarmed and Recording channel state - channelRecordingState = (recordingFunction && recordingDetailedState && channelEnabled); - channelAlarmedState = (alarmedFunction && alarmedDetailedState && channelEnabled); + case PRIORITY_HIGH: + interval = 1000 * 5; + break; + case PRIORITY_ALARM: + interval = 1000; + break; + default: + return false; + } + return ((now - lastUpdate) > interval) ? true : false; } @Override - protected void onFetchData() { + protected void onFetchData(RefreshPriority cyclePriority) { + IZoneMinderConnectionHandler session = null; + IMonitorDataGeneral data = null; - IZoneMinderSession session = null; + boolean refreshChannels = false; - session = aquireSession(); - try { - IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(session, getZoneMinderId()); + if (getThing().getStatus() != ThingStatus.ONLINE) { + return; + } - IZoneMinderMonitorData data = null; - IZoneMinderDaemonStatus captureDaemon = null; - IZoneMinderDaemonStatus analysisDaemon = null; - IZoneMinderDaemonStatus frameDaemon = null; + RefreshPriority curRefreshPriority = RefreshPriority.DISABLED; - data = monitorProxy.getMonitorData(); - logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), - monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(), - monitorProxy.getHttpResponseMessage()); + if (forcedPriority == RefreshPriority.UNKNOWN) { + return; + } - captureDaemon = monitorProxy.getCaptureDaemonStatus(); - logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), - monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(), - monitorProxy.getHttpResponseMessage()); + if (forcedPriority == RefreshPriority.DISABLED) { + curRefreshPriority = cyclePriority; + } else { + curRefreshPriority = forcedPriority; + forcedPriority = RefreshPriority.DISABLED; + } - analysisDaemon = monitorProxy.getAnalysisDaemonStatus(); - logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), - monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(), - monitorProxy.getHttpResponseMessage()); + session = null; + try { + session = aquireSession(); + if (session == null) { + logger.warn("{}: Failed to aquire session for refresh, refresh loop for monitor will be skipped.", + getLogIdentifier()); + return; + } - frameDaemon = monitorProxy.getFrameDaemonStatus(); - logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), - monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(), - monitorProxy.getHttpResponseMessage()); + } catch (Exception ex) { + logger.error("{}: Exception occurred when aquiring exception. Refresh loop for monitor will be skipped.", + getLogIdentifier(), ex.getCause()); + return; + } - if ((data.getHttpResponseCode() != 200) || (captureDaemon.getHttpResponseCode() != 200) - || (analysisDaemon.getHttpResponseCode() != 200) || (frameDaemon.getHttpResponseCode() != 200)) { + IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(session, getZoneMinderId()); + dataConverter.disableRefresh(); + /************************** + * + * Perform refresh of monitor data + **************************/ - if (data.getHttpResponseCode() != 200) { - logger.warn("{}: HTTP Response MonitorData: Code='{}', Message'{}'", getLogIdentifier(), - data.getHttpResponseCode(), data.getHttpResponseMessage()); + if (refreshGeneralData()) { + refreshChannels = true; + fetchMonitorGeneralData(monitorProxy); - channelMonitorStatus = ZoneMinderMonitorStatusEnum.UNKNOWN; - channelFunction = ZoneMinderMonitorFunctionEnum.NONE; - channelEnabled = false; - channelEventCause = ""; - } - if (captureDaemon.getHttpResponseCode() != 200) { - channelDaemonCapture = false; - logger.warn("{}: HTTP Response CaptureDaemon: Code='{}', Message'{}'", getLogIdentifier(), - captureDaemon.getHttpResponseCode(), captureDaemon.getHttpResponseMessage()); + fetchMonitorDaemonStatus(true, true, monitorProxy); + } + if (isLinked(ZoneMinderConstants.CHANNEL_MONITOR_STILL_IMAGE)) { + try { + if (refreshStillImage()) { + lastRefreshStillImage = System.currentTimeMillis(); + IMonitorDataStillImage monitorImage = monitorProxy.getMonitorStillImage(config.getImageScale(), + 1000, null); + logger.debug("{}: context='onFetchData' tag='image' URL='{}' ResponseCode='{}'", getLogIdentifier(), + monitorImage.getHttpRequestUrl(), monitorImage.getHttpStatus()); + + dataConverter.setMonitorStillImage(monitorImage.getImage()); } - if (analysisDaemon.getHttpResponseCode() != 200) { - channelDaemonAnalysis = false; + } catch (MalformedURLException mue) { + logger.error( + "{}: context='onFetchData' NalformedURL Exception occurred when calling to 'getMonitorStillImage'", + getLogIdentifier(), mue.getCause()); + dataConverter.setMonitorStillImage(null); + } catch (Exception ex) { + logger.error("{}: context='onFetchData' error in call to 'getMonitorStillImage'", getLogIdentifier(), + ex.getCause()); + dataConverter.setMonitorStillImage(null); + } catch (ZoneMinderException ex) { + logger.error( + "{}: context='onFetchData' error in call to 'getMonitorStillImage' ExceptionClass='{}' - Message='{}'", + getLogIdentifier(), ex.getClass().getCanonicalName(), ex.getMessage(), ex.getCause()); + } + } else { + dataConverter.setMonitorStillImage(null); - logger.warn("{}: HTTP Response AnalysisDaemon: Code='{}', Message='{}'", getLogIdentifier(), - analysisDaemon.getHttpResponseCode(), analysisDaemon.getHttpResponseMessage()); - } - if (frameDaemon.getHttpResponseCode() != 200) { - channelDaemonFrame = false; - logger.warn("{}: HTTP Response MonitorData: Code='{}', Message'{}'", getLogIdentifier(), - frameDaemon.getHttpResponseCode(), frameDaemon.getHttpResponseMessage()); - } + } - } else { - if (isConnected()) { - channelMonitorStatus = monitorProxy.getMonitorDetailedStatus(); - logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), - monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(), - monitorProxy.getHttpResponseMessage()); - - channelFunction = data.getFunction(); - channelEnabled = data.getEnabled(); - IZoneMinderEventData event = monitorProxy.getLastEvent(); - if (event != null) { - channelEventCause = event.getCause(); - } else { - channelEventCause = ""; - } + if (curRefreshPriority.isPriorityActive(RefreshPriority.PRIORITY_LOW)) - channelDaemonCapture = captureDaemon.getStatus(); - channelDaemonAnalysis = analysisDaemon.getStatus(); - channelDaemonFrame = frameDaemon.getStatus(); - } else { - channelMonitorStatus = ZoneMinderMonitorStatusEnum.UNKNOWN; - channelFunction = ZoneMinderMonitorFunctionEnum.NONE; - channelEnabled = false; - channelEventCause = ""; - channelDaemonCapture = false; - channelDaemonAnalysis = false; - channelDaemonFrame = false; + { + try { + if (dataConverter != null) { + String str = monitorProxy.getMonitorStreamingPath(config.getImageScale(), 1000, null); + dataConverter.setMonitorVideoUrl(str); } + } catch (MalformedURLException e1) { + logger.error("{}: MalformedURLException occurred when calling 'getMonitorStreamingPath()'", + getLogIdentifier(), e1.getCause()); + + } catch (ZoneMinderGeneralException zmge) { + logger.error( + "{}: context='onFetchData' error in call to 'getMonitorStreamingPath' Exception='{}', Message='{}", + getLogIdentifier(), zmge.getClass().getCanonicalName(), zmge.getMessage(), zmge.getCause()); + } catch (ZoneMinderResponseException zmre) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getMonitorStreamingPath' Exception='{}', Message='{} - Http: Status='{}', Mesage='{}'", + getLogIdentifier(), zmre.getClass().getCanonicalName(), zmre.getMessage(), zmre.getHttpStatus(), + zmre.getHttpMessage(), zmre.getCause()); + } catch (ZoneMinderAuthHashNotEnabled zmahne) { + logger.error( + "{}: context='onFetchData' error in call to 'getMonitorStreamingPath' Exception='{}', Message='{}'", + getLogIdentifier(), zmahne.getClass().getCanonicalName(), zmahne.getMessage(), + zmahne.getCause()); } - } finally { + } + + if (session != null) { releaseSession(); + session = null; } + dataConverter.enableRefresh(); + if (refreshChannels) { + logger.debug("{}: context='onFetchData' - Data has changed, channels need refreshing", getLogIdentifier()); + requestChannelRefresh(); + } + tryStopAlarmRefresh(); + } - RecalculateChannelStates(); + void fetchMonitorGeneralData(IZoneMinderMonitor proxy) { + IZoneMinderMonitor monitorProxy = proxy; + boolean doRelase = false; + if (monitorProxy == null) { + doRelase = true; + monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(), getZoneMinderId()); + } - if ((channelForceAlarm == false) && (channelAlarmedState == false) - && (DataRefreshPriorityEnum.HIGH_PRIORITY == getRefreshPriority())) { - stopPriorityRefresh(); + try { + IMonitorDataGeneral generalData = monitorProxy.getMonitorData(); + logger.debug("{}: context='onFetchData' tag='monitorData' URL='{}' ResponseCode='{}' ResponseMessage='{}'", + getLogIdentifier(), generalData.getHttpRequestUrl(), generalData.getHttpStatus(), + generalData.getHttpResponseMessage()); + + dataConverter.setMonitorGeneralData(generalData); + } catch (ZoneMinderInvalidData zmid) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getMonitorData' Exception='{}' Response='{}', Message='{}'", + getLogIdentifier(), zmid.getClass().getCanonicalName(), zmid.getResponseString(), zmid.getMessage(), + zmid.getCause()); + } catch (ZoneMinderAuthenticationException | ZoneMinderGeneralException | ZoneMinderResponseException zme) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getMonitorData' Exception='{}' Message='{}'", + getLogIdentifier(), zme.getClass().getCanonicalName(), zme.getMessage(), zme.getCause()); } + try { + ZoneMinderMonitorStatus status = monitorProxy.getMonitorDetailedStatus(); + + logger.debug( + "{}: context='onFetchData' tag='detailedStatus' URL='{}' ResponseCode='{}' ResponseMessage='{}'", + getLogIdentifier(), status.getHttpRequestUrl(), status.getHttpStatus(), + status.getHttpResponseMessage()); + + dataConverter.setMonitorDetailedStatus(status.getStatus()); + } catch (ZoneMinderInvalidData zmid) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getMonitorDetailedStatus' Exception='{}', Message='{}', Response='{}'", + getLogIdentifier(), zmid.getClass().getCanonicalName(), zmid.getMessage(), zmid.getResponseString(), + zmid.getCause()); + } catch (ZoneMinderAuthenticationException | ZoneMinderGeneralException | ZoneMinderResponseException zme) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getMonitorDetailedStatus' Exception='{}' Message='{}'", + getLogIdentifier(), zme.getClass().getCanonicalName(), zme.getMessage(), zme.getCause()); + } finally { + if (doRelase) { + releaseSession(); + } + } + lastRefreshGeneralData = System.currentTimeMillis(); } - protected State getDetailedStatus() { - State state = UnDefType.UNDEF; + void fetchMonitorDaemonStatus(boolean fetchCapture, boolean fetchAnalysisFrame, IZoneMinderMonitor proxy) { + IZoneMinderMonitor monitorProxy = proxy; + boolean fetchFrame = false; + boolean doRelase = false; + if (monitorProxy == null) { + doRelase = true; + monitorProxy = ZoneMinderFactory.getMonitorProxy(aquireSession(), getZoneMinderId()); + } try { - if (channelMonitorStatus == ZoneMinderMonitorStatusEnum.UNKNOWN) { - state = getChannelStringAsStringState(""); - } else { - state = getChannelStringAsStringState(channelMonitorStatus.toString()); + State stateCapture = UnDefType.UNDEF; + State stateAnalysis = UnDefType.UNDEF; + State stateFrame = UnDefType.UNDEF; + + IZoneMinderDaemonStatus captureDaemon = null; + IZoneMinderDaemonStatus analysisDaemon = null; + IZoneMinderDaemonStatus frameDaemon = null; + + if (isLinked(ZoneMinderConstants.CHANNEL_MONITOR_CAPTURE_DAEMON_STATE)) { + try { + if (fetchCapture) { + captureDaemon = monitorProxy.getCaptureDaemonStatus(); + logger.debug( + "{}: context='fetchMonitorDaemonStatus' tag='captureDaemon' URL='{}' ResponseCode='{}' ResponseMessage='{}'", + getLogIdentifier(), captureDaemon.getHttpRequestUrl(), captureDaemon.getHttpStatus(), + captureDaemon.getHttpResponseMessage()); + stateCapture = (captureDaemon.getStatus() ? OnOffType.ON : OnOffType.OFF); + } + } catch (ZoneMinderResponseException zmre) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getCaptureDaemonStatus' - Http: Status='{}', Message='{}', ExceptionMessage='{}', Exception='{}', Message={}'", + getLogIdentifier(), zmre.getHttpStatus(), zmre.getHttpMessage(), zmre.getExceptionMessage(), + zmre.getClass().getCanonicalName(), zmre.getMessage(), zmre.getCause()); + } catch (ZoneMinderInvalidData zmid) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getCaptureDaemonStatus' - Response='{}', Exception='{}', Message={}'", + getLogIdentifier(), zmid.getResponseString(), zmid.getClass().getCanonicalName(), + zmid.getMessage(), zmid.getCause()); + + } catch (ZoneMinderGeneralException | ZoneMinderAuthenticationException zme) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getCaptureDaemonStatus' - Exception='{}', Message={}' ", + getLogIdentifier(), zme.getClass().getCanonicalName(), zme.getMessage(), zme.getCause()); + } finally { + if (captureDaemon != null) { + dataConverter.setMonitorCaptureDaemonStatus(stateCapture); + } + + } } - } catch (Exception ex) { - logger.debug("{}", ex.getMessage()); - } + if (isLinked(ZoneMinderConstants.CHANNEL_MONITOR_ANALYSIS_DAEMON_STATE)) { + try { + stateAnalysis = UnDefType.UNDEF; + if (fetchAnalysisFrame) { + analysisDaemon = monitorProxy.getAnalysisDaemonStatus(); + logger.debug( + "{}: context='onFetchData' tag='analysisDaemon' URL='{}' ResponseCode='{}' ResponseMessage='{}'", + getLogIdentifier(), analysisDaemon.getHttpRequestUrl(), analysisDaemon.getHttpStatus(), + analysisDaemon.getHttpResponseMessage()); + + stateAnalysis = (analysisDaemon.getStatus() ? OnOffType.ON : OnOffType.OFF); + fetchFrame = true; + } - return state; + } catch (ZoneMinderResponseException zmre) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getAnalysisDaemonStatus' - Http: Status='{}', Message='{}', ExceptionMessage='{}', Exception='{}'", + getLogIdentifier(), zmre.getHttpStatus(), zmre.getHttpMessage(), zmre.getExceptionMessage(), + zmre.getClass().getCanonicalName(), zmre.getCause()); + } catch (ZoneMinderInvalidData zmid) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getAnalysisDaemonStatus' - Response='{}', Exception='{}'", + getLogIdentifier(), zmid.getResponseString(), zmid.getClass().getCanonicalName(), + zmid.getCause()); + + } catch (ZoneMinderGeneralException | ZoneMinderAuthenticationException zme) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getAnalysisDaemonStatus' - Exception='{}' ", + getLogIdentifier(), zme.getClass().getCanonicalName(), zme.getCause()); + } catch (Exception ex) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' tag='exception' error in call to 'getAnalysisDaemonStatus' - Exception='{}'", + getLogIdentifier(), ex.getClass().getCanonicalName(), ex); + } finally { + dataConverter.setMonitorAnalysisDaemonStatus(stateAnalysis); + } + } + + if (isLinked(ZoneMinderConstants.CHANNEL_MONITOR_FRAME_DAEMON_STATE)) { + try { + stateFrame = UnDefType.UNDEF; + if ((fetchFrame) && frameDaemonActive) { + frameDaemon = monitorProxy.getFrameDaemonStatus(); + logger.debug( + "{}: context='fetchMonitorDaemonStatus' tag='frameDaemon' URL='{}' ResponseCode='{}' ResponseMessage='{}'", + getLogIdentifier(), frameDaemon.getHttpRequestUrl(), frameDaemon.getHttpStatus(), + frameDaemon.getHttpResponseMessage()); + + if (frameDaemon != null) { + stateFrame = ((frameDaemon.getStatus() && analysisDaemon.getStatus()) ? OnOffType.ON + : OnOffType.OFF); + } + } + } catch (ZoneMinderResponseException zmre) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getFrameDaemonStatus' - Http: Status='{}', Message='{}', ExceptionMessage'{}', Exception='{}'", + getLogIdentifier(), zmre.getHttpStatus(), zmre.getHttpMessage(), zmre.getExceptionMessage(), + zmre.getClass().getCanonicalName(), zmre.getCause()); + } catch (ZoneMinderInvalidData zmid) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getFrameDaemonStatus' - Response='{}', Exception='{}'", + getLogIdentifier(), zmid.getResponseString(), zmid.getClass().getCanonicalName(), + zmid.getCause()); + + } catch (ZoneMinderGeneralException | ZoneMinderAuthenticationException zme) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' error in call to 'getFrameDaemonStatus' - Exception='{}'", + getLogIdentifier(), zme.getClass().getCanonicalName(), zme.getCause()); + } catch (Exception ex) { + logger.error( + "{}: context='fetchMonitorDaemonStatus' tag='exception' error in call to 'getFrameDaemonStatus' - Exception='{}'", + getLogIdentifier(), ex.getClass().getCanonicalName(), ex); + } finally { + dataConverter.setMonitorFrameDaemonStatus(stateFrame); + } + } + + } finally { + if (doRelase) { + releaseSession(); + } + } } /* * This is experimental * Try to add different properties */ - private void updateMonitorProperties(IZoneMinderSession session) { + private void updateMonitorProperties() { logger.debug("{}: Update Monitor Properties", getLogIdentifier()); // Update property information about this device Map properties = editProperties(); - IZoneMinderMonitor monitorProxy = ZoneMinderFactory.getMonitorProxy(session, getZoneMinderId()); - IZoneMinderMonitorData monitorData = monitorProxy.getMonitorData(); - logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), - monitorProxy.getHttpUrl(), monitorProxy.getHttpResponseCode(), monitorProxy.getHttpResponseMessage()); + IZoneMinderMonitor monitorProxy = null; + IMonitorDataGeneral monitorData = null; + IZoneMinderConnectionHandler session = null; + try { + session = aquireSession(); + + if (session == null) { + logger.error("{}: context='updateMonitorProperties' Unable to aquire session.", getLogIdentifier()); + return; + } + monitorProxy = ZoneMinderFactory.getMonitorProxy(session, getZoneMinderId()); + monitorData = monitorProxy.getMonitorData(); + logger.debug("{}: URL='{}' ResponseCode='{}' ResponseMessage='{}'", getLogIdentifier(), + monitorData.getHttpRequestUrl(), monitorData.getHttpStatus(), monitorData.getHttpResponseMessage()); - properties.put(ZoneMinderProperties.PROPERTY_ID, getLogIdentifier()); - properties.put(ZoneMinderProperties.PROPERTY_MONITOR_NAME, monitorData.getName()); + } catch (Exception e) { + logger.error("{}: Exception occurred when updating monitor properties - Message:{}", getLogIdentifier(), + e.getMessage()); - properties.put(ZoneMinderProperties.PROPERTY_MONITOR_SOURCETYPE, monitorData.getSourceType().name()); + } catch (ZoneMinderException ex) { + logger.error( + "{}: context='onFetchData' error in call to 'getMonitorData' ExceptionClass='{}' - Message='{}'", + getLogIdentifier(), ex.getClass().getCanonicalName(), ex.getMessage(), ex.getCause()); + } finally { + if (session != null) { + releaseSession(); + } + } - properties.put(ZoneMinderProperties.PROPERTY_MONITOR_ANALYSIS_FPS, monitorData.getAnalysisFPS()); - properties.put(ZoneMinderProperties.PROPERTY_MONITOR_MAXIMUM_FPS, monitorData.getMaxFPS()); - properties.put(ZoneMinderProperties.PROPERTY_MONITOR_ALARM_MAXIMUM, monitorData.getAlarmMaxFPS()); + if (monitorData != null) { + properties.put(ZoneMinderProperties.PROPERTY_ID, getLogIdentifier()); + properties.put(ZoneMinderProperties.PROPERTY_NAME, monitorData.getName()); - properties.put(ZoneMinderProperties.PROPERTY_MONITOR_IMAGE_WIDTH, monitorData.getWidth()); - properties.put(ZoneMinderProperties.PROPERTY_MONITOR_IMAGE_HEIGHT, monitorData.getHeight()); + properties.put(ZoneMinderProperties.PROPERTY_MONITOR_SOURCETYPE, monitorData.getSourceType().name()); + properties.put(ZoneMinderProperties.PROPERTY_MONITOR_ANALYSIS_FPS, monitorData.getAnalysisFPS()); + properties.put(ZoneMinderProperties.PROPERTY_MONITOR_MAXIMUM_FPS, monitorData.getMaxFPS()); + properties.put(ZoneMinderProperties.PROPERTY_MONITOR_ALARM_MAXIMUM, monitorData.getAlarmMaxFPS()); + + properties.put(ZoneMinderProperties.PROPERTY_MONITOR_IMAGE_WIDTH, monitorData.getWidth()); + properties.put(ZoneMinderProperties.PROPERTY_MONITOR_IMAGE_HEIGHT, monitorData.getHeight()); + } // Must loop over the new properties since we might have added data boolean update = false; Map originalProperties = editProperties(); for (String property : properties.keySet()) { if ((originalProperties.get(property) == null - || originalProperties.get(property).equals(properties.get(property)) == false)) { + || !originalProperties.get(property).equals(properties.get(property)))) { update = true; break; } } - if (update == true) { - logger.debug("{}: Properties synchronised", getLogIdentifier()); + if (update) { + logger.debug("{}: context='updateMonitorProperties' Properties synchronised", getLogIdentifier()); updateProperties(properties); } } @@ -893,14 +1194,28 @@ public String getLogIdentifier() { try { if (config != null) { - result = String.format("[MONITOR-%s]", config.getZoneMinderId().toString()); } } catch (Exception ex) { result = "[MONITOR]"; } - return result; } + + @Override + public void onStateChanged(ChannelUID channelUID, State state) { + logger.debug("{}: context='onStateChanged' channel='{}' - State changed to '{}'", getLogIdentifier(), + channelUID.getAsString(), state.toString()); + updateState(channelUID.getId(), state); + } + + @Override + public void onRefreshDisabled() { + } + + @Override + public void onRefreshEnabled() { + } + } diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderThingType.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderThingType.java index b8771a14a021f..a3c950154efdc 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderThingType.java +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/handler/ZoneMinderThingType.java @@ -11,7 +11,7 @@ /** * Enumerator for each Bridge and Thing * - * @author Martin S. Eskildsen + * @author Martin S. Eskildsen - Initial contribution */ public enum ZoneMinderThingType { ZoneMinderServerBridge, diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/DataRefreshPriorityEnum.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/DataRefreshPriorityEnum.java deleted file mode 100644 index ff968f1bfc42d..0000000000000 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/DataRefreshPriorityEnum.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2010-2018 by the respective copyright holders. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.openhab.binding.zoneminder.internal; - -/** - * - * @author Martin S. Eskildsen - Initial contribution - */ -public enum DataRefreshPriorityEnum { - SCHEDULED, - HIGH_PRIORITY; -} diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/RefreshPriority.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/RefreshPriority.java new file mode 100644 index 0000000000000..abace17b0fd5f --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/RefreshPriority.java @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.zoneminder.internal; + +import java.util.HashMap; +import java.util.Map; + +import org.openhab.binding.zoneminder.ZoneMinderConstants; + +/** + * + * @author Martin S. Eskildsen - Initial contribution + */ +public enum RefreshPriority { + PRIORITY_BATCH(1), + PRIORITY_LOW(2), + PRIORITY_NORMAL(3), + PRIORITY_HIGH(4), + PRIORITY_ALARM(10), + DISABLED(0), + UNKNOWN(-1); + + private int value; + private static Map map = new HashMap<>(); + + private RefreshPriority(int value) { + this.value = value; + } + + public static RefreshPriority valueOf(int pageType) { + return (RefreshPriority) map.get(pageType); + } + + public int getValue() { + return value; + } + + public static RefreshPriority fromConfigValue(String value) { + if (value.equalsIgnoreCase(ZoneMinderConstants.CONFIG_VALUE_REFRESH_DISABLED)) { + return DISABLED; + } else if (value.equalsIgnoreCase(ZoneMinderConstants.CONFIG_VALUE_REFRESH_BATCH)) { + return RefreshPriority.PRIORITY_BATCH; + } else if (value.equalsIgnoreCase(ZoneMinderConstants.CONFIG_VALUE_REFRESH_LOW)) { + return RefreshPriority.PRIORITY_LOW; + } else if (value.equalsIgnoreCase(ZoneMinderConstants.CONFIG_VALUE_REFRESH_NORMAL)) { + return RefreshPriority.PRIORITY_NORMAL; + } else if (value.equalsIgnoreCase(ZoneMinderConstants.CONFIG_VALUE_REFRESH_HIGH)) { + return RefreshPriority.PRIORITY_HIGH; + } else if (value.equalsIgnoreCase(ZoneMinderConstants.CONFIG_VALUE_REFRESH_ALARM)) { + return RefreshPriority.PRIORITY_ALARM; + } + return UNKNOWN; + + } + + public boolean isEqual(RefreshPriority refrenceVal) { + if (value == refrenceVal.getValue()) { + return true; + } + return false; + } + + public boolean isLessThan(RefreshPriority refrenceVal) { + if (value < refrenceVal.getValue()) { + return true; + } + return false; + } + + public boolean isGreaterThan(RefreshPriority refrenceVal) { + if (value > refrenceVal.getValue()) { + return true; + } + return false; + } + + public boolean isPriorityActive(RefreshPriority refrenceVal) { + if (value <= refrenceVal.getValue()) { + return true; + } + return false; + } +} diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderConnectionStatus.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderConnectionStatus.java new file mode 100644 index 0000000000000..74d3128409101 --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/ZoneMinderConnectionStatus.java @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ + +package org.openhab.binding.zoneminder.internal; + +import java.util.HashMap; +import java.util.Map; + +/** + * The {@link ZoneMinderConnectionStatus} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Martin S. Eskildsen - Initial contribution + */ +public enum ZoneMinderConnectionStatus { + GENERAL_ERROR(-99), + BINDING_CONFIG_INVALID(-98), + BINDING_CONNECTION_INVALID(-97), + BINDING_SESSION_INVALID(-96), + + SERVER_CREDENTIALS_INVALID(-89), + SERVER_API_DISABLED(-88), + SERVER_OPT_TRIGGERS_DISABLED(-86), + ERROR_RECOVERABLE(-20), + SERVER_DAEMON_NOT_RUNNING(-19), + BINDING_CONNECTION_TIMEOUT(-1), + UNINITIALIZED(0), + BINDING_CONFIG_LOAD_PASSED(1), + BINDING_CONFIG_VALIDATE_PASSED(2), + ZONEMINDER_CONNECTION_CREATED(3), + ZONEMINDER_API_ACCESS_PASSED(4), + ZONEMINDER_SESSION_CREATED(5), + ZONEMINDER_SERVER_CONFIG_PASSED(6), + INITIALIZED(10); + + private int value; + private static Map map = new HashMap<>(); + + private ZoneMinderConnectionStatus(int value) { + this.value = value; + } + + public static ZoneMinderConnectionStatus valueOf(int pageType) { + return (ZoneMinderConnectionStatus) map.get(pageType); + } + + public boolean lessThan(ZoneMinderConnectionStatus reference) { + return (getValue() < reference.getValue()) ? true : false; + } + + public boolean greatherThan(ZoneMinderConnectionStatus reference) { + return (getValue() > reference.getValue()) ? true : false; + } + + public boolean greatherThanEqual(ZoneMinderConnectionStatus reference) { + return (getValue() >= reference.getValue()) ? true : false; + } + + public boolean isErrorState() { + return lessThan(UNINITIALIZED); + } + + public boolean hasUnrecoverableError() { + return lessThan(ERROR_RECOVERABLE); + } + + public boolean hasPassed(ZoneMinderConnectionStatus reference) { + return greatherThanEqual(reference); + } + + public int getValue() { + return value; + } + + public boolean hasRecoverableError() { + return isErrorState() && !hasUnrecoverableError(); + } +} diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderBridgeServerConfig.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderBridgeServerConfig.java index c7190eaa07fac..c8a7287eef0d3 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderBridgeServerConfig.java +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderBridgeServerConfig.java @@ -9,63 +9,70 @@ package org.openhab.binding.zoneminder.internal.config; import org.openhab.binding.zoneminder.ZoneMinderConstants; +import org.openhab.binding.zoneminder.internal.RefreshPriority; /** * Configuration data according to zoneminderserver.xml * - * @author Martin S. Eskildsen + * @author Martin S. Eskildsen - Initial contribution * */ public class ZoneMinderBridgeServerConfig extends ZoneMinderConfig { - private String hostname; - private Integer http_port; - private Integer telnet_port; + private Integer portHttp; + private Integer portTelnet; private String protocol; + private String host; - private String urlpath; + private String urlSite; + private String urlApi; private String user; private String password; - private Integer refresh_interval; - private Integer refresh_interval_disk_usage; - private Boolean autodiscover_things; + private Integer refreshNormal; + private Integer refreshLow; + private String diskUsageRefresh; + private Boolean autodiscover; + + private Boolean useSpecificUserStreaming; + private String streamingUser; + private String streamingPassword; @Override public String getConfigId() { return ZoneMinderConstants.BRIDGE_ZONEMINDER_SERVER; } - public String getHostName() { - return hostname; + public String getHost() { + return host; } - public void setHostName(String hostName) { - this.hostname = hostName; + public void setHostName(String host) { + this.host = host; } public Integer getHttpPort() { - if ((http_port == null) || (http_port == 0)) { + if ((portHttp == null) || (portHttp == 0)) { if (getProtocol().equalsIgnoreCase("http")) { - http_port = 80; + portHttp = 80; } else { - http_port = 443; + portHttp = 443; } } - return http_port; + return portHttp; } public void setHttpPort(Integer port) { - this.http_port = port; + this.portHttp = port; } public Integer getTelnetPort() { - return telnet_port; + return portTelnet; } public void setTelnetPort(Integer telnetPort) { - this.telnet_port = telnetPort; + this.portTelnet = telnetPort; } public String getProtocol() { @@ -77,11 +84,19 @@ public void setProtocol(String protocol) { } public String getServerBasePath() { - return urlpath; + return urlSite; } public void setServerBasePath(String urlpath) { - this.urlpath = urlpath; + this.urlSite = urlpath; + } + + public String getServerApiPath() { + return urlApi; + } + + public void setServerApiPath(String apiPath) { + this.urlApi = apiPath; } public String getUserName() { @@ -100,28 +115,49 @@ public void setPassword(String password) { this.password = password; } - public Integer getRefreshInterval() { - return refresh_interval; + public Integer getRefreshIntervalNormal() { + return this.refreshNormal; } - public void setRefreshInterval(Integer refreshInterval) { - this.refresh_interval = refreshInterval; + public void setRefreshIntervalNormal(Integer refreshInterval) { + this.refreshNormal = refreshInterval; } - public Integer getRefreshIntervalLowPriorityTask() { - return refresh_interval_disk_usage; + public Integer getRefreshIntervalLow() { + return this.refreshLow; } - public void setRefreshIntervalDiskUsage(Integer refreshIntervalDiskUsage) { - this.refresh_interval_disk_usage = refreshIntervalDiskUsage; + public void setRefreshIntervalDiskUsage(Integer refreshInterval) { + this.refreshLow = refreshInterval; } public Boolean getAutodiscoverThings() { - return autodiscover_things; + return autodiscover; } public void setAutodiscoverThings(Boolean autodiscoverThings) { - this.autodiscover_things = autodiscoverThings; + this.autodiscover = autodiscoverThings; + } + + public RefreshPriority getDiskUsageRefresh() { + return getRefreshPriorityEnum(diskUsageRefresh); } + public Boolean getUseSpecificUserStreaming() { + return useSpecificUserStreaming; + } + + public String getStreamingUser() { + if (!getUseSpecificUserStreaming()) { + return getUserName(); + } + return streamingUser; + } + + public String getStreamingPassword() { + if (!getUseSpecificUserStreaming()) { + return getPassword(); + } + return streamingPassword; + } } diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderConfig.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderConfig.java index 0ca0820dd3ec4..50e3cf3cd3d89 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderConfig.java +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderConfig.java @@ -8,11 +8,21 @@ */ package org.openhab.binding.zoneminder.internal.config; +import org.openhab.binding.zoneminder.internal.RefreshPriority; + /** * base class containing Configuration in openHAB * - * @author Martin S. Eskildsen + * @author Martin S. Eskildsen - Initial contribution */ public abstract class ZoneMinderConfig { public abstract String getConfigId(); + + protected RefreshPriority getRefreshPriorityEnum(String configValue) { + RefreshPriority priority = RefreshPriority.fromConfigValue(configValue); + if (priority != RefreshPriority.UNKNOWN) { + return priority; + } + return RefreshPriority.UNKNOWN; + } } diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderThingMonitorConfig.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderThingMonitorConfig.java index e702fa64f9aa1..b08b0307dbe20 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderThingMonitorConfig.java +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/config/ZoneMinderThingMonitorConfig.java @@ -9,6 +9,7 @@ package org.openhab.binding.zoneminder.internal.config; import org.openhab.binding.zoneminder.ZoneMinderConstants; +import org.openhab.binding.zoneminder.internal.RefreshPriority; /** * Specific configuration class for Monitor COnfig. @@ -18,7 +19,19 @@ public class ZoneMinderThingMonitorConfig extends ZoneMinderThingConfig { // Parameters - private Integer monitorId; + private Integer id; + private Integer triggerTimeout; + private String eventText; + private String imageRefreshIdle; + private String imageRefreshEvent; + // private String daemonRefresh; + private Integer imageScale; + + // private Integer max_image_size; + + // private Boolean enable_image_updates; + // private String video_encoding; + // private Integer video_framerate; @Override public String getConfigId() { @@ -26,11 +39,64 @@ public String getConfigId() { } public String getId() { - return monitorId.toString(); + return id.toString(); } @Override public String getZoneMinderId() { - return monitorId.toString(); + return id.toString(); } -} + + public RefreshPriority getImageRefreshIdle() { + return getRefreshPriorityEnum(imageRefreshIdle); + } + + public RefreshPriority getImageRefreshEvent() { + return getRefreshPriorityEnum(imageRefreshEvent); + } + + /* + * public RefreshPriority getDaemonRefresh() { + * return getRefreshPriorityEnum(daemonRefresh); + * } + */ + public Integer getImageScale() { + return imageScale; + } + + /* + * public Integer getMaxImageSize() { + * return max_image_size; + * } + * + * public void setMaxImageSize(Integer size) { + * this.max_image_size = size; + * } + */ + /* + * public Boolean getEnableImageUpdates() { + * return enable_image_updates; + * } + * + * public void setEnableImageUpdates(Boolean enable_image_updates) { + * this.enable_image_updates = enable_image_updates; + * } + */ + /* + * public String getVideoEncoding() { + * return video_encoding; + * } + * + * public void setVideoEncoding(String video_encoding) { + * this.video_encoding = video_encoding; + * } + * + * public Integer getVideoFramerate() { + * return video_framerate; + * } + * + * public void setVideoFramerate(Integer video_framerate) { + * this.video_framerate = video_framerate; + * } + * + */} diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/discovery/ZoneMinderDiscoveryService.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/discovery/ZoneMinderDiscoveryService.java index bf62a4fb14d6a..cb22a90b0f2ac 100644 --- a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/discovery/ZoneMinderDiscoveryService.java +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/discovery/ZoneMinderDiscoveryService.java @@ -25,7 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import name.eskildsen.zoneminder.IZoneMinderMonitorData; +import name.eskildsen.zoneminder.data.IMonitorDataGeneral; /** * @@ -65,12 +65,15 @@ public Set getSupportedThingTypes() { @Override public void startBackgroundDiscovery() { logger.debug("[DISCOVERY]: Performing background discovery scan for {}", serverHandler.getThing().getUID()); + + // removeOlderResults(getTimestampOfLastScan()); discoverMonitors(); } @Override public void startScan() { logger.debug("[DISCOVERY]: Starting discovery scan for {}", serverHandler.getThing().getUID()); + discoverMonitors(); } @@ -84,55 +87,67 @@ protected synchronized void stopScan() { super.stopScan(); } - protected String BuildMonitorLabel(String id, String name) { + private String buildMonitorLabel(String id, String name) { return String.format("%s [%s]", ZoneMinderConstants.ZONEMINDER_MONITOR_NAME, name); } protected synchronized void discoverMonitors() { - // Add all existing devices - for (IZoneMinderMonitorData monitor : serverHandler.getMonitors()) { - deviceAdded(monitor); + for (IMonitorDataGeneral monitorData : serverHandler.getMonitors()) { + DiscoveryResult curDiscoveryResult = null; + ThingUID thingUID = getMonitorThingUID(monitorData); + + // Avoid issue #5143 in Eclipse SmartHome + DiscoveryResult existingResult = discoveryServiceCallback.getExistingDiscoveryResult(thingUID); + if ((existingResult != null) && (existingResult.getThingUID() != thingUID)) { + existingResult = null; + } + + if (existingResult != null) { + logger.debug("[DISCOVERY]: Monitor with Id='{}' and Name='{}' with ThingUID='{}' already discovered", + monitorData.getId(), monitorData.getName(), thingUID); + + } else if (discoveryServiceCallback.getExistingThing(thingUID) != null) { + logger.debug("[DISCOVERY]: Monitor with Id='{}' and Name='{}' with ThingUID='{}' already added", + monitorData.getId(), monitorData.getName(), thingUID); + } else { + curDiscoveryResult = createMonitorDiscoveryResult(thingUID, monitorData); + + } + + if (curDiscoveryResult != null) { + logger.info("[DISCOVERY]: Monitor with Id='{}' and Name='{}' added to Inbox with ThingUID='{}'", + monitorData.getId(), monitorData.getName(), thingUID); + thingDiscovered(curDiscoveryResult); + } } } - private boolean monitorThingExists(ThingUID newThingUID) { - return serverHandler.getThingByUID(newThingUID) != null ? true : false; - } + private ThingUID getMonitorThingUID(IMonitorDataGeneral monitor) { + ThingUID bridgeUID = serverHandler.getThing().getUID(); + String monitorUID = String.format("%s-%s", ZoneMinderConstants.THING_ZONEMINDER_MONITOR, monitor.getId()); + + return new ThingUID(ZoneMinderConstants.THING_TYPE_THING_ZONEMINDER_MONITOR, bridgeUID, monitorUID); - /** - * This is called once the node is fully discovered. At this point we know most of the information about - * the device including manufacturer information. - * - * @param node the node to be added - */ + } - public void deviceAdded(IZoneMinderMonitorData monitor) { + protected DiscoveryResult createMonitorDiscoveryResult(ThingUID monitorUID, IMonitorDataGeneral monitorData) { try { ThingUID bridgeUID = serverHandler.getThing().getUID(); - String monitorUID = String.format("%s-%s", ZoneMinderConstants.THING_ZONEMINDER_MONITOR, monitor.getId()); - ThingUID thingUID = new ThingUID(ZoneMinderConstants.THING_TYPE_THING_ZONEMINDER_MONITOR, bridgeUID, - monitorUID); - - // Does Monitor exist? - if (!monitorThingExists(thingUID)) { - logger.info("[DISCOVERY]: Monitor with Id='{}' and Name='{}' added", monitor.getId(), - monitor.getName()); - Map properties = new HashMap<>(0); - properties.put(ZoneMinderConstants.PARAMETER_MONITOR_ID, Integer.valueOf(monitor.getId())); - properties.put(ZoneMinderConstants.PARAMETER_MONITOR_TRIGGER_TIMEOUT, - ZoneMinderConstants.PARAMETER_MONITOR_TRIGGER_TIMEOUT_DEFAULTVALUE); - properties.put(ZoneMinderConstants.PARAMETER_MONITOR_EVENTTEXT, - ZoneMinderConstants.MONITOR_EVENT_OPENHAB); - - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties) - .withBridge(bridgeUID).withLabel(BuildMonitorLabel(monitor.getId(), monitor.getName())).build(); - - thingDiscovered(discoveryResult); - } + + Map properties = new HashMap<>(0); + properties.put(ZoneMinderConstants.PARAMETER_MONITOR_ID, Integer.valueOf(monitorData.getId())); + properties.put(ZoneMinderConstants.PARAMETER_MONITOR_TRIGGER_TIMEOUT, + ZoneMinderConstants.PARAMETER_MONITOR_TRIGGER_TIMEOUT_DEFAULTVALUE); + properties.put(ZoneMinderConstants.PARAMETER_MONITOR_EVENTTEXT, ZoneMinderConstants.MONITOR_EVENT_OPENHAB); + + return DiscoveryResultBuilder.create(monitorUID).withProperties(properties).withBridge(bridgeUID) + .withLabel(buildMonitorLabel(monitorData.getId(), monitorData.getName())).build(); + } catch (Exception ex) { - logger.error("[DISCOVERY]: Error occurred when calling 'monitorAdded' from Discovery. Exception={}", - ex.getMessage()); + logger.error( + "[DISCOVERY]: Error occurred when calling 'monitorAdded' from Discovery. Id='{}', Name='{}', ThingUID='{}'", + monitorData.getId(), monitorData.getName(), monitorUID, ex.getCause()); } - + return null; } } diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelOnOffType.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelOnOffType.java new file mode 100644 index 0000000000000..ceb3f4bf7b599 --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelOnOffType.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ + +package org.openhab.binding.zoneminder.internal.state; + +import javax.activation.UnsupportedDataTypeException; + +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; + +/** + * The {@link ChannelOnOffType} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Martin S. Eskildsen - Initial contribution + */ + +public class ChannelOnOffType extends GenericChannelState { + + protected ChannelOnOffType(ChannelUID channelUID, GenericThingState thing, + ChannelStateChangeSubscriber subscriber) { + super(channelUID, thing, subscriber); + } + + @Override + protected State convert(Object _state) throws UnsupportedDataTypeException { + State newState = UnDefType.UNDEF; + if (_state instanceof String) { + String value = (String) _state; + if (((String) _state).equalsIgnoreCase("ON")) { + newState = OnOffType.ON; + } else if (((String) _state).equalsIgnoreCase("OFF")) { + newState = OnOffType.OFF; + } else { + throw new UnsupportedDataTypeException(); + } + } else if (_state instanceof Boolean) { + newState = ((Boolean) _state) ? OnOffType.ON : OnOffType.OFF; + } else if (_state instanceof OnOffType) { + newState = (OnOffType) _state; + } else if (_state instanceof UnDefType) { + newState = (UnDefType) _state; + } else { + throw new UnsupportedDataTypeException(); + } + return newState; + } + +} diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelRawType.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelRawType.java new file mode 100644 index 0000000000000..d776e6359b9a1 --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelRawType.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.zoneminder.internal.state; + +import java.io.ByteArrayOutputStream; + +import javax.activation.UnsupportedDataTypeException; + +import org.eclipse.smarthome.core.library.types.RawType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; + +/** + * The {@link ChannelRawType} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Martin S. Eskildsen - Initial contribution + */ +public class ChannelRawType extends GenericChannelState { + + protected ChannelRawType(ChannelUID channelUID, GenericThingState thing, ChannelStateChangeSubscriber subscriber) { + super(channelUID, thing, subscriber); + } + + @Override + protected State convert(Object _state) throws UnsupportedDataTypeException { + State newState = UnDefType.UNDEF; + + State state = UnDefType.UNDEF; + if (_state instanceof ByteArrayOutputStream) { + // ByteArrayOutputStream baos = (ByteArrayOutputStream) _state; + newState = new RawType(((ByteArrayOutputStream) _state).toByteArray(), "image/jpeg"); + } + + else if (_state instanceof RawType) { + newState = (RawType) _state; + } else if (_state instanceof UnDefType) { + newState = (UnDefType) _state; + } else { + throw new UnsupportedDataTypeException(); + } + return newState; + + } + +} diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelState.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelState.java new file mode 100644 index 0000000000000..d1a25e9c8a7fa --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelState.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ + +package org.openhab.binding.zoneminder.internal.state; + +import javax.activation.UnsupportedDataTypeException; + +import org.eclipse.smarthome.core.types.State; + +/** + * The {@link ChannelState} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Martin S. Eskildsen - Initial contribution + */ +interface ChannelState { + + public void subscribe(); + + public void unsubscribe(); + + public State getState(); + + // private State statePublished = UnDefType.NULL; + // private Type dataType = UnDefType.class; + + public void setState(Object state) throws UnsupportedDataTypeException; + + public void setState(Object state, boolean update) throws UnsupportedDataTypeException; + +} diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelStateChangePublisher.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelStateChangePublisher.java new file mode 100644 index 0000000000000..a1e2778768ac9 --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelStateChangePublisher.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.zoneminder.internal.state; + +import org.eclipse.smarthome.core.thing.ChannelUID; + +/** + * The {@link GenericThingState} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Martin S. Eskildsen - Initial contribution + */ +public interface ChannelStateChangePublisher { + /* + * public void subscribeStringType(ChannelUID channelUID, ChannelStateChangeSubscriber subscriber); + * + * public void subscribeOnOffType(ChannelUID channelUID, ChannelStateChangeSubscriber subscriber); + * + * public void subscribeNumberType(ChannelUID channelUID, ChannelStateChangeSubscriber subscriber); + */ + // public void subscribeRawType(ChannelUID channelUID, ChannelStateChangeSubscriber subscriber); + + public GenericChannelState createChannelSubscription( + ChannelUID channelUID /* + * , + * ChannelStateChangeSubscriber subscriber + */); + + public void addChannel(ChannelUID channelUID/* , ChannelStateChangeSubscriber subscriber */); + + public void subscribe(ChannelUID channelUID/* , ChannelStateChangeSubscriber subscriber */); + + public void unsubscribe(ChannelUID channelUID); + + public void disableRefresh(); + + public void enableRefresh(); + + public boolean allowRefresh(); +} diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelStateChangeSubscriber.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelStateChangeSubscriber.java new file mode 100644 index 0000000000000..a88fb59dd3d86 --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelStateChangeSubscriber.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.zoneminder.internal.state; + +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; + +/** + * The {@link GenericThingState} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Martin S. Eskildsen - Initial contribution + */ +public interface ChannelStateChangeSubscriber { + void onStateChanged(ChannelUID channelUID, State state); + + void onRefreshDisabled(); + + void onRefreshEnabled(); +} diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelStringType.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelStringType.java new file mode 100644 index 0000000000000..e96e512344bb6 --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/ChannelStringType.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.zoneminder.internal.state; + +import javax.activation.UnsupportedDataTypeException; + +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; + +/** + * The {@link GenericThingState} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Martin S. Eskildsen - Initial contribution + */ +public class ChannelStringType extends GenericChannelState { + + protected ChannelStringType(ChannelUID channelUID, GenericThingState thing, + ChannelStateChangeSubscriber subscriber) { + super(channelUID, thing, subscriber); + } + + @Override + protected State convert(Object _state) throws UnsupportedDataTypeException { + State newState = UnDefType.UNDEF; + + if (_state instanceof String) { + newState = new StringType((String) _state); + } else if (_state instanceof StringType) { + newState = (StringType) _state; + + } else if (_state instanceof UnDefType) { + newState = (UnDefType) _state; + } else { + throw new UnsupportedDataTypeException(); + } + return newState; + } + +} diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/GenericChannelState.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/GenericChannelState.java new file mode 100644 index 0000000000000..a0fb1ed407fcd --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/GenericChannelState.java @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.zoneminder.internal.state; + +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.activation.UnsupportedDataTypeException; + +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; + +/** + * The {@link GenericThingState} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Martin S. Eskildsen - Initial contribution + */ +public abstract class GenericChannelState implements ChannelState { + + private AtomicBoolean isDirty = new AtomicBoolean(false); + + private ChannelUID channelUID; + private GenericThingState thing = null; + private ChannelStateChangeSubscriber subscriber = null; + private State state = UnDefType.NULL; + private int countSubscription = 0; + + protected GenericChannelState(ChannelUID channelUID, GenericThingState thing, + ChannelStateChangeSubscriber subscriber) { + this.channelUID = channelUID; + this.thing = thing; + this.subscriber = subscriber; + } + + @Override + public void subscribe() { + countSubscription++; + } + + @Override + public void unsubscribe() { + if (countSubscription > 0) { + countSubscription--; + } + + } + + @Override + public State getState() { + return state; + } + + @Override + public void setState(Object objState) throws UnsupportedDataTypeException { + setState(objState, true); + } + + @Override + public void setState(Object objState, boolean update) throws UnsupportedDataTypeException { + State newState = convert(objState); + boolean changed = false; + + if (!(state.toString().equals(newState.toString()))) { + changed = true; + state = newState; + // Set Dirty flag + setDirtyFlag(); + + thing.onChannelChanged(channelUID); + } + + if (update && changed) { + // Try to udpate + flushChanges(); + } + } + + public void flushChanges() { + flushChanges(false); + } + + public void flushChanges(boolean force) { + if (!isDirty.get() && !force) { + return; + } + + if (thing.allowRefresh() || force) { + subscriber.onStateChanged(channelUID, state); + // Clear dirtyflag + clearDirtyFlag(); + } + } + + public void setDirtyFlag() { + isDirty.lazySet(true); + } + + public void clearDirtyFlag() { + isDirty.set(false); + } + + protected abstract State convert(Object state) throws UnsupportedDataTypeException; + +} diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/GenericThingState.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/GenericThingState.java new file mode 100644 index 0000000000000..bcfd201499dda --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/GenericThingState.java @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ + +package org.openhab.binding.zoneminder.internal.state; + +import java.io.ByteArrayOutputStream; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.RawType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link GenericThingState} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Martin S. Eskildsen - Initial contribution + */ +public abstract class GenericThingState implements ChannelStateChangePublisher { + + private Logger logger = LoggerFactory.getLogger(GenericThingState.class); + + final AtomicBoolean allowRefresh = new AtomicBoolean(true); + + private ChannelStateChangeSubscriber subscriber = null; + + // Keeps track of current channel refresh status of subscribed channels + private Map subscriptions = new ConcurrentHashMap(); + + public GenericThingState(ChannelStateChangeSubscriber subscriber) { + this.subscriber = subscriber; + } + + protected abstract void recalculate(); + + public void onChannelChanged(ChannelUID channelUID) { + } + + @Override + public void addChannel(ChannelUID channelUID) { + try { + if (!subscriptions.containsKey(channelUID.getId())) { + subscriptions.put(channelUID.getId(), createChannelSubscription(channelUID)); + } + } catch ( + + Exception ex) { + logger.error("{}: context='subscribe' - Exception occurred when subscribing to channel '{}'", "", + channelUID.getId()); + } + } + + protected GenericChannelState createSubscriptionStringType(ChannelUID channelUID) { + return new ChannelStringType(channelUID, this, subscriber); + + } + + protected GenericChannelState createSubscriptionOnOffType(ChannelUID channelUID) { + return new ChannelOnOffType(channelUID, this, subscriber); + } + + protected GenericChannelState getChannelStateHandler(String channelId) { + return subscriptions.get(channelId); + + } + + public GenericChannelState getChannelStateHandler(ChannelUID channelUID) { + return getChannelStateHandler(channelUID.getId()); + + } + + protected GenericChannelState createSubscriptionRawType(ChannelUID channelUID) { + return new ChannelRawType(channelUID, this, subscriber); + } + + @Override + public void subscribe(ChannelUID channelUID/* , ChannelStateChangeSubscriber subscriber */) { + try { + if (getChannelStateHandler(channelUID) == null) { + addChannel(channelUID/* , subscriber */); + } + getChannelStateHandler(channelUID).subscribe(); + + } catch ( + + Exception ex) { + logger.error("{}: context='subscribe' - Exception occurred when subscribing to channel '{}'", "", + channelUID.getId(), ex); + } + } + + @Override + public void unsubscribe(ChannelUID channelUID) { + try { + if (getChannelStateHandler(channelUID) != null) { + getChannelStateHandler(channelUID).subscribe(); + } + } catch (Exception ex) { + logger.error("{}: context='unsubscribe' - Exception occurred when subscribing to channel '{}'", "", + channelUID.getId()); + } + } + + @Override + public void disableRefresh() { + allowRefresh.compareAndSet(true, false); + } + + @Override + public void enableRefresh() { + if (allowRefresh.compareAndSet(false, true)) { + for (Map.Entry entry : subscriptions.entrySet()) { + String key = entry.getKey().toString(); + GenericChannelState channel = entry.getValue(); + channel.flushChanges(); + } + } + } + + @Override + public boolean allowRefresh() { + return allowRefresh.get(); + } + + public void forceUpdate(boolean _recalculate) { + if (_recalculate) { + recalculate(); + } + + for (Map.Entry entry : subscriptions.entrySet()) { + String key = entry.getKey().toString(); + GenericChannelState channel = entry.getValue(); + channel.flushChanges(true); + } + } + + /** + * + * OLD STUFF Needs cleaning + * + */ + + protected State getStringAsStringState(String value) { + State state = UnDefType.UNDEF; + + if (value != null) { + state = new StringType(value); + } + + return state; + + } + + protected State getBooleanAsOnOffState(boolean value) { + State state = UnDefType.UNDEF; + state = value ? OnOffType.ON : OnOffType.OFF; + return state; + } + + protected State getImageByteArrayAsRawType(ByteArrayOutputStream baos) { + State state = UnDefType.UNDEF; + if (baos != null) { + state = new RawType(baos.toByteArray(), "image/jpeg"); + } + + return state; + } + +} diff --git a/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/MonitorThingState.java b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/MonitorThingState.java new file mode 100644 index 0000000000000..9df154c5002b4 --- /dev/null +++ b/addons/binding/org.openhab.binding.zoneminder/src/main/java/org/openhab/binding/zoneminder/internal/state/MonitorThingState.java @@ -0,0 +1,537 @@ +/** + * Copyright (c) 2010-2018 by the respective copyright holders. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.openhab.binding.zoneminder.internal.state; + +import java.io.ByteArrayOutputStream; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.activation.UnsupportedDataTypeException; + +import org.eclipse.smarthome.core.library.types.OnOffType; +import org.eclipse.smarthome.core.library.types.RawType; +import org.eclipse.smarthome.core.library.types.StringType; +import org.eclipse.smarthome.core.thing.ChannelUID; +import org.eclipse.smarthome.core.types.State; +import org.eclipse.smarthome.core.types.UnDefType; +import org.openhab.binding.zoneminder.ZoneMinderConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import name.eskildsen.zoneminder.api.telnet.ZoneMinderTriggerEvent; +import name.eskildsen.zoneminder.common.ZoneMinderMonitorFunctionEnum; +import name.eskildsen.zoneminder.common.ZoneMinderMonitorStatusEnum; +import name.eskildsen.zoneminder.data.IMonitorDataGeneral; +import name.eskildsen.zoneminder.data.IZoneMinderEventData; + +/** + * The {@link MonitorThingState} is responsible for handling commands, which are + * sent to one of the channels. + * + * @author Martin S. Eskildsen - Initial contribution + */ +public class MonitorThingState extends GenericThingState { + + private Logger logger = LoggerFactory.getLogger(MonitorThingState.class); + + private boolean isDirty; + private String logIdentifier = ""; + + private IZoneMinderEventData activeEvent = null; + private ZoneMinderTriggerEvent curTriggerEvent = null; + + /* + * Used in recalculate + */ + AtomicBoolean recordingFunction = new AtomicBoolean(false); + AtomicBoolean recordingDetailedState = new AtomicBoolean(false); + AtomicBoolean alarmedFunction = new AtomicBoolean(false); + AtomicBoolean alarmedDetailedState = new AtomicBoolean(false); + + // Monitor properties + + // Video Url + private State channelVideoUrl; + + boolean bRecalculating = false; + + /* + * public MonitorThingState(String id, ChannelStateChangeSubscriber subscriber) { + * super(subscriber); + * logIdentifier = id; + * initialize(); + * } + */ + protected void initialize() { + isDirty = true; + } + + public MonitorThingState(ChannelStateChangeSubscriber subscriber) { + super(subscriber); + + } + + @Override + public GenericChannelState createChannelSubscription(ChannelUID channelUID) { + GenericChannelState channelState = null; + try { + switch (channelUID.getId()) { + case ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS: + case ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE: + case ZoneMinderConstants.CHANNEL_MONITOR_FUNCTION: + channelState = createSubscriptionStringType(channelUID); + break; + + case ZoneMinderConstants.CHANNEL_MONITOR_STILL_IMAGE: + channelState = createSubscriptionRawType(channelUID); + break; + default: + channelState = createSubscriptionOnOffType(channelUID); + break; + } + + } catch (Exception ex) { + logger.error("{}: context='subscribe' - Exception occurred when subscribing to channel '{}'", "", + channelUID.getId()); + } + return channelState; + } + + public boolean isDirty() { + return isDirty; + } + + public boolean isAlarmed() { + State stateEnabled = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_EVENT_STATE).getState(); + return ((alarmedDetailedState.get() || (stateEnabled == OnOffType.ON)) ? true : false); + + } + + private boolean isEnabled() { + State stateEnabled = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_ENABLED).getState(); + if (stateEnabled == OnOffType.ON) { + return true; + } + return false; + } + + public ZoneMinderMonitorFunctionEnum getMonitorFunction() { + State stateFunction = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_FUNCTION).getState(); + return ZoneMinderMonitorFunctionEnum.getEnum(stateFunction.toString()); + } + + public String getMonitorEventCause() { + State stateEventCause = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE).getState(); + return stateEventCause.toString(); + } + + public boolean getMonitorEventMotion() { + State stateEventMotion = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_MOTION_EVENT).getState(); + return false; + + } + + public ZoneMinderMonitorStatusEnum getMonitorDetailedStatus() { + State stateStatus = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS).getState(); + ZoneMinderMonitorStatusEnum result = ZoneMinderMonitorStatusEnum.UNKNOWN; + + if (stateStatus == UnDefType.NULL || stateStatus == UnDefType.UNDEF) { + return ZoneMinderMonitorStatusEnum.UNKNOWN; + } + + try { + result = ZoneMinderMonitorStatusEnum.getEnumFromName(stateStatus.toString()); + } catch (Exception ex) { + logger.error( + "{}: context='getMonitorDetailedStatus' Exception occurred when calling getMonitorDetailedStatus", + logIdentifier, ex); + + } + return result; + } + + public void setMonitorTriggerEvent(ZoneMinderTriggerEvent event) { + isDirty = true; + curTriggerEvent = event; + } + + public void setMonitorFunction(ZoneMinderMonitorFunctionEnum monitorFunction) { + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_FUNCTION); + if (gcs != null) { + try { + gcs.setState(monitorFunction.toString()); + } catch (UnsupportedDataTypeException e) { + logger.error("{}: context='setMonitorFunction' Exception occurred when updating capture-daemon channel", + logIdentifier, e); + } + } + } + + public void setMonitorEventCause(State state) { + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE); + if (gcs != null) { + try { + gcs.setState(state); + } catch (UnsupportedDataTypeException e) { + logger.error( + "{}: context='setMonitorEventCause' Exception occurred when updating capture-daemon channel", + logIdentifier, e); + + } + } + + } + + public void setMonitorEventMotion(State state) { + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_MOTION_EVENT); + + if (gcs != null) { + try { + gcs.setState(state); + } catch (UnsupportedDataTypeException e) { + logger.error("{}: context='setMonitorEnabled' Exception occurred when updating capture-daemon channel", + logIdentifier, e); + } + } + + } + + public void setMonitorGeneralData(IMonitorDataGeneral monitorData) { + if (monitorData != null) { + setMonitorFunction(monitorData.getFunction()); + setMonitorEnabled(monitorData.getEnabled()); + } + } + + public void setMonitorEnabled(boolean state) { + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_ENABLED); + + try { + if (gcs != null) { + gcs.setState(state); + } + } catch (UnsupportedDataTypeException e) { + logger.error("{}: context='setMonitorEnabled' Exception occurred when calling setMonitorEnabled", + logIdentifier, e); + + } catch (Exception ex) { + logger.error( + "{}: context='setMonitorEnabled' tag='exception' Exception occurredwhen calling setMonitorEnabled", + logIdentifier, ex); + } + + } + + public void setMonitorRecording(boolean state) { + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_RECORD_STATE); + if (gcs != null) { + try { + gcs.setState(state); + } catch (UnsupportedDataTypeException e) { + logger.error("{}: context='setMonitorRecortding' Exception occurred", logIdentifier, e); + } + } + + } + + public void setMonitorEventState(boolean state) { + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_EVENT_STATE); + if (gcs != null) { + try { + gcs.setState(state); + } catch (UnsupportedDataTypeException e) { + logger.error("{}: context='setMonitorEventState' Exception occurred", logIdentifier, e); + } + } + } + + public void setMonitorForceAlarmInternal(boolean state) { + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_FORCE_ALARM); + if (gcs != null) { + try { + gcs.setState(state, true); + } catch (UnsupportedDataTypeException e) { + logger.error("{}: context='setMonitorForceAlarmInternal' Exception occurred", logIdentifier, e); + + } + } + + } + + public void setMonitorForceAlarmExternal(boolean state) { + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_FORCE_ALARM); + if (gcs != null) { + try { + gcs.setState(state); + } catch (UnsupportedDataTypeException e) { + logger.error("{}: context='setMonitorForceAlarmExternal' Exception occurred", logIdentifier, e); + + } + } + + } + + public void setMonitorEventData(IZoneMinderEventData eventData) { + // If it is set to null ignore since set back to idle from alarm is handled internally + if (eventData == null) { + return; + } + + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_EVENT_CAUSE); + if (gcs != null) { + try { + if (!activeEvent.equals(eventData)) { + activeEvent = eventData; + gcs.setState(eventData.getCause()); + } + } catch (UnsupportedDataTypeException e) { + logger.error("{}: context='setMonitorEventData' Exception occurred", logIdentifier, e); + } + } + } + + public void setMonitorDetailedStatus(ZoneMinderMonitorStatusEnum status) { + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS); + if (gcs != null) { + try { + gcs.setState(status.toString()); + + } catch (UnsupportedDataTypeException e) { + logger.error("{}: context='setMonitorDetailedStatus' Exception occurred", logIdentifier, e); + + } + } + } + + public void setMonitorCaptureDaemonStatus(State state) { + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_CAPTURE_DAEMON_STATE); + if (gcs != null) { + try { + gcs.setState(state); + } catch (UnsupportedDataTypeException e) { + logger.error("{}: context='setMonitorStillImage' Exception occurred", logIdentifier, e); + + } + } + } + + public void setMonitorAnalysisDaemonStatus(State state) { + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_ANALYSIS_DAEMON_STATE); + if (gcs != null) { + try { + gcs.setState(state); + } catch (UnsupportedDataTypeException e) { + logger.error("{}: context='setMonitorAnalysisDaemonStatus' Exception occurred", logIdentifier, e); + + } + } + } + + public void setMonitorFrameDaemonStatus(State state) { + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_FRAME_DAEMON_STATE); + if (gcs != null) { + try { + gcs.setState(state); + } catch (UnsupportedDataTypeException e) { + logger.error("{}: context='setMonitorFrameDaemonStatus' Exception occurred", logIdentifier, e); + + } + } + } + + public void setMonitorFrameDaemonStatus(String status) { + getStringAsStringState(status); + } + + public void setMonitorStillImage(ByteArrayOutputStream baos) { + GenericChannelState gcs = getChannelStateHandler(ZoneMinderConstants.CHANNEL_MONITOR_STILL_IMAGE); + if (gcs != null) { + try { + if (baos != null) { + gcs.setState(baos); + } else { + gcs.setState(UnDefType.UNDEF); + } + } catch (UnsupportedDataTypeException e) { + logger.error("{}: context='setMonitorStillImage' Exception occurred", logIdentifier, e); + + } + } + + } + + public void setMonitorVideoUrl(String url) { + channelVideoUrl = new StringType(url); + } + + public State getVideoUrl() { + return channelVideoUrl; + } + + @Override + public void onChannelChanged(ChannelUID channelUID) { + switch (channelUID.getId()) { + case ZoneMinderConstants.CHANNEL_MONITOR_ENABLED: + case ZoneMinderConstants.CHANNEL_MONITOR_FUNCTION: + case ZoneMinderConstants.CHANNEL_MONITOR_DETAILED_STATUS: + case ZoneMinderConstants.CHANNEL_MONITOR_FORCE_ALARM: + recalculate(); + break; + } + + } + + @Override + protected void recalculate() { + try { + ZoneMinderMonitorFunctionEnum monitorFunction = getMonitorFunction(); + ZoneMinderMonitorStatusEnum monitorStatus = getMonitorDetailedStatus(); + + // Calculate based on state of Function + switch (monitorFunction) { + case NONE: + case MONITOR: + alarmedFunction.set(false); + recordingFunction.set(false); + break; + + case MODECT: + alarmedFunction.set(true); + recordingFunction.set(true); + break; + case RECORD: + alarmedFunction.set(false); + recordingFunction.set(true); + break; + case MOCORD: + alarmedFunction.set(true); + recordingFunction.set(true); + break; + case NODECT: + alarmedFunction.set(false); + recordingFunction.set(true); + break; + default: + recordingFunction.set((activeEvent != null) ? true : false); + } + logger.debug( + "{}: Recalculate channel states based on Function: Function='{}' -> alarmState='{}', recordingState='{}'", + logIdentifier, monitorFunction, alarmedFunction, recordingFunction); + + // Calculated based on detailed Monitor Status + switch (monitorStatus) { + case IDLE: + alarmedDetailedState.set(false); + recordingDetailedState.set(false); + break; + + case PRE_ALARM: + case ALARM: + case ALERT: + alarmedDetailedState.set(true); + recordingDetailedState.set(true); + break; + + case RECORDING: + alarmedDetailedState.set(false); + recordingDetailedState.set(true); + break; + case UNKNOWN: + default: + alarmedDetailedState.set(false); + recordingDetailedState.set(false); + break; + + } + logger.debug( + "{}: Recalculate channel states based on Detailed State: DetailedState='{}' -> alarmState='{}', recordingState='{}'", + logIdentifier, monitorStatus.name(), alarmedDetailedState, recordingDetailedState); + + if (monitorStatus == ZoneMinderMonitorStatusEnum.IDLE && !alarmedDetailedState.get() + && activeEvent != null) { + activeEvent = null; + } + + updateEventChannels(); + + // Now we can conclude on the Alarmed and Recording channel state + setMonitorRecording((recordingFunction.get() && recordingDetailedState.get() && isEnabled())); + setMonitorEventState((alarmedFunction.get() && alarmedDetailedState.get() && isEnabled())); + + switch (getMonitorDetailedStatus()) { + case UNKNOWN: + setMonitorEventCause(UnDefType.UNDEF); + setMonitorEventMotion(UnDefType.UNDEF); + break; + + case PRE_ALARM: + case ALARM: + case ALERT: + if (activeEvent != null) { + setMonitorEventCause(new StringType(activeEvent.getCause())); + setMonitorEventMotion( + activeEvent.getCause().equalsIgnoreCase("motion") ? OnOffType.ON : OnOffType.OFF); + } else { + setMonitorEventMotion(OnOffType.OFF); + } + break; + + case IDLE: + case RECORDING: + default: + setMonitorEventCause(new StringType("")); + setMonitorEventMotion(OnOffType.OFF); + break; + } + } catch (Exception ex) { + logger.error("{}: context='recalculate' Exception occurred", logIdentifier, ex); + } finally { + isDirty = false; + } + } + + protected State getChannelByteArrayAsRawType(ByteArrayOutputStream image) { + State state = UnDefType.UNDEF; + try { + if (image != null) { + state = new RawType(image.toByteArray(), "image/jpeg"); + } + + } catch (Exception ex) { + logger.error("{}: Exception occurred in 'getChannelByteArrayAsRawType()'", logIdentifier, ex); + } + + return state; + } + + private void updateEventChannels() { + switch (getMonitorDetailedStatus()) { + case UNKNOWN: + setMonitorEventCause(UnDefType.UNDEF); + setMonitorEventMotion(UnDefType.UNDEF); + break; + + case PRE_ALARM: + case ALARM: + case ALERT: + if (activeEvent != null) { + setMonitorEventCause(new StringType(activeEvent.getCause())); + setMonitorEventMotion( + activeEvent.getCause().toLowerCase().contains("motion") ? OnOffType.ON : OnOffType.OFF); + } + break; + + case IDLE: + case RECORDING: + default: + setMonitorEventCause(new StringType("")); + setMonitorEventMotion(OnOffType.OFF); + break; + } + } +} \ No newline at end of file